mirror of
https://github.com/mosip/inji-wallet.git
synced 2026-01-06 20:23:52 -05:00
[INJIMOB-1078] Merge remote-tracking branch 'upstream/develop' into injimob-1078-mso-mdoc-vc-support-copy
Signed-off-by: KiruthikaJeyashankar <81218987+KiruthikaJeyashankar@users.noreply.github.com>
This commit is contained in:
11
.github/workflows/android-custom-build.yml
vendored
11
.github/workflows/android-custom-build.yml
vendored
@@ -35,6 +35,14 @@ on:
|
||||
options:
|
||||
- orange
|
||||
- purple
|
||||
type:
|
||||
description: 'Apk type'
|
||||
required: true
|
||||
default: 'release'
|
||||
type: choice
|
||||
options:
|
||||
- release
|
||||
- debug
|
||||
|
||||
jobs:
|
||||
build-android:
|
||||
@@ -45,6 +53,7 @@ jobs:
|
||||
ESIGNET_HOST: ${{ inputs.esignetBackendServiceUrl }}
|
||||
APPLICATION_THEME: ${{ inputs.theme }}
|
||||
ALLOW_ENV_EDIT: ${{ inputs.allow_env_edit }}
|
||||
APPLICATION_TYPE: ${{ inputs.type }}
|
||||
KEYSTORE_ALIAS: androidbuildkey
|
||||
KEYSTORE_PASSWORD: 'password'
|
||||
SERVICE_LOCATION: '.'
|
||||
@@ -53,4 +62,4 @@ jobs:
|
||||
SCRIPT_NAME: './android-build.sh'
|
||||
UPLOAD_TO_ACTIONS: 'true'
|
||||
ANDROID_ARTIFACT_NAME: ${{ inputs.buildName }}
|
||||
ANDROID_ARTIFACT_PATH: "android/app/build/outputs/apk/residentapp/release/Inji_universal.apk"
|
||||
ANDROID_ARTIFACT_PATH: "android/app/build/outputs/apk/residentapp/${{ inputs.type }}/Inji_universal.apk"
|
||||
23
.github/workflows/ui-automation.yml
vendored
23
.github/workflows/ui-automation.yml
vendored
@@ -41,11 +41,10 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3.1.0
|
||||
- uses: actions/checkout@v3.1.0
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '18.x'
|
||||
|
||||
node-version: '18.x'
|
||||
- name: Cache npm dependencies
|
||||
uses: actions/cache@v3.3.1
|
||||
with:
|
||||
@@ -57,7 +56,9 @@ jobs:
|
||||
- name: Install npm dependencies
|
||||
run: |
|
||||
npm ci
|
||||
|
||||
- name: Check Java Version
|
||||
run: |
|
||||
java -version
|
||||
- name: Generate Android keystore
|
||||
run: |
|
||||
echo "$ANDROID_KEYSTORE_FILE" > release.keystore.b64
|
||||
@@ -84,7 +85,11 @@ jobs:
|
||||
name: residentapp
|
||||
path: android/app/build/outputs/apk/residentapp/release/Inji_universal.apk
|
||||
retention-days: 10
|
||||
|
||||
- name: Set up JDK 21
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '21'
|
||||
- name: Run UI Automation Tests on BrowserStack
|
||||
run: |
|
||||
chmod +x injitest/automation_trigger.sh
|
||||
@@ -123,7 +128,7 @@ jobs:
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '18.x'
|
||||
|
||||
|
||||
- name: Cache npm dependencies
|
||||
uses: actions/cache@v3.3.1
|
||||
with:
|
||||
@@ -161,6 +166,12 @@ jobs:
|
||||
name: residentapp
|
||||
path: ios/Inji.ipa
|
||||
retention-days: 10
|
||||
|
||||
- name: Set up JDK 21
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '21'
|
||||
|
||||
- name: Run UI Automation Tests on BrowserStack
|
||||
run: |
|
||||
|
||||
@@ -265,9 +265,11 @@ android {
|
||||
dependencies {
|
||||
implementation("com.facebook.react:react-android")
|
||||
implementation 'com.facebook.soloader:soloader:0.10.1+'
|
||||
implementation("io.mosip:pixelpass:0.2.0-SNAPSHOT")
|
||||
implementation("io.mosip:secure-keystore:0.2.0-SNAPSHOT")
|
||||
implementation("io.mosip:tuvali:0.5.0-SNAPSHOT")
|
||||
implementation("io.mosip:pixelpass:0.2.1")
|
||||
implementation("io.mosip:secure-keystore:0.3.0-SNAPSHOT")
|
||||
implementation("io.mosip:tuvali:0.5.1")
|
||||
implementation("io.mosip:inji-vci-client:0.3.3-SNAPSHOT")
|
||||
|
||||
|
||||
def isGifEnabled = (findProperty('expo.gif.enabled') ?: "") == "true";
|
||||
def isWebpEnabled = (findProperty('expo.webp.enabled') ?: "") == "true";
|
||||
@@ -296,7 +298,6 @@ dependencies {
|
||||
}
|
||||
compileOnly project(':react-native-android-location-services-dialog-box')
|
||||
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
|
||||
implementation("io.mosip:inji-vci-client:0.3.3-SNAPSHOT")
|
||||
// {
|
||||
// exclude group: "androidx.annotation", module: "annotation"
|
||||
// }
|
||||
|
||||
@@ -1,46 +1,87 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="io.mosip.residentapp" xmlns:tools="http://schemas.android.com/tools">
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" android:maxSdkVersion="28"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" android:maxSdkVersion="30"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
|
||||
<uses-permission android:name="android.permission.BLUETOOTH"/>
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE"/>
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation"/>
|
||||
<uses-permission android:name="android.permission.CAMERA"/>
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE"/>
|
||||
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.USE_BIOMETRIC"/>
|
||||
<uses-permission android:name="android.permission.USE_FINGERPRINT"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
<queries>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
<category android:name="android.intent.category.BROWSABLE"/>
|
||||
<data android:scheme="https"/>
|
||||
</intent>
|
||||
</queries>
|
||||
<application tools:replace="usesCleartextTraffic" android:name=".MainApplication" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:allowBackup="false" android:theme="@style/AppTheme" android:usesCleartextTraffic="false">
|
||||
<meta-data android:name="expo.modules.updates.ENABLED" android:value="true"/>
|
||||
<meta-data android:name="expo.modules.updates.EXPO_SDK_VERSION" android:value="48.0.0"/>
|
||||
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_CHECK_ON_LAUNCH" android:value="ALWAYS"/>
|
||||
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_LAUNCH_WAIT_MS" android:value="0"/>
|
||||
<meta-data android:name="expo.modules.updates.EXPO_UPDATE_URL" android:value="https://exp.host/@anonymous/inji"/>
|
||||
<activity android:name=".MainActivity" android:exported="true" android:label="@string/app_name" android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|locale|layoutDirection" android:launchMode="singleTask" android:windowSoftInputMode="adjustResize" android:screenOrientation="portrait">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<category android:name="android.intent.category.BROWSABLE"/>
|
||||
<data android:scheme="io.mosip.residentapp"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity"/>
|
||||
</application>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="io.mosip.residentapp">
|
||||
|
||||
<uses-permission
|
||||
android:name="android.permission.ACCESS_COARSE_LOCATION"
|
||||
android:maxSdkVersion="28" />
|
||||
<uses-permission
|
||||
android:name="android.permission.ACCESS_FINE_LOCATION"
|
||||
android:maxSdkVersion="30" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
|
||||
<uses-permission
|
||||
android:name="android.permission.BLUETOOTH_SCAN"
|
||||
android:usesPermissionFlags="neverForLocation" />
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
|
||||
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
|
||||
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
|
||||
<queries>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="https" />
|
||||
</intent>
|
||||
</queries>
|
||||
<application
|
||||
android:name=".MainApplication"
|
||||
android:allowBackup="false"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:theme="@style/AppTheme"
|
||||
android:usesCleartextTraffic="false"
|
||||
tools:replace="usesCleartextTraffic">
|
||||
<meta-data
|
||||
android:name="expo.modules.updates.ENABLED"
|
||||
android:value="true" />
|
||||
<meta-data
|
||||
android:name="expo.modules.updates.EXPO_SDK_VERSION"
|
||||
android:value="48.0.0" />
|
||||
<meta-data
|
||||
android:name="expo.modules.updates.EXPO_UPDATES_CHECK_ON_LAUNCH"
|
||||
android:value="ALWAYS" />
|
||||
<meta-data
|
||||
android:name="expo.modules.updates.EXPO_UPDATES_LAUNCH_WAIT_MS"
|
||||
android:value="0" />
|
||||
<meta-data
|
||||
android:name="expo.modules.updates.EXPO_UPDATE_URL"
|
||||
android:value="https://exp.host/@anonymous/inji" />
|
||||
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|locale|layoutDirection"
|
||||
android:exported="true"
|
||||
android:label="@string/app_name"
|
||||
android:launchMode="singleTask"
|
||||
android:screenOrientation="portrait"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
|
||||
<data
|
||||
android:host="wla-auth"
|
||||
android:scheme="io.mosip.residentapp.inji" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
|
||||
</application>
|
||||
</manifest>
|
||||
@@ -25,6 +25,7 @@ public class InjiPackage implements ReactPackage {
|
||||
modules.add(new RNVersionModule());
|
||||
modules.add(new RNWalletModule(new RNEventEmitter(reactApplicationContext), new Wallet(reactApplicationContext), reactApplicationContext));
|
||||
modules.add(new RNVerifierModule(new RNEventEmitter(reactApplicationContext), new Verifier(reactApplicationContext), reactApplicationContext));
|
||||
modules.add(new RNQrLoginIntentModule(reactApplicationContext));
|
||||
return modules;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package io.mosip.residentapp;
|
||||
|
||||
public class IntentData {
|
||||
private String qrData = "";
|
||||
private static IntentData intentData;
|
||||
public static IntentData getInstance() {
|
||||
if(intentData == null)
|
||||
intentData = new IntentData();
|
||||
return intentData;
|
||||
}
|
||||
public String getQrData() {
|
||||
return qrData;
|
||||
}
|
||||
|
||||
public void setQrData(String qrData) {
|
||||
this.qrData = qrData;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,20 +3,19 @@ import expo.modules.ReactActivityDelegateWrapper;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import androidx.annotation.CallSuper;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
import com.facebook.react.ReactActivity;
|
||||
import com.facebook.react.ReactActivityDelegate;
|
||||
import com.facebook.react.ReactRootView;
|
||||
import com.facebook.react.ReactActivityDelegate;
|
||||
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
|
||||
import com.facebook.react.defaults.DefaultReactActivityDelegate;
|
||||
import expo.modules.ReactActivityDelegateWrapper;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* IMPORTANT NOTE: The Android permission flow here works
|
||||
@@ -43,6 +42,22 @@ public class MainActivity extends ReactActivity {
|
||||
// This is required for expo-splash-screen.
|
||||
setTheme(R.style.AppTheme);
|
||||
super.onCreate(null);
|
||||
Intent intent = getIntent();
|
||||
readAndSetQRLoginIntentData(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewIntent(Intent intent) {
|
||||
super.onNewIntent(intent);
|
||||
readAndSetQRLoginIntentData(intent);
|
||||
}
|
||||
|
||||
private void readAndSetQRLoginIntentData(Intent intent){
|
||||
Uri data = intent.getData();
|
||||
if(data != null && Objects.equals(data.getScheme(), "io.mosip.residentapp.inji")){
|
||||
IntentData intentData = IntentData.getInstance();
|
||||
intentData.setQrData(String.valueOf(data));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package io.mosip.residentapp;
|
||||
|
||||
import com.facebook.react.bridge.Promise;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
|
||||
public class RNQrLoginIntentModule extends ReactContextBaseJavaModule {
|
||||
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "QrLoginIntent";
|
||||
}
|
||||
|
||||
RNQrLoginIntentModule(ReactApplicationContext context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void isQrLoginByDeepLink(Promise promise) {
|
||||
try {
|
||||
|
||||
IntentData intentData = IntentData.getInstance();
|
||||
promise.resolve(intentData.getQrData());
|
||||
|
||||
} catch (Exception e) {
|
||||
promise.reject("E_UNKNOWN", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void resetQRLoginDeepLinkData(){
|
||||
IntentData intentData = IntentData.getInstance();
|
||||
intentData.setQrData("");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import com.reactnativesecurekeystore.SecureKeystoreImpl;
|
||||
import com.reactnativesecurekeystore.KeyGeneratorImpl;
|
||||
import com.reactnativesecurekeystore.CipherBoxImpl;
|
||||
import com.reactnativesecurekeystore.DeviceCapability;
|
||||
import com.reactnativesecurekeystore.PreferencesImpl;
|
||||
import com.facebook.react.bridge.Promise;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
@@ -13,6 +14,10 @@ import com.reactnativesecurekeystore.common.Util;
|
||||
import kotlin.Unit;
|
||||
import kotlin.jvm.functions.Function1;
|
||||
import kotlin.jvm.functions.Function2;
|
||||
import com.facebook.react.bridge.WritableArray;
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class RNSecureKeystoreModule extends ReactContextBaseJavaModule {
|
||||
private final KeyGeneratorImpl keyGenerator = new KeyGeneratorImpl();
|
||||
@@ -21,16 +26,17 @@ public class RNSecureKeystoreModule extends ReactContextBaseJavaModule {
|
||||
private final SecureKeystoreImpl keystore;
|
||||
private final DeviceCapability deviceCapability;
|
||||
private final String logTag;
|
||||
private final PreferencesImpl preferences;
|
||||
|
||||
public RNSecureKeystoreModule(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
this.biometrics = new Biometrics(reactContext);
|
||||
this.keystore = new SecureKeystoreImpl(keyGenerator, cipherBox, biometrics);
|
||||
this.biometrics = new Biometrics();
|
||||
this.preferences = new PreferencesImpl(reactContext);
|
||||
this.keystore = new SecureKeystoreImpl(keyGenerator, cipherBox, biometrics, preferences);
|
||||
this.deviceCapability = new DeviceCapability(keystore, keyGenerator, biometrics);
|
||||
this.logTag = Util.Companion.getLogTag(getClass().getSimpleName());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "RNSecureKeystoreModule";
|
||||
@@ -57,8 +63,8 @@ public class RNSecureKeystoreModule extends ReactContextBaseJavaModule {
|
||||
}
|
||||
|
||||
@ReactMethod(isBlockingSynchronousMethod = true)
|
||||
public String generateKeyPair(String alias, boolean isAuthRequired, Integer authTimeout) {
|
||||
return keystore.generateKeyPair(alias, isAuthRequired, authTimeout);
|
||||
public String generateKeyPair(String type, String alias, boolean isAuthRequired, Integer authTimeout) {
|
||||
return keystore.generateKeyPair(type, alias, isAuthRequired, authTimeout);
|
||||
}
|
||||
|
||||
@ReactMethod(isBlockingSynchronousMethod = true)
|
||||
@@ -69,103 +75,109 @@ public class RNSecureKeystoreModule extends ReactContextBaseJavaModule {
|
||||
@ReactMethod
|
||||
public void encryptData(String alias, String data, Promise promise) {
|
||||
Function1<String, Unit> successLambda = new Function1<String, Unit>() {
|
||||
@Override
|
||||
public Unit invoke(String encryptedText) {
|
||||
promise.resolve(encryptedText);
|
||||
return Unit.INSTANCE;
|
||||
}
|
||||
@Override
|
||||
public Unit invoke(String encryptedText) {
|
||||
promise.resolve(encryptedText);
|
||||
return Unit.INSTANCE;
|
||||
}
|
||||
};
|
||||
|
||||
Function2<Integer, String, Unit> failureLambda = new Function2<Integer, String, Unit>() {
|
||||
@Override
|
||||
public Unit invoke(Integer code, String message) {
|
||||
promise.reject(code.toString(),message);
|
||||
return Unit.INSTANCE;
|
||||
}
|
||||
@Override
|
||||
public Unit invoke(Integer code, String message) {
|
||||
promise.reject(code.toString(), message);
|
||||
return Unit.INSTANCE;
|
||||
}
|
||||
};
|
||||
keystore.encryptData(
|
||||
alias,
|
||||
data,
|
||||
successLambda,
|
||||
failureLambda
|
||||
);
|
||||
alias,
|
||||
data,
|
||||
successLambda,
|
||||
failureLambda,
|
||||
getCurrentActivity());
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void decryptData(String alias, String encryptedText,Promise promise) {
|
||||
Function1<String, Unit> successLambda = new Function1<String, Unit>() {
|
||||
@Override
|
||||
public Unit invoke(String data) {
|
||||
promise.resolve(data);
|
||||
return Unit.INSTANCE;
|
||||
}
|
||||
};
|
||||
public void decryptData(String alias, String encryptedText, Promise promise) {
|
||||
Function1<String, Unit> successLambda = new Function1<String, Unit>() {
|
||||
@Override
|
||||
public Unit invoke(String data) {
|
||||
promise.resolve(data);
|
||||
return Unit.INSTANCE;
|
||||
}
|
||||
};
|
||||
|
||||
Function2<Integer, String, Unit> failureLambda = new Function2<Integer, String, Unit>() {
|
||||
@Override
|
||||
public Unit invoke(Integer code, String message) {
|
||||
promise.reject(code.toString(),message);
|
||||
return Unit.INSTANCE;
|
||||
}
|
||||
};
|
||||
|
||||
keystore.decryptData(alias, encryptedText, successLambda, failureLambda);
|
||||
}
|
||||
Function2<Integer, String, Unit> failureLambda = new Function2<Integer, String, Unit>() {
|
||||
@Override
|
||||
public Unit invoke(Integer code, String message) {
|
||||
promise.reject(code.toString(), message);
|
||||
return Unit.INSTANCE;
|
||||
}
|
||||
};
|
||||
|
||||
keystore.decryptData(alias, encryptedText, successLambda, failureLambda, getCurrentActivity());
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void generateHmacSha(String alias, String data, Promise promise) {
|
||||
|
||||
Function1<String, Unit> successLambda = new Function1<String, Unit>() {
|
||||
@Override
|
||||
public Unit invoke(String sha) {
|
||||
promise.resolve(sha);
|
||||
return Unit.INSTANCE;
|
||||
}
|
||||
@Override
|
||||
public Unit invoke(String sha) {
|
||||
promise.resolve(sha);
|
||||
return Unit.INSTANCE;
|
||||
}
|
||||
};
|
||||
|
||||
Function2<Integer, String, Unit> failureLambda = new Function2<Integer, String, Unit>() {
|
||||
@Override
|
||||
public Unit invoke(Integer code, String message) {
|
||||
promise.reject(code.toString(),message);
|
||||
return Unit.INSTANCE;
|
||||
}
|
||||
@Override
|
||||
public Unit invoke(Integer code, String message) {
|
||||
promise.reject(code.toString(), message);
|
||||
return Unit.INSTANCE;
|
||||
}
|
||||
};
|
||||
|
||||
keystore.generateHmacSha(
|
||||
alias,
|
||||
data,
|
||||
successLambda,
|
||||
failureLambda
|
||||
);
|
||||
alias,
|
||||
data,
|
||||
successLambda,
|
||||
failureLambda);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void sign(String alias, String data, Promise promise) {
|
||||
public void sign(String signAlgorithm, String alias, String data, Promise promise) {
|
||||
String algorithm = "";
|
||||
if ("RS256".equals(signAlgorithm))
|
||||
algorithm = "SHA256withRSA";
|
||||
else if ("ES256".equals(signAlgorithm))
|
||||
algorithm = "SHA256withECDSA";
|
||||
else {
|
||||
promise.reject("", "Unsupported algorithm for signing");
|
||||
}
|
||||
|
||||
Function1<String, Unit> successLambda = new Function1<String, Unit>() {
|
||||
@Override
|
||||
public Unit invoke(String signature) {
|
||||
promise.resolve(signature);
|
||||
return Unit.INSTANCE;
|
||||
}
|
||||
@Override
|
||||
public Unit invoke(String signature) {
|
||||
promise.resolve(signature);
|
||||
return Unit.INSTANCE;
|
||||
}
|
||||
};
|
||||
|
||||
Function2<Integer, String, Unit> failureLambda = new Function2<Integer, String, Unit>() {
|
||||
@Override
|
||||
public Unit invoke(Integer code, String message) {
|
||||
promise.reject(code.toString(),message);
|
||||
return Unit.INSTANCE;
|
||||
}
|
||||
@Override
|
||||
public Unit invoke(Integer code, String message) {
|
||||
promise.reject(code.toString(), message);
|
||||
return Unit.INSTANCE;
|
||||
}
|
||||
};
|
||||
|
||||
keystore.sign(
|
||||
alias,
|
||||
data,
|
||||
successLambda,
|
||||
failureLambda
|
||||
|
||||
);
|
||||
algorithm,
|
||||
alias,
|
||||
data,
|
||||
successLambda,
|
||||
failureLambda,
|
||||
getCurrentActivity());
|
||||
}
|
||||
|
||||
@ReactMethod(isBlockingSynchronousMethod = true)
|
||||
@@ -175,6 +187,60 @@ public class RNSecureKeystoreModule extends ReactContextBaseJavaModule {
|
||||
|
||||
@ReactMethod(isBlockingSynchronousMethod = true)
|
||||
public boolean hasBiometricsEnabled() {
|
||||
return deviceCapability.hasBiometricsEnabled();
|
||||
return deviceCapability.hasBiometricsEnabled(getCurrentActivity());
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void retrieveKey(String alias, Promise promise) {
|
||||
try {
|
||||
promise.resolve(keystore.retrieveKey(alias));
|
||||
} catch (Exception e) {
|
||||
promise.reject(e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void storeGenericKey(String publicKey, String privateKey, String account, Promise promise) {
|
||||
try {
|
||||
keystore.storeGenericKey(publicKey, privateKey, account);
|
||||
promise.resolve("Success");
|
||||
} catch (Exception e) {
|
||||
promise.reject(e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void storeData(String key, String value, Promise promise) {
|
||||
try {
|
||||
keystore.storeGenericKey(value, "", key);
|
||||
promise.resolve("success");
|
||||
} catch (Exception e) {
|
||||
promise.reject("Error occurred storing data");
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void retrieveGenericKey(String account, Promise promise) {
|
||||
retrieveDataAndResolve(account, promise);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void getData(String key, Promise promise) {
|
||||
retrieveDataAndResolve(key, promise);
|
||||
}
|
||||
|
||||
private void retrieveDataAndResolve(String key, Promise promise) {
|
||||
try {
|
||||
List<String> dataList = keystore.retrieveGenericKey(key);
|
||||
WritableArray writableArray = Arguments.createArray();
|
||||
for (String data : dataList) {
|
||||
writableArray.pushString(data);
|
||||
}
|
||||
promise.resolve(writableArray);
|
||||
} catch (Exception e) {
|
||||
promise.reject(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,9 @@ MIMOTO_HOST = ENV["MIMOTO_HOST"]
|
||||
ESIGNET_HOST = ENV["ESIGNET_HOST"]
|
||||
APPLICATION_THEME = ENV["APPLICATION_THEME"]
|
||||
RELEASE_KEYSTORE_ALIAS = ENV["RELEASE_KEYSTORE_ALIAS"]
|
||||
DEBUG_KEYSTORE_ALIAS = ENV["DEBUG_KEYSTORE_ALIAS"]
|
||||
RELEASE_KEYSTORE_PASSWORD = ENV["RELEASE_KEYSTORE_PASSWORD"]
|
||||
DEBUG_KEYSTORE_PASSWORD = ENV["DEBUG_KEYSTORE_PASSWORD"]
|
||||
PLAY_CONSOLE_RELEASE_DESCRIPTION = ENV["BUILD_DESCRIPTION"]
|
||||
SLACK_URL = ENV["SLACK_WEBHOOK_URL"]
|
||||
CREDENTIAL_REGISTRY_EDIT = ENV["CREDENTIAL_REGISTRY_EDIT"]
|
||||
@@ -43,11 +45,16 @@ def generate_app_name()
|
||||
end
|
||||
end
|
||||
|
||||
desc "Verify Build for Android"
|
||||
lane :android_build do
|
||||
desc "Verify Release Build for Android"
|
||||
lane :android_build_release do
|
||||
gradle(task: "assembleResidentappRelease")
|
||||
end
|
||||
|
||||
desc "Verify Debug Build for Android"
|
||||
lane :android_build_debug do
|
||||
gradle(task: "assembleResidentappDebug")
|
||||
end
|
||||
|
||||
desc "Deploy an Internal testing version to the Google Play"
|
||||
lane :android_build_internal do
|
||||
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
#As react-native/location npm package is old, We need to run this to map androidX source code
|
||||
|
||||
APPLICATION_TYPE=$1
|
||||
|
||||
(cd ../../ && npx jetify)
|
||||
|
||||
cd ..
|
||||
@@ -7,4 +10,8 @@ yes | sudo gem install bundler
|
||||
|
||||
yes | sudo fastlane install_plugins
|
||||
|
||||
bundle exec fastlane android_build
|
||||
if [ "$APPLICATION_TYPE" == "debug" ]; then
|
||||
bundle exec fastlane android_build_debug
|
||||
else
|
||||
bundle exec fastlane android_build_release
|
||||
fi
|
||||
@@ -6,7 +6,6 @@ import {useTranslation} from 'react-i18next';
|
||||
import i18next, {i18n} from 'i18next';
|
||||
import RNRestart from 'react-native-restart';
|
||||
import {setItem} from '../machines/store';
|
||||
import Keychain from 'react-native-keychain';
|
||||
|
||||
export const LanguageSelector: React.FC<LanguageSelectorProps> = props => {
|
||||
const {i18n} = useTranslation();
|
||||
@@ -30,8 +29,7 @@ export const LanguageSelector: React.FC<LanguageSelectorProps> = props => {
|
||||
export const changeLanguage = async (i18n: i18n, language: string) => {
|
||||
if (language !== i18n.language) {
|
||||
await i18n.changeLanguage(language).then(async () => {
|
||||
const existingCredentials = await Keychain.getGenericPassword();
|
||||
await setItem('language', i18n.language, existingCredentials.password);
|
||||
await setItem('language', i18n.language, '');
|
||||
const isRTL = i18next.dir(language) === 'rtl';
|
||||
if (isRTL !== I18nManager.isRTL) {
|
||||
try {
|
||||
|
||||
@@ -9,7 +9,6 @@ import testIDProps from '../shared/commonUtil';
|
||||
import {SvgImage} from './ui/svg';
|
||||
import {NativeModules} from 'react-native';
|
||||
import {VerifiableCredential} from '../machines/VerifiableCredential/VCMetaMachine/vc';
|
||||
import RNSecureKeyStore, {ACCESSIBLE} from 'react-native-secure-key-store';
|
||||
import {DEFAULT_ECL, MAX_QR_DATA_LENGTH} from '../shared/constants';
|
||||
import {VCMetadata} from '../shared/VCMetadata';
|
||||
import {shareImageToAllSupportedApps} from '../shared/sharing/imageUtils';
|
||||
@@ -21,19 +20,23 @@ export const QrCodeOverlay: React.FC<QrCodeOverlayProps> = props => {
|
||||
const [qrString, setQrString] = useState('');
|
||||
const [qrError, setQrError] = useState(false);
|
||||
const base64ImageType = 'data:image/png;base64,';
|
||||
const {RNSecureKeystoreModule} = NativeModules;
|
||||
|
||||
async function getQRData(): Promise<string> {
|
||||
let qrData: string;
|
||||
try {
|
||||
qrData = await RNSecureKeyStore.get(props.meta.id);
|
||||
const keyData = await RNSecureKeystoreModule.getData(props.meta.id);
|
||||
if (keyData[1] && keyData.length > 0) {
|
||||
qrData = keyData[1];
|
||||
} else {
|
||||
throw new Error('No key data found');
|
||||
}
|
||||
} catch {
|
||||
qrData = await RNPixelpassModule.generateQRData(
|
||||
JSON.stringify(props.verifiableCredential),
|
||||
'',
|
||||
);
|
||||
await RNSecureKeyStore.set(props.meta.id, qrData, {
|
||||
accessible: ACCESSIBLE.ALWAYS_THIS_DEVICE_ONLY,
|
||||
});
|
||||
await RNSecureKeystoreModule.storeData(props.meta.id, qrData);
|
||||
}
|
||||
return qrData;
|
||||
}
|
||||
|
||||
8
i18n.ts
8
i18n.ts
@@ -11,7 +11,6 @@ import ta from './locales/tam.json';
|
||||
|
||||
import {iso6393To1} from 'iso-639-3';
|
||||
|
||||
import Keychain from 'react-native-keychain';
|
||||
import {getItem} from './machines/store';
|
||||
import {LocalizedField} from './machines/VerifiableCredential/VCMetaMachine/vc';
|
||||
|
||||
@@ -38,12 +37,7 @@ i18next
|
||||
supportedLngs: Object.keys(SUPPORTED_LANGUAGES),
|
||||
})
|
||||
.then(async () => {
|
||||
const existingCredentials = await Keychain.getGenericPassword();
|
||||
const language = await getItem(
|
||||
'language',
|
||||
null,
|
||||
existingCredentials.password,
|
||||
);
|
||||
const language = await getItem('language', null, '');
|
||||
|
||||
if (language !== i18next.language) {
|
||||
i18next.changeLanguage(language);
|
||||
|
||||
@@ -208,4 +208,4 @@
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
</project>
|
||||
|
||||
@@ -23,6 +23,9 @@
|
||||
9C4850502C3E59B5002ECBD5 /* RNEventMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C4850492C3E59B5002ECBD5 /* RNEventMapper.swift */; };
|
||||
9C4850512C3E59B5002ECBD5 /* RNVersionModule.m in Sources */ = {isa = PBXBuildFile; fileRef = 9C48504A2C3E59B5002ECBD5 /* RNVersionModule.m */; };
|
||||
9C4850532C3E59E2002ECBD5 /* RNEventEmitterProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C4850522C3E59E2002ECBD5 /* RNEventEmitterProtocol.swift */; };
|
||||
9C7CDF3E2C7CBEDE00243A9A /* RNSecureKeystoreModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C7CDF3D2C7CBEDE00243A9A /* RNSecureKeystoreModule.swift */; };
|
||||
9C7CDF432C7CC13500243A9A /* RNSecureKeystoreModule.m in Sources */ = {isa = PBXBuildFile; fileRef = 9C7CDF422C7CC13500243A9A /* RNSecureKeystoreModule.m */; };
|
||||
9C7CDF492C89802C00243A9A /* securekeystore in Frameworks */ = {isa = PBXBuildFile; productRef = 9C7CDF482C89802C00243A9A /* securekeystore */; };
|
||||
9CE34B1F2BFE03E4001AF414 /* pixelpass in Frameworks */ = {isa = PBXBuildFile; productRef = 9CE34B1E2BFE03E4001AF414 /* pixelpass */; };
|
||||
B18059E884C0ABDD17F3DC3D /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAC715A2D49A985799AEE119 /* ExpoModulesProvider.swift */; };
|
||||
BB2F792D24A3F905000567C9 /* Expo.plist in Resources */ = {isa = PBXBuildFile; fileRef = BB2F792C24A3F905000567C9 /* Expo.plist */; };
|
||||
@@ -70,6 +73,8 @@
|
||||
9C4850492C3E59B5002ECBD5 /* RNEventMapper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = RNEventMapper.swift; path = Inji/RNEventMapper.swift; sourceTree = "<group>"; };
|
||||
9C48504A2C3E59B5002ECBD5 /* RNVersionModule.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNVersionModule.m; path = Inji/RNVersionModule.m; sourceTree = "<group>"; };
|
||||
9C4850522C3E59E2002ECBD5 /* RNEventEmitterProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = RNEventEmitterProtocol.swift; path = Inji/RNEventEmitterProtocol.swift; sourceTree = "<group>"; };
|
||||
9C7CDF3D2C7CBEDE00243A9A /* RNSecureKeystoreModule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RNSecureKeystoreModule.swift; sourceTree = "<group>"; };
|
||||
9C7CDF422C7CC13500243A9A /* RNSecureKeystoreModule.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNSecureKeystoreModule.m; sourceTree = "<group>"; };
|
||||
AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = SplashScreen.storyboard; path = Inji/SplashScreen.storyboard; sourceTree = "<group>"; };
|
||||
BB2F792C24A3F905000567C9 /* Expo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Expo.plist; sourceTree = "<group>"; };
|
||||
D98B96A488E54CBDB286B26F /* noop-file.swift */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.swift; name = "noop-file.swift"; path = "Inji/noop-file.swift"; sourceTree = "<group>"; };
|
||||
@@ -106,6 +111,7 @@
|
||||
E8AF2B9D2C0D93E800E775F6 /* VCIClient in Frameworks */,
|
||||
9C4850432C3E5873002ECBD5 /* ios-tuvali-library in Frameworks */,
|
||||
9CE34B1F2BFE03E4001AF414 /* pixelpass in Frameworks */,
|
||||
9C7CDF492C89802C00243A9A /* securekeystore in Frameworks */,
|
||||
96905EF65AED1B983A6B3ABC /* libPods-Inji.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -124,10 +130,12 @@
|
||||
9C4850472C3E59B5002ECBD5 /* RNEventEmitter.swift */,
|
||||
9C4850522C3E59E2002ECBD5 /* RNEventEmitterProtocol.swift */,
|
||||
9C4850492C3E59B5002ECBD5 /* RNEventMapper.swift */,
|
||||
9C7CDF3D2C7CBEDE00243A9A /* RNSecureKeystoreModule.swift */,
|
||||
9C48504A2C3E59B5002ECBD5 /* RNVersionModule.m */,
|
||||
9C4850482C3E59B5002ECBD5 /* RNVersionModule.swift */,
|
||||
9C4850462C3E59B5002ECBD5 /* RNWalletModule.m */,
|
||||
9C4850442C3E59B5002ECBD5 /* RNWalletModule.swift */,
|
||||
9C7CDF422C7CC13500243A9A /* RNSecureKeystoreModule.m */,
|
||||
9C0E86BA2BEE36C300E9F9F6 /* RNPixelpassModule.m */,
|
||||
13B07FB51A68108700A75B9A /* Images.xcassets */,
|
||||
13B07FB61A68108700A75B9A /* Info.plist */,
|
||||
@@ -267,6 +275,7 @@
|
||||
9CE34B1E2BFE03E4001AF414 /* pixelpass */,
|
||||
E8AF2B9C2C0D93E800E775F6 /* VCIClient */,
|
||||
9C4850422C3E5873002ECBD5 /* ios-tuvali-library */,
|
||||
9C7CDF482C89802C00243A9A /* securekeystore */,
|
||||
);
|
||||
productName = Inji;
|
||||
productReference = 13B07F961A680F5B00A75B9A /* Inji.app */;
|
||||
@@ -298,6 +307,7 @@
|
||||
9CE34B1D2BFE03E4001AF414 /* XCRemoteSwiftPackageReference "pixelpass-ios-swift" */,
|
||||
E86208242C039895007C3E24 /* XCRemoteSwiftPackageReference "inji-vci-client-ios-swift" */,
|
||||
9C4850412C3E5873002ECBD5 /* XCRemoteSwiftPackageReference "tuvali-ios-swift" */,
|
||||
9C7CDF472C89802C00243A9A /* XCRemoteSwiftPackageReference "secure-keystore-ios-swift" */,
|
||||
);
|
||||
productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */;
|
||||
projectDirPath = "";
|
||||
@@ -484,8 +494,10 @@
|
||||
9C4850532C3E59E2002ECBD5 /* RNEventEmitterProtocol.swift in Sources */,
|
||||
13B07FC11A68108700A75B9A /* main.m in Sources */,
|
||||
9C4850502C3E59B5002ECBD5 /* RNEventMapper.swift in Sources */,
|
||||
9C7CDF432C7CC13500243A9A /* RNSecureKeystoreModule.m in Sources */,
|
||||
E86208172C0335EC007C3E24 /* RNVCIClientModule.m in Sources */,
|
||||
9C48504E2C3E59B5002ECBD5 /* RNEventEmitter.swift in Sources */,
|
||||
9C7CDF3E2C7CBEDE00243A9A /* RNSecureKeystoreModule.swift in Sources */,
|
||||
9C48504B2C3E59B5002ECBD5 /* RNWalletModule.swift in Sources */,
|
||||
9C48504D2C3E59B5002ECBD5 /* RNWalletModule.m in Sources */,
|
||||
B18059E884C0ABDD17F3DC3D /* ExpoModulesProvider.swift in Sources */,
|
||||
@@ -728,6 +740,14 @@
|
||||
kind = branch;
|
||||
};
|
||||
};
|
||||
9C7CDF472C89802C00243A9A /* XCRemoteSwiftPackageReference "secure-keystore-ios-swift" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/mosip/secure-keystore-ios-swift";
|
||||
requirement = {
|
||||
branch = develop;
|
||||
kind = branch;
|
||||
};
|
||||
};
|
||||
9CE34B1D2BFE03E4001AF414 /* XCRemoteSwiftPackageReference "pixelpass-ios-swift" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/mosip/pixelpass-ios-swift/";
|
||||
@@ -752,6 +772,11 @@
|
||||
package = 9C4850412C3E5873002ECBD5 /* XCRemoteSwiftPackageReference "tuvali-ios-swift" */;
|
||||
productName = "ios-tuvali-library";
|
||||
};
|
||||
9C7CDF482C89802C00243A9A /* securekeystore */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 9C7CDF472C89802C00243A9A /* XCRemoteSwiftPackageReference "secure-keystore-ios-swift" */;
|
||||
productName = securekeystore;
|
||||
};
|
||||
9CE34B1E2BFE03E4001AF414 /* pixelpass */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 9CE34B1D2BFE03E4001AF414 /* XCRemoteSwiftPackageReference "pixelpass-ios-swift" */;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"originHash" : "37844cf80fe1416dd9922e75c396bd616f0723f347f5649a1c0355a3ba3f3a82",
|
||||
"originHash" : "23605762247a1c453627ce326361ce9a41806d86fd97ebda599994901cc7610b",
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "base45-swift",
|
||||
@@ -43,7 +43,25 @@
|
||||
"location" : "https://github.com/mosip/pixelpass-ios-swift/",
|
||||
"state" : {
|
||||
"branch" : "develop",
|
||||
"revision" : "0b716d5f6545b10de84c69edbf59565e66776ccd"
|
||||
"revision" : "170f3489e77940212f471ee1cb9f3ab392039a45"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "secure-keystore-ios-swift",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/mosip/secure-keystore-ios-swift",
|
||||
"state" : {
|
||||
"branch" : "develop",
|
||||
"revision" : "497a8a3dc4a8658665c84bc8a36fa0e6bf252c09"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swiftcbor",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/valpackett/SwiftCBOR",
|
||||
"state" : {
|
||||
"branch" : "master",
|
||||
"revision" : "ec24382864e5ffc6d3915c0818745d5ab12545a8"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -431,6 +431,8 @@ PODS:
|
||||
- react-native-cloud-storage (1.4.0):
|
||||
- RCT-Folly (= 2021.07.22.00)
|
||||
- React-Core
|
||||
- react-native-get-random-values (1.11.0):
|
||||
- React-Core
|
||||
- react-native-image-editor (4.2.0):
|
||||
- RCT-Folly (= 2021.07.22.00)
|
||||
- React-Core
|
||||
@@ -452,8 +454,6 @@ PODS:
|
||||
- RCTTypeSafety
|
||||
- React-Core
|
||||
- ReactCommon/turbomodule/core
|
||||
- react-native-secure-key-store (2.0.10):
|
||||
- React-Core
|
||||
- react-native-spinkit (1.4.1):
|
||||
- React
|
||||
- React-perflogger (0.71.8)
|
||||
@@ -560,8 +560,6 @@ PODS:
|
||||
- RNGoogleSignin (10.1.1):
|
||||
- GoogleSignIn (~> 7.0)
|
||||
- React-Core
|
||||
- RNKeychain (8.0.0):
|
||||
- React-Core
|
||||
- RNLocalize (3.0.2):
|
||||
- React-Core
|
||||
- RNPermissions (3.8.4):
|
||||
@@ -651,6 +649,7 @@ DEPENDENCIES:
|
||||
- React-logger (from `../node_modules/react-native/ReactCommon/logger`)
|
||||
- react-native-app-auth (from `../node_modules/react-native-app-auth`)
|
||||
- react-native-cloud-storage (from `../node_modules/react-native-cloud-storage`)
|
||||
- react-native-get-random-values (from `../node_modules/react-native-get-random-values`)
|
||||
- "react-native-image-editor (from `../node_modules/@react-native-community/image-editor`)"
|
||||
- react-native-location (from `../node_modules/react-native-location`)
|
||||
- react-native-mmkv-storage (from `../node_modules/react-native-mmkv-storage`)
|
||||
@@ -658,7 +657,6 @@ DEPENDENCIES:
|
||||
- react-native-restart (from `../node_modules/react-native-restart`)
|
||||
- react-native-rsa-native (from `../node_modules/react-native-rsa-native`)
|
||||
- react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
|
||||
- react-native-secure-key-store (from `../node_modules/react-native-secure-key-store`)
|
||||
- react-native-spinkit (from `../node_modules/react-native-spinkit`)
|
||||
- React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`)
|
||||
- React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`)
|
||||
@@ -682,7 +680,6 @@ DEPENDENCIES:
|
||||
- RNFS (from `../node_modules/react-native-fs`)
|
||||
- RNGestureHandler (from `../node_modules/react-native-gesture-handler`)
|
||||
- "RNGoogleSignin (from `../node_modules/@react-native-google-signin/google-signin`)"
|
||||
- RNKeychain (from `../node_modules/react-native-keychain`)
|
||||
- RNLocalize (from `../node_modules/react-native-localize`)
|
||||
- RNPermissions (from `../node_modules/react-native-permissions`)
|
||||
- RNScreens (from `../node_modules/react-native-screens`)
|
||||
@@ -826,6 +823,8 @@ EXTERNAL SOURCES:
|
||||
:path: "../node_modules/react-native-app-auth"
|
||||
react-native-cloud-storage:
|
||||
:path: "../node_modules/react-native-cloud-storage"
|
||||
react-native-get-random-values:
|
||||
:path: "../node_modules/react-native-get-random-values"
|
||||
react-native-image-editor:
|
||||
:path: "../node_modules/@react-native-community/image-editor"
|
||||
react-native-location:
|
||||
@@ -840,8 +839,6 @@ EXTERNAL SOURCES:
|
||||
:path: "../node_modules/react-native-rsa-native"
|
||||
react-native-safe-area-context:
|
||||
:path: "../node_modules/react-native-safe-area-context"
|
||||
react-native-secure-key-store:
|
||||
:path: "../node_modules/react-native-secure-key-store"
|
||||
react-native-spinkit:
|
||||
:path: "../node_modules/react-native-spinkit"
|
||||
React-perflogger:
|
||||
@@ -888,8 +885,6 @@ EXTERNAL SOURCES:
|
||||
:path: "../node_modules/react-native-gesture-handler"
|
||||
RNGoogleSignin:
|
||||
:path: "../node_modules/@react-native-google-signin/google-signin"
|
||||
RNKeychain:
|
||||
:path: "../node_modules/react-native-keychain"
|
||||
RNLocalize:
|
||||
:path: "../node_modules/react-native-localize"
|
||||
RNPermissions:
|
||||
@@ -983,6 +978,7 @@ SPEC CHECKSUMS:
|
||||
React-logger: 342f358b8decfbf8f272367f4eacf4b6154061be
|
||||
react-native-app-auth: 1d12b6874a24152715a381d8e9149398ce7c2c95
|
||||
react-native-cloud-storage: 4a4726995158d9d45bc6c4a27fd83ab4d0673632
|
||||
react-native-get-random-values: 21325b2244dfa6b58878f51f9aa42821e7ba3d06
|
||||
react-native-image-editor: a60c74bc79f6101b7863c092aa53726e9e40a327
|
||||
react-native-location: 5a40ec1cc6abf2f6d94df979f98ec76c3a415681
|
||||
react-native-mmkv-storage: cfb6854594cfdc5f7383a9e464bb025417d1721c
|
||||
@@ -990,7 +986,6 @@ SPEC CHECKSUMS:
|
||||
react-native-restart: 45c8dca02491980f2958595333cbccd6877cb57e
|
||||
react-native-rsa-native: 12132eb627797529fdb1f0d22fd0f8f9678df64a
|
||||
react-native-safe-area-context: 39c2d8be3328df5d437ac1700f4f3a4f75716acc
|
||||
react-native-secure-key-store: 910e6df6bc33cb790aba6ee24bc7818df1fe5898
|
||||
react-native-spinkit: da294fd828216ad211fe36a5c14c1e09f09e62db
|
||||
React-perflogger: d21f182895de9d1b077f8a3cd00011095c8c9100
|
||||
React-RCTActionSheet: 0151f83ef92d2a7139bba7dfdbc8066632a6d47b
|
||||
@@ -1014,7 +1009,6 @@ SPEC CHECKSUMS:
|
||||
RNFS: 4ac0f0ea233904cb798630b3c077808c06931688
|
||||
RNGestureHandler: 071d7a9ad81e8b83fe7663b303d132406a7d8f39
|
||||
RNGoogleSignin: aac5c1ec73422109dec1da770247a1e410dcc620
|
||||
RNKeychain: 4f63aada75ebafd26f4bc2c670199461eab85d94
|
||||
RNLocalize: dbea38dcb344bf80ff18a1757b1becf11f70cae4
|
||||
RNPermissions: f1b49dd05fa9b83993cd05a9ee115247944d8f1a
|
||||
RNScreens: 218801c16a2782546d30bd2026bb625c0302d70f
|
||||
|
||||
87
ios/RNSecureKeystoreModule.m
Normal file
87
ios/RNSecureKeystoreModule.m
Normal file
@@ -0,0 +1,87 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "React/RCTBridgeModule.h"
|
||||
|
||||
@interface RCT_EXTERN_MODULE(RNSecureKeystoreModule, NSObject)
|
||||
|
||||
RCT_EXTERN_METHOD(generateKeyPair:(NSString *)type
|
||||
isAuthRequired:(BOOL)isAuthRequired
|
||||
authTimeout:(NSInteger)authTimeout
|
||||
resolver:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject)
|
||||
|
||||
RCT_EXTERN_METHOD(deleteKeyPair:(NSString *)tag
|
||||
resolver:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject)
|
||||
|
||||
RCT_EXTERN_METHOD(hasAlias:(NSString *)tag
|
||||
resolver:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject)
|
||||
|
||||
RCT_EXTERN_METHOD(sign:(NSString *)signAlgorithm
|
||||
alias:(NSString *)alias
|
||||
data:(NSString *)data
|
||||
resolver:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject)
|
||||
|
||||
RCT_EXTERN_METHOD(storeGenericKey:(NSString *)publicKey
|
||||
privateKey:(NSString *)privateKey
|
||||
account:(NSString *)account
|
||||
resolver:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject)
|
||||
|
||||
RCT_EXTERN_METHOD(storeData:(NSString *)key
|
||||
value:(NSString *)value
|
||||
resolver:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject)
|
||||
|
||||
RCT_EXTERN_METHOD(retrieveGenericKey:(NSString *)account
|
||||
resolver:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject)
|
||||
|
||||
RCT_EXTERN_METHOD(retrieveKey:(NSString *)alias
|
||||
resolver:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject)
|
||||
|
||||
RCT_EXTERN_METHOD(getData:(NSString *)key
|
||||
resolver:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject)
|
||||
|
||||
RCT_EXTERN_METHOD(hasBiometricsEnabled:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject)
|
||||
|
||||
RCT_EXTERN_METHOD(updatePopup:(NSString *)title
|
||||
desc:(NSString *)desc)
|
||||
|
||||
RCT_EXTERN_METHOD(generateKey:(NSString *)alias
|
||||
authRequired:(BOOL)authRequired
|
||||
authTimeout:(NSInteger)authTimeout
|
||||
resolver:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject)
|
||||
|
||||
RCT_EXTERN_METHOD(encryptData:(NSString *)alias
|
||||
data:(NSString *)data
|
||||
resolver:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject)
|
||||
|
||||
RCT_EXTERN_METHOD(decryptData:(NSString *)alias
|
||||
data:(NSString *)data
|
||||
resolver:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject)
|
||||
|
||||
RCT_EXTERN_METHOD(generateHmacshaKey:(NSString *)alias
|
||||
resolver:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject)
|
||||
|
||||
RCT_EXTERN_METHOD(generateHmacSha:(NSString *)alias
|
||||
data:(NSString *)data
|
||||
resolver:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject)
|
||||
|
||||
RCT_EXTERN_METHOD(clearKeys:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject)
|
||||
|
||||
RCT_EXTERN_METHOD(getJwk: (RCTPromiseResolveBlock)resolve
|
||||
rejecter: (RCTPromiseRejectBlock)reject)
|
||||
|
||||
|
||||
@end
|
||||
259
ios/RNSecureKeystoreModule.swift
Normal file
259
ios/RNSecureKeystoreModule.swift
Normal file
@@ -0,0 +1,259 @@
|
||||
import Foundation
|
||||
import Security
|
||||
import React
|
||||
import securekeystore
|
||||
|
||||
@objc(RNSecureKeystoreModule)
|
||||
class RNSecureKeystoreModule: NSObject,RCTBridgeModule {
|
||||
static func moduleName() -> String! {
|
||||
return "RNSecureKeystoreModule"
|
||||
}
|
||||
|
||||
|
||||
private var secureKeystore:SecureKeystoreProtocol
|
||||
|
||||
override init() {
|
||||
self.secureKeystore=SecureKeystoreImpl()
|
||||
}
|
||||
|
||||
@objc
|
||||
static func requiresMainQueueSetup() -> Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
@objc
|
||||
func generateKeyPair(_ type: String, isAuthRequired: Bool, authTimeout: Int, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
|
||||
let successLambda: (String) -> Void = { success in
|
||||
resolve(success)
|
||||
}
|
||||
|
||||
let failureLambda: (String, String) -> Void = { code, message in
|
||||
reject(code, message, nil)
|
||||
}
|
||||
var tag=""
|
||||
if(type=="RS256")
|
||||
{
|
||||
tag="RSA"
|
||||
}
|
||||
else
|
||||
{
|
||||
tag="ECR1"
|
||||
|
||||
}
|
||||
|
||||
secureKeystore.generateKeyPair(type: tag, tag: tag, isAuthRequired: isAuthRequired, authTimeout: Int32(authTimeout), onSuccess: successLambda, onFailure: failureLambda)
|
||||
}
|
||||
|
||||
@objc
|
||||
func deleteKeyPair(_ tag: String, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
|
||||
let successLambda: (Bool) -> Void = { success in
|
||||
resolve(success)
|
||||
}
|
||||
|
||||
let failureLambda: (String, String) -> Void = { code, message in
|
||||
reject(code, message, nil)
|
||||
}
|
||||
|
||||
|
||||
secureKeystore.deleteKeyPair(tag: tag,onSuccess: successLambda,onFailure: failureLambda)
|
||||
}
|
||||
|
||||
@objc
|
||||
func hasAlias(_ tag: String, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
|
||||
let successLambda: (Bool) -> Void = { success in
|
||||
resolve(success)
|
||||
}
|
||||
|
||||
let failureLambda: (String, String) -> Void = { code, message in
|
||||
reject(code, message, nil)
|
||||
}
|
||||
|
||||
secureKeystore.hasAlias(tag: tag,onSuccess: successLambda,onFailure: failureLambda)
|
||||
}
|
||||
|
||||
@objc
|
||||
func sign(_ signAlgorithm: String, alias: String, data: String, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
|
||||
let successLambda: (String) -> Void = { success in
|
||||
resolve(success)
|
||||
}
|
||||
|
||||
let failureLambda: (String, String) -> Void = { code, message in
|
||||
reject(code, message, nil)
|
||||
}
|
||||
var tag=""
|
||||
if(alias=="RS256")
|
||||
{
|
||||
tag="RSA"
|
||||
}
|
||||
else
|
||||
{
|
||||
tag="ECR1"
|
||||
|
||||
}
|
||||
secureKeystore.sign(signAlgorithm: tag,alias: tag,data: data,onSuccess: successLambda,onFailure: failureLambda)
|
||||
}
|
||||
|
||||
@objc
|
||||
func storeGenericKey(_ publickKey:String,privateKey:String, account: String, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
|
||||
let successLambda: (Bool) -> Void = { success in
|
||||
resolve(success)
|
||||
}
|
||||
|
||||
let failureLambda: (String, String) -> Void = { code, message in
|
||||
reject(code, message, nil)
|
||||
}
|
||||
|
||||
secureKeystore.storeGenericKey(publicKey: publickKey, privateKey: privateKey, account: account, onSuccess: successLambda, onFailure: failureLambda)
|
||||
}
|
||||
|
||||
@objc
|
||||
func storeData(_ key:String,value:String,resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
|
||||
let successLambda: (Bool) -> Void = { success in
|
||||
resolve(success)
|
||||
}
|
||||
|
||||
let failureLambda: (String, String) -> Void = { code, message in
|
||||
reject(code, message, nil)
|
||||
}
|
||||
|
||||
secureKeystore.storeGenericKey(publicKey: value, privateKey: "", account: key, onSuccess: successLambda, onFailure: failureLambda)
|
||||
}
|
||||
|
||||
@objc
|
||||
func retrieveGenericKey(_ account: String, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
|
||||
let successLambda: (String?,String?) -> Void = { privateKey,publicKey in
|
||||
let keyPair=[privateKey,publicKey]
|
||||
resolve(keyPair)
|
||||
|
||||
}
|
||||
|
||||
let failureLambda: (String, String) -> Void = { code, message in
|
||||
reject(code, message, nil)
|
||||
}
|
||||
|
||||
secureKeystore.retrieveGenericKey(account: account,onSuccess: successLambda,onFailure: failureLambda)
|
||||
}
|
||||
|
||||
@objc
|
||||
func getData(_ key: String, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock)
|
||||
{
|
||||
let successLambda: (String?,String?) -> Void = { privateKey,publicKey in
|
||||
let keyPair=[privateKey,publicKey]
|
||||
resolve(keyPair)
|
||||
|
||||
}
|
||||
|
||||
let failureLambda: (String, String) -> Void = { code, message in
|
||||
reject(code, message, nil)
|
||||
}
|
||||
|
||||
secureKeystore.retrieveGenericKey(account: key,onSuccess: successLambda,onFailure: failureLambda)
|
||||
}
|
||||
|
||||
@objc
|
||||
func hasBiometricsEnabled(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
|
||||
let success = secureKeystore.hasBiometricsEnabled()
|
||||
resolve(success)
|
||||
}
|
||||
|
||||
@objc
|
||||
func updatePopup(_ title: String, desc: String) {
|
||||
secureKeystore.updatePopup(title: title, desc: desc)
|
||||
}
|
||||
|
||||
@objc
|
||||
func generateKey(_ alias: String, authRequired: Bool, authTimeout: Int, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
|
||||
let successLambda: (Bool) -> Void = { success in
|
||||
resolve(success)
|
||||
}
|
||||
|
||||
let failureLambda: (String, String) -> Void = { code, message in
|
||||
reject(code, message, nil)
|
||||
}
|
||||
|
||||
secureKeystore.generateKey(alias: alias, authRequired: authRequired, authTimeout: Int32(authTimeout), onSuccess: successLambda, onFailure: failureLambda)
|
||||
}
|
||||
|
||||
@objc
|
||||
func encryptData(_ alias: String, data: String, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
|
||||
let successLambda: (String) -> Void = { encryptedData in
|
||||
resolve(encryptedData)
|
||||
}
|
||||
|
||||
let failureLambda: (String, String) -> Void = { code, message in
|
||||
reject(code, message, nil)
|
||||
}
|
||||
|
||||
secureKeystore.encryptData(alias: alias, data: data, onSuccess: successLambda, onFailure: failureLambda)
|
||||
}
|
||||
|
||||
@objc
|
||||
func decryptData(_ alias: String, data: String, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
|
||||
let successLambda: (String) -> Void = { decryptedData in
|
||||
resolve(decryptedData)
|
||||
}
|
||||
|
||||
let failureLambda: (String, String) -> Void = { code, message in
|
||||
reject(code, message, nil)
|
||||
}
|
||||
|
||||
secureKeystore.decryptData(alias: alias, data: data, onSuccess: successLambda, onFailure: failureLambda)
|
||||
}
|
||||
|
||||
@available(iOS 13.0, *)
|
||||
@objc
|
||||
func generateHmacshaKey(_ alias: String, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
|
||||
let successLambda: (Bool) -> Void = { success in
|
||||
resolve(success)
|
||||
}
|
||||
|
||||
let failureLambda: (String, String) -> Void = { code, message in
|
||||
reject(code, message, nil)
|
||||
}
|
||||
|
||||
secureKeystore.generateHmacshaKey(alias: alias, onSuccess: successLambda, onFailure: failureLambda)
|
||||
}
|
||||
|
||||
@available(iOS 13.0, *)
|
||||
@objc
|
||||
func generateHmacSha(_ alias: String, data: String, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
|
||||
let successLambda: (String?) -> Void = { hash in
|
||||
resolve(hash)
|
||||
}
|
||||
|
||||
let failureLambda: (String, String) -> Void = { code, message in
|
||||
reject(code, message, nil)
|
||||
}
|
||||
|
||||
secureKeystore.generateHmacSha(alias: alias, data: data, onSuccess: successLambda, onFailure: failureLambda)
|
||||
}
|
||||
|
||||
@objc
|
||||
func clearKeys(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
|
||||
let successLambda: (Bool) -> Void = { success in
|
||||
resolve(success)
|
||||
}
|
||||
|
||||
let failureLambda: (String, String) -> Void = { code, message in
|
||||
reject(code, message, nil)
|
||||
}
|
||||
|
||||
secureKeystore.clearKeys(onSuccess: successLambda, onFailure: failureLambda)
|
||||
}
|
||||
|
||||
@objc
|
||||
func retrieveKey(_ alias:String, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock){
|
||||
let successLambda: (String) -> Void = { success in
|
||||
resolve(success)
|
||||
}
|
||||
|
||||
let failureLambda: (String, String) -> Void = { code, message in
|
||||
reject(code, message, nil)
|
||||
}
|
||||
secureKeystore.retrieveKey(tag: alias, onSuccess: successLambda, onFailure: failureLambda)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,8 +1,14 @@
|
||||
import {ErrorMessage, Issuers_Key_Ref} from '../../shared/openId4VCI/Utils';
|
||||
import {
|
||||
ErrorMessage,
|
||||
Issuers_Key_Ref,
|
||||
getKeyTypeFromWellknown,
|
||||
selectCredentialRequestKey,
|
||||
} from '../../shared/openId4VCI/Utils';
|
||||
import {
|
||||
MY_VCS_STORE_KEY,
|
||||
NETWORK_REQUEST_FAILED,
|
||||
REQUEST_TIMEOUT,
|
||||
isIOS,
|
||||
} from '../../shared/constants';
|
||||
import {assign, send} from 'xstate';
|
||||
import {StoreEvents} from '../store';
|
||||
@@ -17,8 +23,10 @@ import {
|
||||
sendImpressionEvent,
|
||||
} from '../../shared/telemetry/TelemetryUtils';
|
||||
import {TelemetryConstants} from '../../shared/telemetry/TelemetryConstants';
|
||||
import {KeyPair} from 'react-native-rsa-native';
|
||||
import {NativeModules} from 'react-native';
|
||||
import {KeyTypes} from '../../shared/cryptoutil/KeyTypes';
|
||||
|
||||
const {RNSecureKeystoreModule} = NativeModules;
|
||||
export const IssuersActions = (model: any) => {
|
||||
return {
|
||||
setIsVerified: assign({
|
||||
@@ -55,6 +63,15 @@ export const IssuersActions = (model: any) => {
|
||||
}),
|
||||
setSelectedCredentialType: model.assign({
|
||||
selectedCredentialType: (_: any, event: any) => event.credType,
|
||||
wellknownKeyTypes: (_: any, event: any) => {
|
||||
const proofTypesSupported = event.credType.proof_types_supported;
|
||||
if (proofTypesSupported?.jwt) {
|
||||
return proofTypesSupported.jwt
|
||||
.proof_signing_alg_values_supported as string[];
|
||||
} else {
|
||||
return [KeyTypes.RS256] as string[];
|
||||
}
|
||||
},
|
||||
}),
|
||||
setSupportedCredentialTypes: model.assign({
|
||||
supportedCredentialTypes: (_: any, event: any) => event.data,
|
||||
@@ -102,11 +119,11 @@ export const IssuersActions = (model: any) => {
|
||||
}),
|
||||
|
||||
loadKeyPair: assign({
|
||||
publicKey: (_, event: any) => event.response?.publicKey,
|
||||
publicKey: (_, event: any) => event.data?.publicKey as string,
|
||||
privateKey: (context: any, event: any) =>
|
||||
event.response?.privateKey
|
||||
? event.response.privateKey
|
||||
: context.privateKey,
|
||||
event.data?.privateKey
|
||||
? event.data.privateKey
|
||||
: (context.privateKey as string),
|
||||
}),
|
||||
getKeyPairFromStore: send(StoreEvents.GET(Issuers_Key_Ref), {
|
||||
to: (context: any) => context.serviceRefs.store,
|
||||
@@ -114,19 +131,22 @@ export const IssuersActions = (model: any) => {
|
||||
sendBackupEvent: send(BackupEvents.DATA_BACKUP(true), {
|
||||
to: (context: any) => context.serviceRefs.backup,
|
||||
}),
|
||||
storeKeyPair: send(
|
||||
(context: any) => {
|
||||
return StoreEvents.SET(Issuers_Key_Ref, {
|
||||
publicKey: context.publicKey,
|
||||
privateKey: context.privateKey,
|
||||
});
|
||||
},
|
||||
{
|
||||
to: context => context.serviceRefs.store,
|
||||
},
|
||||
),
|
||||
storeKeyPair: async (context: any) => {
|
||||
const keyType = context.keyType;
|
||||
if ((keyType != 'ES256' && keyType != 'RS256') || isIOS())
|
||||
await RNSecureKeystoreModule.storeGenericKey(
|
||||
context.publicKey,
|
||||
context.privateKey,
|
||||
keyType,
|
||||
);
|
||||
},
|
||||
|
||||
storeVerifiableCredentialMeta: send(
|
||||
context => StoreEvents.PREPEND(MY_VCS_STORE_KEY, getVCMetadata(context)),
|
||||
context =>
|
||||
StoreEvents.PREPEND(
|
||||
MY_VCS_STORE_KEY,
|
||||
getVCMetadata(context, context.keyType),
|
||||
),
|
||||
{
|
||||
to: (context: any) => context.serviceRefs.store,
|
||||
},
|
||||
@@ -140,14 +160,14 @@ export const IssuersActions = (model: any) => {
|
||||
},
|
||||
|
||||
setVCMetadata: assign({
|
||||
vcMetadata: context => {
|
||||
return getVCMetadata(context);
|
||||
vcMetadata: (context: any) => {
|
||||
return getVCMetadata(context, context.keyType);
|
||||
},
|
||||
}),
|
||||
|
||||
storeVerifiableCredentialData: send(
|
||||
(context: any) => {
|
||||
const vcMeatadata = getVCMetadata(context);
|
||||
const vcMeatadata = getVCMetadata(context, context.keyType);
|
||||
return StoreEvents.SET(vcMeatadata.getVcKey(), {
|
||||
...context.credentialWrapper,
|
||||
vcMetadata: vcMeatadata,
|
||||
@@ -162,7 +182,7 @@ export const IssuersActions = (model: any) => {
|
||||
context => {
|
||||
return {
|
||||
type: 'VC_ADDED',
|
||||
vcMetadata: getVCMetadata(context),
|
||||
vcMetadata: getVCMetadata(context, context.keyType),
|
||||
};
|
||||
},
|
||||
{
|
||||
@@ -174,7 +194,7 @@ export const IssuersActions = (model: any) => {
|
||||
(context: any) => {
|
||||
return {
|
||||
type: 'VC_DOWNLOADED',
|
||||
vcMetadata: getVCMetadata(context),
|
||||
vcMetadata: getVCMetadata(context, context.keyType),
|
||||
vc: context.credentialWrapper,
|
||||
};
|
||||
},
|
||||
@@ -183,6 +203,13 @@ export const IssuersActions = (model: any) => {
|
||||
},
|
||||
),
|
||||
|
||||
setSelectedKey: model.assign({
|
||||
keyType: (context: any) => {
|
||||
const keyType = selectCredentialRequestKey(context.wellknownKeyTypes);
|
||||
return keyType;
|
||||
},
|
||||
}),
|
||||
|
||||
setSelectedIssuers: model.assign({
|
||||
selectedIssuer: (context: any, event: any) =>
|
||||
context.issuers.find(issuer => issuer.credential_issuer === event.id),
|
||||
@@ -217,19 +244,19 @@ export const IssuersActions = (model: any) => {
|
||||
setPublicKey: assign({
|
||||
publicKey: (_, event: any) => {
|
||||
if (!isHardwareKeystoreExists) {
|
||||
return (event.data as KeyPair).public;
|
||||
return event.data.publicKey as string;
|
||||
}
|
||||
return event.data as string;
|
||||
return event.data.publicKey as string;
|
||||
},
|
||||
}),
|
||||
|
||||
setPrivateKey: assign({
|
||||
privateKey: (_, event: any) => (event.data as KeyPair).private,
|
||||
privateKey: (_, event: any) => event.data.privateKey as string,
|
||||
}),
|
||||
|
||||
logDownloaded: send(
|
||||
context => {
|
||||
const vcMetadata = getVCMetadata(context);
|
||||
const vcMetadata = getVCMetadata(context, context.keyType);
|
||||
return ActivityLogEvents.LOG_ACTIVITY(
|
||||
{
|
||||
_vcKey: vcMetadata.getVcKey(),
|
||||
|
||||
@@ -11,7 +11,9 @@ export const IssuersGuards = () => {
|
||||
(event.data as Error).message == VerificationErrorType.NETWORK_ERROR,
|
||||
isSignedIn: (_: any, event: any) =>
|
||||
(event.data as isSignedInResult).isSignedIn,
|
||||
hasKeyPair: (context: any) => !!context.publicKey,
|
||||
hasKeyPair: (context: any) => {
|
||||
return !!context.publicKey;
|
||||
},
|
||||
isInternetConnected: (_: any, event: any) => !!event.data.isConnected,
|
||||
isOIDCflowCancelled: (_: any, event: any) => {
|
||||
// iOS & Android have different error strings for user cancelled flow
|
||||
|
||||
@@ -94,7 +94,7 @@ export const IssuersMachine = model.createMachine(
|
||||
invoke: {
|
||||
src: 'downloadIssuerWellknown',
|
||||
onDone: {
|
||||
actions: 'updateIssuerFromWellknown',
|
||||
actions: ['updateIssuerFromWellknown'],
|
||||
target: 'downloadCredentialTypes',
|
||||
},
|
||||
onError: {
|
||||
@@ -174,8 +174,9 @@ export const IssuersMachine = model.createMachine(
|
||||
actions: [
|
||||
'setTokenResponse',
|
||||
'setLoadingReasonAsSettingUp',
|
||||
'getKeyPairFromStore',
|
||||
'setSelectedKey',
|
||||
],
|
||||
target: '.getKeyPairFromKeystore',
|
||||
},
|
||||
onError: [
|
||||
{
|
||||
@@ -210,26 +211,30 @@ export const IssuersMachine = model.createMachine(
|
||||
},
|
||||
initial: 'idle',
|
||||
states: {
|
||||
idle: {
|
||||
on: {
|
||||
STORE_RESPONSE: {
|
||||
actions: 'loadKeyPair',
|
||||
target: '#issuersMachine.checkKeyPair',
|
||||
},
|
||||
BIOMETRIC_CANCELLED: {
|
||||
target: 'userCancelledBiometric',
|
||||
},
|
||||
STORE_ERROR: {
|
||||
idle: {},
|
||||
getKeyPairFromKeystore: {
|
||||
invoke: {
|
||||
src: 'getKeyPair',
|
||||
onDone: {
|
||||
actions: ['loadKeyPair'],
|
||||
target: '#issuersMachine.checkKeyPair',
|
||||
},
|
||||
onError: [
|
||||
{
|
||||
cond: 'isBiometricCancelled',
|
||||
target: 'userCancelledBiometric',
|
||||
},
|
||||
{
|
||||
target: '#issuersMachine.checkKeyPair',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
userCancelledBiometric: {
|
||||
on: {
|
||||
TRY_AGAIN: [
|
||||
{
|
||||
actions: ['getKeyPairFromStore'],
|
||||
target: 'idle',
|
||||
target: 'getKeyPairFromKeystore',
|
||||
},
|
||||
],
|
||||
RESET_ERROR: {
|
||||
@@ -242,12 +247,10 @@ export const IssuersMachine = model.createMachine(
|
||||
},
|
||||
checkKeyPair: {
|
||||
description: 'checks whether key pair is generated',
|
||||
entry: [
|
||||
'setLoadingReasonAsDownloadingCredentials',
|
||||
send('CHECK_KEY_PAIR'),
|
||||
],
|
||||
on: {
|
||||
CHECK_KEY_PAIR: [
|
||||
entry: ['setLoadingReasonAsDownloadingCredentials'],
|
||||
invoke: {
|
||||
src: 'getSelectedKey',
|
||||
onDone: [
|
||||
{
|
||||
cond: 'hasKeyPair',
|
||||
target: 'downloadCredentials',
|
||||
@@ -256,6 +259,12 @@ export const IssuersMachine = model.createMachine(
|
||||
target: 'generateKeyPair',
|
||||
},
|
||||
],
|
||||
|
||||
onError: [
|
||||
{
|
||||
target: 'selectingIssuer',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
generateKeyPair: {
|
||||
@@ -267,6 +276,7 @@ export const IssuersMachine = model.createMachine(
|
||||
{
|
||||
actions: [
|
||||
'setPublicKey',
|
||||
'setPrivateKey',
|
||||
'setLoadingReasonAsDownloadingCredentials',
|
||||
'storeKeyPair',
|
||||
],
|
||||
@@ -275,6 +285,7 @@ export const IssuersMachine = model.createMachine(
|
||||
},
|
||||
{
|
||||
actions: [
|
||||
// to be decided
|
||||
'setPublicKey',
|
||||
'setLoadingReasonAsDownloadingCredentials',
|
||||
'setPrivateKey',
|
||||
|
||||
@@ -8,6 +8,11 @@ export interface Typegen0 {
|
||||
data: unknown;
|
||||
__tip: 'See the XState TS docs to learn how to strongly type this.';
|
||||
};
|
||||
'done.invoke.issuersMachine.checkKeyPair:invocation[0]': {
|
||||
type: 'done.invoke.issuersMachine.checkKeyPair:invocation[0]';
|
||||
data: unknown;
|
||||
__tip: 'See the XState TS docs to learn how to strongly type this.';
|
||||
};
|
||||
'done.invoke.issuersMachine.displayIssuers:invocation[0]': {
|
||||
type: 'done.invoke.issuersMachine.displayIssuers:invocation[0]';
|
||||
data: unknown;
|
||||
@@ -33,6 +38,11 @@ export interface Typegen0 {
|
||||
data: unknown;
|
||||
__tip: 'See the XState TS docs to learn how to strongly type this.';
|
||||
};
|
||||
'done.invoke.issuersMachine.performAuthorization.getKeyPairFromKeystore:invocation[0]': {
|
||||
type: 'done.invoke.issuersMachine.performAuthorization.getKeyPairFromKeystore:invocation[0]';
|
||||
data: unknown;
|
||||
__tip: 'See the XState TS docs to learn how to strongly type this.';
|
||||
};
|
||||
'done.invoke.issuersMachine.performAuthorization:invocation[0]': {
|
||||
type: 'done.invoke.issuersMachine.performAuthorization:invocation[0]';
|
||||
data: unknown;
|
||||
@@ -68,6 +78,10 @@ export interface Typegen0 {
|
||||
type: 'error.platform.issuersMachine.downloadIssuerWellknown:invocation[0]';
|
||||
data: unknown;
|
||||
};
|
||||
'error.platform.issuersMachine.performAuthorization.getKeyPairFromKeystore:invocation[0]': {
|
||||
type: 'error.platform.issuersMachine.performAuthorization.getKeyPairFromKeystore:invocation[0]';
|
||||
data: unknown;
|
||||
};
|
||||
'error.platform.issuersMachine.performAuthorization:invocation[0]': {
|
||||
type: 'error.platform.issuersMachine.performAuthorization:invocation[0]';
|
||||
data: unknown;
|
||||
@@ -85,13 +99,15 @@ export interface Typegen0 {
|
||||
downloadIssuerWellknown: 'done.invoke.issuersMachine.downloadIssuerWellknown:invocation[0]';
|
||||
downloadIssuersList: 'done.invoke.issuersMachine.displayIssuers:invocation[0]';
|
||||
generateKeyPair: 'done.invoke.issuersMachine.generateKeyPair:invocation[0]';
|
||||
getKeyPair: 'done.invoke.issuersMachine.performAuthorization.getKeyPairFromKeystore:invocation[0]';
|
||||
getSelectedKey: 'done.invoke.issuersMachine.checkKeyPair:invocation[0]';
|
||||
invokeAuthorization: 'done.invoke.issuersMachine.performAuthorization:invocation[0]';
|
||||
isUserSignedAlready: 'done.invoke.issuersMachine.storing:invocation[0]';
|
||||
verifyCredential: 'done.invoke.issuersMachine.verifyingCredential:invocation[0]';
|
||||
};
|
||||
missingImplementations: {
|
||||
actions:
|
||||
| 'getKeyPairFromStore'
|
||||
| 'downloadIssuerWellknown'
|
||||
| 'loadKeyPair'
|
||||
| 'logDownloaded'
|
||||
| 'resetError'
|
||||
@@ -104,6 +120,7 @@ export interface Typegen0 {
|
||||
| 'sendErrorEndEvent'
|
||||
| 'sendImpressionEvent'
|
||||
| 'sendSuccessEndEvent'
|
||||
| 'setCredentialTypeListDownloadFailureError'
|
||||
| 'setCredentialWrapper'
|
||||
| 'setError'
|
||||
| 'setFetchWellknownError'
|
||||
@@ -120,6 +137,7 @@ export interface Typegen0 {
|
||||
| 'setSelectedCredentialType'
|
||||
| 'setSelectedIssuerId'
|
||||
| 'setSelectedIssuers'
|
||||
| 'setSelectedKey'
|
||||
| 'setSupportedCredentialTypes'
|
||||
| 'setTokenResponse'
|
||||
| 'setVCMetadata'
|
||||
@@ -136,6 +154,7 @@ export interface Typegen0 {
|
||||
| 'canSelectIssuerAgain'
|
||||
| 'hasKeyPair'
|
||||
| 'hasUserCancelledBiometric'
|
||||
| 'isBiometricCancelled'
|
||||
| 'isCustomSecureKeystore'
|
||||
| 'isGenericError'
|
||||
| 'isInternetConnected'
|
||||
@@ -151,15 +170,15 @@ export interface Typegen0 {
|
||||
| 'downloadIssuerWellknown'
|
||||
| 'downloadIssuersList'
|
||||
| 'generateKeyPair'
|
||||
| 'getKeyPair'
|
||||
| 'getSelectedKey'
|
||||
| 'invokeAuthorization'
|
||||
| 'isUserSignedAlready'
|
||||
| 'verifyCredential';
|
||||
};
|
||||
eventsCausingActions: {
|
||||
getKeyPairFromStore:
|
||||
| 'TRY_AGAIN'
|
||||
| 'done.invoke.issuersMachine.performAuthorization:invocation[0]';
|
||||
loadKeyPair: 'STORE_RESPONSE';
|
||||
downloadIssuerWellknown: 'TRY_AGAIN';
|
||||
loadKeyPair: 'done.invoke.issuersMachine.performAuthorization.getKeyPairFromKeystore:invocation[0]';
|
||||
logDownloaded:
|
||||
| 'done.invoke.issuersMachine.verifyingCredential:invocation[0]'
|
||||
| 'error.platform.issuersMachine.verifyingCredential:invocation[0]';
|
||||
@@ -189,10 +208,10 @@ export interface Typegen0 {
|
||||
sendErrorEndEvent: 'error.platform.issuersMachine.verifyingCredential:invocation[0]';
|
||||
sendImpressionEvent: 'done.invoke.issuersMachine.displayIssuers:invocation[0]';
|
||||
sendSuccessEndEvent: 'done.invoke.issuersMachine.verifyingCredential:invocation[0]';
|
||||
setCredentialTypeListDownloadFailureError: 'error.platform.issuersMachine.downloadCredentialTypes:invocation[0]';
|
||||
setCredentialWrapper: 'done.invoke.issuersMachine.downloadCredentials:invocation[0]';
|
||||
setError:
|
||||
| 'error.platform.issuersMachine.displayIssuers:invocation[0]'
|
||||
| 'error.platform.issuersMachine.downloadCredentialTypes:invocation[0]'
|
||||
| 'error.platform.issuersMachine.downloadCredentials:invocation[0]'
|
||||
| 'error.platform.issuersMachine.performAuthorization:invocation[0]';
|
||||
setFetchWellknownError: 'error.platform.issuersMachine.downloadIssuerWellknown:invocation[0]';
|
||||
@@ -200,10 +219,10 @@ export interface Typegen0 {
|
||||
setIssuers: 'done.invoke.issuersMachine.displayIssuers:invocation[0]';
|
||||
setLoadingReasonAsDisplayIssuers: 'TRY_AGAIN';
|
||||
setLoadingReasonAsDownloadingCredentials:
|
||||
| 'STORE_ERROR'
|
||||
| 'STORE_RESPONSE'
|
||||
| 'TRY_AGAIN'
|
||||
| 'done.invoke.issuersMachine.generateKeyPair:invocation[0]';
|
||||
| 'done.invoke.issuersMachine.generateKeyPair:invocation[0]'
|
||||
| 'done.invoke.issuersMachine.performAuthorization.getKeyPairFromKeystore:invocation[0]'
|
||||
| 'error.platform.issuersMachine.performAuthorization.getKeyPairFromKeystore:invocation[0]';
|
||||
setLoadingReasonAsSettingUp:
|
||||
| 'SELECTED_ISSUER'
|
||||
| 'TRY_AGAIN'
|
||||
@@ -218,6 +237,7 @@ export interface Typegen0 {
|
||||
setSelectedCredentialType: 'SELECTED_CREDENTIAL_TYPE';
|
||||
setSelectedIssuerId: 'SELECTED_ISSUER';
|
||||
setSelectedIssuers: 'SELECTED_ISSUER';
|
||||
setSelectedKey: 'done.invoke.issuersMachine.performAuthorization:invocation[0]';
|
||||
setSupportedCredentialTypes: 'done.invoke.issuersMachine.downloadCredentialTypes:invocation[0]';
|
||||
setTokenResponse: 'done.invoke.issuersMachine.performAuthorization:invocation[0]';
|
||||
setVCMetadata:
|
||||
@@ -243,8 +263,9 @@ export interface Typegen0 {
|
||||
eventsCausingDelays: {};
|
||||
eventsCausingGuards: {
|
||||
canSelectIssuerAgain: 'TRY_AGAIN';
|
||||
hasKeyPair: 'CHECK_KEY_PAIR';
|
||||
hasKeyPair: 'done.invoke.issuersMachine.checkKeyPair:invocation[0]';
|
||||
hasUserCancelledBiometric: 'error.platform.issuersMachine.downloadCredentials:invocation[0]';
|
||||
isBiometricCancelled: 'error.platform.issuersMachine.performAuthorization.getKeyPairFromKeystore:invocation[0]';
|
||||
isCustomSecureKeystore: 'done.invoke.issuersMachine.generateKeyPair:invocation[0]';
|
||||
isGenericError: 'error.platform.issuersMachine.downloadCredentials:invocation[0]';
|
||||
isInternetConnected: 'done.invoke.checkInternet';
|
||||
@@ -259,12 +280,18 @@ export interface Typegen0 {
|
||||
| 'SELECTED_CREDENTIAL_TYPE'
|
||||
| 'done.invoke.issuersMachine.downloadCredentialTypes:invocation[0]';
|
||||
downloadCredential:
|
||||
| 'CHECK_KEY_PAIR'
|
||||
| 'done.invoke.issuersMachine.checkKeyPair:invocation[0]'
|
||||
| 'done.invoke.issuersMachine.generateKeyPair:invocation[0]';
|
||||
downloadCredentialTypes: 'done.invoke.issuersMachine.downloadIssuerWellknown:invocation[0]';
|
||||
downloadIssuerWellknown: 'SELECTED_ISSUER' | 'TRY_AGAIN';
|
||||
downloadIssuersList: 'CANCEL' | 'TRY_AGAIN' | 'xstate.init';
|
||||
generateKeyPair: 'CHECK_KEY_PAIR';
|
||||
generateKeyPair: 'done.invoke.issuersMachine.checkKeyPair:invocation[0]';
|
||||
getKeyPair:
|
||||
| 'TRY_AGAIN'
|
||||
| 'done.invoke.issuersMachine.performAuthorization:invocation[0]';
|
||||
getSelectedKey:
|
||||
| 'done.invoke.issuersMachine.performAuthorization.getKeyPairFromKeystore:invocation[0]'
|
||||
| 'error.platform.issuersMachine.performAuthorization.getKeyPairFromKeystore:invocation[0]';
|
||||
invokeAuthorization: 'done.invoke.checkInternet';
|
||||
isUserSignedAlready:
|
||||
| 'done.invoke.issuersMachine.verifyingCredential:invocation[0]'
|
||||
@@ -286,6 +313,7 @@ export interface Typegen0 {
|
||||
| 'handleVCVerificationFailure'
|
||||
| 'idle'
|
||||
| 'performAuthorization'
|
||||
| 'performAuthorization.getKeyPairFromKeystore'
|
||||
| 'performAuthorization.idle'
|
||||
| 'performAuthorization.userCancelledBiometric'
|
||||
| 'selectingCredentialType'
|
||||
@@ -294,7 +322,10 @@ export interface Typegen0 {
|
||||
| 'verifyingCredential'
|
||||
| {
|
||||
downloadCredentials?: 'idle' | 'userCancelledBiometric';
|
||||
performAuthorization?: 'idle' | 'userCancelledBiometric';
|
||||
performAuthorization?:
|
||||
| 'getKeyPairFromKeystore'
|
||||
| 'idle'
|
||||
| 'userCancelledBiometric';
|
||||
};
|
||||
tags: never;
|
||||
}
|
||||
|
||||
@@ -24,9 +24,11 @@ export const IssuersModel = createModel(
|
||||
credentialWrapper: {} as CredentialWrapper,
|
||||
serviceRefs: {} as AppServices,
|
||||
verificationErrorMessage: '',
|
||||
publicKey: ``,
|
||||
privateKey: ``,
|
||||
publicKey: '',
|
||||
privateKey: '',
|
||||
vcMetadata: {} as VCMetadata,
|
||||
keyType: 'RS256' as string,
|
||||
wellknownKeyTypes: [] as string[],
|
||||
},
|
||||
{
|
||||
events: IssuersEvents,
|
||||
|
||||
@@ -5,14 +5,16 @@ import {
|
||||
constructAuthorizationConfiguration,
|
||||
constructIssuerMetaData,
|
||||
constructProofJWT,
|
||||
Issuers_Key_Ref,
|
||||
getKeyTypeFromWellknown,
|
||||
hasKeyPair,
|
||||
updateCredentialInformation,
|
||||
vcDownloadTimeout,
|
||||
selectCredentialRequestKey,
|
||||
} from '../../shared/openId4VCI/Utils';
|
||||
import {authorize} from 'react-native-app-auth';
|
||||
import {
|
||||
generateKeys,
|
||||
isHardwareKeystoreExists,
|
||||
fetchKeyPair,
|
||||
generateKeyPair,
|
||||
} from '../../shared/cryptoutil/cryptoUtil';
|
||||
import {NativeModules} from 'react-native';
|
||||
import {
|
||||
@@ -27,7 +29,6 @@ import {TelemetryConstants} from '../../shared/telemetry/TelemetryConstants';
|
||||
import {isMosipVC} from '../../shared/Utils';
|
||||
import {VciClient} from '../../shared/vciClient/VciClient';
|
||||
|
||||
const {RNSecureKeystoreModule} = NativeModules;
|
||||
export const IssuersService = () => {
|
||||
return {
|
||||
isUserSignedAlready: () => async () => {
|
||||
@@ -67,6 +68,7 @@ export const IssuersService = () => {
|
||||
context.privateKey,
|
||||
accessToken,
|
||||
context.selectedIssuer,
|
||||
context.keyType,
|
||||
);
|
||||
let credential = await VciClient.downloadCredential(
|
||||
constructIssuerMetaData(
|
||||
@@ -97,17 +99,21 @@ export const IssuersService = () => {
|
||||
);
|
||||
},
|
||||
|
||||
generateKeyPair: async () => {
|
||||
if (!isHardwareKeystoreExists) {
|
||||
return await generateKeys();
|
||||
}
|
||||
const isBiometricsEnabled = RNSecureKeystoreModule.hasBiometricsEnabled();
|
||||
return RNSecureKeystoreModule.generateKeyPair(
|
||||
Issuers_Key_Ref,
|
||||
isBiometricsEnabled,
|
||||
0,
|
||||
);
|
||||
generateKeyPair: async (context: any) => {
|
||||
const keypair = await generateKeyPair(context.keyType);
|
||||
return keypair;
|
||||
},
|
||||
|
||||
getKeyPair: async (context: any) => {
|
||||
if (!!(await fetchKeyPair(context.keyType)).publicKey) {
|
||||
return await fetchKeyPair(context.keyType);
|
||||
}
|
||||
},
|
||||
|
||||
getSelectedKey: async (context: any) => {
|
||||
return context.keyType;
|
||||
},
|
||||
|
||||
verifyCredential: async (context: any) => {
|
||||
//this issuer specific check has to be removed once vc validation is done.
|
||||
if (isMosipVC(context.selectedIssuerId)) {
|
||||
|
||||
@@ -4,6 +4,7 @@ import {ESIGNET_BASE_URL} from '../../shared/constants';
|
||||
import {
|
||||
isHardwareKeystoreExists,
|
||||
getJWT,
|
||||
fetchKeyPair,
|
||||
} from '../../shared/cryptoutil/cryptoUtil';
|
||||
import {getPrivateKey} from '../../shared/keystore/SecureKeystore';
|
||||
|
||||
@@ -26,16 +27,17 @@ export const QrLoginServices = {
|
||||
sendAuthenticate: async context => {
|
||||
let privateKey;
|
||||
const individualId = context.selectedVc.vcMetadata.displayId;
|
||||
const alias = context.selectedVc.vcMetadata.id;
|
||||
const keyType = context.selectedVc.vcMetadata.downloadKeyType;
|
||||
if (!isHardwareKeystoreExists) {
|
||||
privateKey = await getPrivateKey(
|
||||
context.selectedVc.walletBindingResponse?.walletBindingId,
|
||||
);
|
||||
}
|
||||
|
||||
const keyPair = await fetchKeyPair(keyType);
|
||||
privateKey = keyPair.privateKey;
|
||||
var config = await getAllConfigurations();
|
||||
const jwtHeader = {
|
||||
alg: 'RS256',
|
||||
alg: keyType,
|
||||
'x5t#S256': context.thumbprint,
|
||||
};
|
||||
|
||||
@@ -47,7 +49,13 @@ export const QrLoginServices = {
|
||||
exp: Math.floor(new Date().getTime() / 1000) + 18000,
|
||||
};
|
||||
|
||||
const jwt = await getJWT(jwtHeader, jwtPayload, alias, privateKey);
|
||||
const jwt = await getJWT(
|
||||
jwtHeader,
|
||||
jwtPayload,
|
||||
keyType,
|
||||
privateKey,
|
||||
keyType,
|
||||
);
|
||||
|
||||
const response = await request(
|
||||
API_URLS.authenticate.method,
|
||||
@@ -73,15 +81,17 @@ export const QrLoginServices = {
|
||||
|
||||
sendConsent: async context => {
|
||||
let privateKey;
|
||||
const alias = context.selectedVc.vcMetadata.id;
|
||||
const keyType = context.selectedVc.vcMetadata.downloadKeyType;
|
||||
if (!isHardwareKeystoreExists) {
|
||||
privateKey = await getPrivateKey(
|
||||
context.selectedVc.walletBindingResponse?.walletBindingId,
|
||||
);
|
||||
}
|
||||
const keyPair = await fetchKeyPair(keyType);
|
||||
privateKey = keyPair.privateKey;
|
||||
|
||||
const header = {
|
||||
alg: 'RS256',
|
||||
alg: keyType,
|
||||
'x5t#S256': context.thumbprint,
|
||||
};
|
||||
const payload = {
|
||||
@@ -91,7 +101,7 @@ export const QrLoginServices = {
|
||||
permitted_authorized_scopes: context.authorizeScopes,
|
||||
};
|
||||
|
||||
const JWT = await getJWT(header, payload, alias, privateKey);
|
||||
const JWT = await getJWT(header, payload, keyType, privateKey, keyType);
|
||||
const jwtComponents = JWT.split('.');
|
||||
const detachedSignature = jwtComponents[0] + '.' + jwtComponents[2];
|
||||
|
||||
|
||||
@@ -375,14 +375,14 @@ export const VCItemActions = model => {
|
||||
setPublicKey: assign({
|
||||
publicKey: (_context, event) => {
|
||||
if (!isHardwareKeystoreExists) {
|
||||
return (event.data as KeyPair).public;
|
||||
return event.data.publicKey as string;
|
||||
}
|
||||
return event.data as string;
|
||||
return event.data.publicKey as string;
|
||||
},
|
||||
}),
|
||||
|
||||
setPrivateKey: assign({
|
||||
privateKey: (_context, event) => (event.data as KeyPair).private,
|
||||
privateKey: (_context, event) => event.data.privateKey as string,
|
||||
}),
|
||||
resetPrivateKey: assign({
|
||||
privateKey: () => '',
|
||||
@@ -403,7 +403,9 @@ export const VCItemActions = model => {
|
||||
},
|
||||
),
|
||||
setOTP: model.assign({
|
||||
OTP: (_, event) => event.OTP,
|
||||
OTP: (_, event) => {
|
||||
return event.OTP;
|
||||
},
|
||||
}),
|
||||
|
||||
unSetOTP: model.assign({OTP: () => ''}),
|
||||
|
||||
@@ -15,6 +15,8 @@ export const VCItemGaurds = () => {
|
||||
const vc = event.response;
|
||||
return vc?.verifiableCredential != null;
|
||||
},
|
||||
hasKeyPair: (_, event) => !!event?.data?.publicKey,
|
||||
|
||||
isSignedIn: (_context, event) =>
|
||||
(event.data as isSignedInResult).isSignedIn,
|
||||
|
||||
|
||||
@@ -284,16 +284,36 @@ export const VCItemMachine = model.createMachine(
|
||||
},
|
||||
addKeyPair: {
|
||||
invoke: {
|
||||
src: 'generateKeyPair',
|
||||
src: 'fetchKeyPair',
|
||||
onDone: [
|
||||
{
|
||||
cond: 'isCustomSecureKeystore',
|
||||
target: 'addingWalletBindingId',
|
||||
cond: 'hasKeyPair',
|
||||
actions: ['setPublicKey'],
|
||||
target: 'addingWalletBindingId',
|
||||
},
|
||||
{
|
||||
target: 'addingWalletBindingId',
|
||||
target: 'generateKeyPair',
|
||||
},
|
||||
],
|
||||
onError: [
|
||||
{
|
||||
actions: [
|
||||
'setErrorAsWalletBindingError',
|
||||
'sendWalletBindingErrorEvent',
|
||||
'logWalletBindingFailure',
|
||||
],
|
||||
target: 'showingWalletBindingError',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
generateKeyPair: {
|
||||
invoke: {
|
||||
src: 'generateKeypairAndStore',
|
||||
onDone: [
|
||||
{
|
||||
actions: ['setPublicKey', 'setPrivateKey'],
|
||||
target: 'addingWalletBindingId',
|
||||
},
|
||||
],
|
||||
onError: [
|
||||
@@ -317,11 +337,6 @@ export const VCItemMachine = model.createMachine(
|
||||
target: 'updatingContextVariables',
|
||||
actions: ['setWalletBindingResponse'],
|
||||
},
|
||||
{
|
||||
target: 'updatingPrivateKey',
|
||||
/*The walletBindingResponse is used for conditional rendering in wallet binding. response and use it in updatingPrivateKey state*/
|
||||
actions: ['setWalletBindingResponse'],
|
||||
},
|
||||
],
|
||||
onError: [
|
||||
{
|
||||
@@ -452,10 +467,7 @@ export const VCItemMachine = model.createMachine(
|
||||
onDone: [
|
||||
{
|
||||
cond: 'isSignedIn',
|
||||
actions: [
|
||||
'sendBackupEvent',
|
||||
'refreshAllVcs',
|
||||
],
|
||||
actions: ['sendBackupEvent', 'refreshAllVcs'],
|
||||
target: '#vc-item-machine.vcUtilitiesState.idle',
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,142 +0,0 @@
|
||||
|
||||
// This file was automatically generated. Edits will be overwritten
|
||||
|
||||
export interface Typegen0 {
|
||||
'@@xstate/typegen': true;
|
||||
internalEvents: {
|
||||
"": { type: "" };
|
||||
"done.invoke.checkStatus": { type: "done.invoke.checkStatus"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
|
||||
"done.invoke.downloadCredential": { type: "done.invoke.downloadCredential"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
|
||||
"done.invoke.vc-item-machine.vcUtilitiesState.kebabPopUp.triggerAutoBackup:invocation[0]": { type: "done.invoke.vc-item-machine.vcUtilitiesState.kebabPopUp.triggerAutoBackup:invocation[0]"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
|
||||
"done.invoke.vc-item-machine.vcUtilitiesState.loadVc.loadVcFromContext.fetchWellknown:invocation[0]": { type: "done.invoke.vc-item-machine.vcUtilitiesState.loadVc.loadVcFromContext.fetchWellknown:invocation[0]"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
|
||||
"done.invoke.vc-item-machine.vcUtilitiesState.loadVc.loadVcFromServer.loadDownloadLimitConfig:invocation[0]": { type: "done.invoke.vc-item-machine.vcUtilitiesState.loadVc.loadVcFromServer.loadDownloadLimitConfig:invocation[0]"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
|
||||
"done.invoke.vc-item-machine.vcUtilitiesState.loadVc.loadVcFromServer.verifyingDownloadLimitExpiry:invocation[0]": { type: "done.invoke.vc-item-machine.vcUtilitiesState.loadVc.loadVcFromServer.verifyingDownloadLimitExpiry:invocation[0]"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
|
||||
"done.invoke.vc-item-machine.vcUtilitiesState.verifyingCredential.triggerAutoBackupForVcDownload:invocation[0]": { type: "done.invoke.vc-item-machine.vcUtilitiesState.verifyingCredential.triggerAutoBackupForVcDownload:invocation[0]"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
|
||||
"done.invoke.vc-item-machine.vcUtilitiesState.verifyingCredential:invocation[0]": { type: "done.invoke.vc-item-machine.vcUtilitiesState.verifyingCredential:invocation[0]"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
|
||||
"done.invoke.vc-item-machine.vcUtilitiesState.walletBinding.acceptingBindingOTP.resendOTP:invocation[0]": { type: "done.invoke.vc-item-machine.vcUtilitiesState.walletBinding.acceptingBindingOTP.resendOTP:invocation[0]"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
|
||||
"done.invoke.vc-item-machine.vcUtilitiesState.walletBinding.addKeyPair:invocation[0]": { type: "done.invoke.vc-item-machine.vcUtilitiesState.walletBinding.addKeyPair:invocation[0]"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
|
||||
"done.invoke.vc-item-machine.vcUtilitiesState.walletBinding.addingWalletBindingId:invocation[0]": { type: "done.invoke.vc-item-machine.vcUtilitiesState.walletBinding.addingWalletBindingId:invocation[0]"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
|
||||
"done.invoke.vc-item-machine.vcUtilitiesState.walletBinding.requestingBindingOTP:invocation[0]": { type: "done.invoke.vc-item-machine.vcUtilitiesState.walletBinding.requestingBindingOTP:invocation[0]"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
|
||||
"done.invoke.vc-item-machine.vcUtilitiesState.walletBinding.updatingPrivateKey:invocation[0]": { type: "done.invoke.vc-item-machine.vcUtilitiesState.walletBinding.updatingPrivateKey:invocation[0]"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
|
||||
"done.invoke.vc-item-machine.verifyState.verifyingCredential:invocation[0]": { type: "done.invoke.vc-item-machine.verifyState.verifyingCredential:invocation[0]"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
|
||||
"error.platform.checkStatus": { type: "error.platform.checkStatus"; data: unknown };
|
||||
"error.platform.downloadCredential": { type: "error.platform.downloadCredential"; data: unknown };
|
||||
"error.platform.vc-item-machine.vcUtilitiesState.loadVc.loadVcFromServer.verifyingDownloadLimitExpiry:invocation[0]": { type: "error.platform.vc-item-machine.vcUtilitiesState.loadVc.loadVcFromServer.verifyingDownloadLimitExpiry:invocation[0]"; data: unknown };
|
||||
"error.platform.vc-item-machine.vcUtilitiesState.verifyingCredential:invocation[0]": { type: "error.platform.vc-item-machine.vcUtilitiesState.verifyingCredential:invocation[0]"; data: unknown };
|
||||
"error.platform.vc-item-machine.vcUtilitiesState.walletBinding.acceptingBindingOTP.resendOTP:invocation[0]": { type: "error.platform.vc-item-machine.vcUtilitiesState.walletBinding.acceptingBindingOTP.resendOTP:invocation[0]"; data: unknown };
|
||||
"error.platform.vc-item-machine.vcUtilitiesState.walletBinding.addKeyPair:invocation[0]": { type: "error.platform.vc-item-machine.vcUtilitiesState.walletBinding.addKeyPair:invocation[0]"; data: unknown };
|
||||
"error.platform.vc-item-machine.vcUtilitiesState.walletBinding.addingWalletBindingId:invocation[0]": { type: "error.platform.vc-item-machine.vcUtilitiesState.walletBinding.addingWalletBindingId:invocation[0]"; data: unknown };
|
||||
"error.platform.vc-item-machine.vcUtilitiesState.walletBinding.requestingBindingOTP:invocation[0]": { type: "error.platform.vc-item-machine.vcUtilitiesState.walletBinding.requestingBindingOTP:invocation[0]"; data: unknown };
|
||||
"error.platform.vc-item-machine.vcUtilitiesState.walletBinding.updatingPrivateKey:invocation[0]": { type: "error.platform.vc-item-machine.vcUtilitiesState.walletBinding.updatingPrivateKey:invocation[0]"; data: unknown };
|
||||
"error.platform.vc-item-machine.verifyState.verifyingCredential:invocation[0]": { type: "error.platform.vc-item-machine.verifyState.verifyingCredential:invocation[0]"; data: unknown };
|
||||
"xstate.after(500)#vc-item-machine.verifyState.verifyingCredential": { type: "xstate.after(500)#vc-item-machine.verifyState.verifyingCredential" };
|
||||
"xstate.init": { type: "xstate.init" };
|
||||
"xstate.stop": { type: "xstate.stop" };
|
||||
};
|
||||
invokeSrcNameMap: {
|
||||
"addWalletBindingId": "done.invoke.vc-item-machine.vcUtilitiesState.walletBinding.addingWalletBindingId:invocation[0]";
|
||||
"checkDownloadExpiryLimit": "done.invoke.vc-item-machine.vcUtilitiesState.loadVc.loadVcFromServer.verifyingDownloadLimitExpiry:invocation[0]";
|
||||
"checkStatus": "done.invoke.checkStatus";
|
||||
"downloadCredential": "done.invoke.downloadCredential";
|
||||
"fetchIssuerWellknown": "done.invoke.vc-item-machine.vcUtilitiesState.loadVc.loadVcFromContext.fetchWellknown:invocation[0]";
|
||||
"generateKeyPair": "done.invoke.vc-item-machine.vcUtilitiesState.walletBinding.addKeyPair:invocation[0]";
|
||||
"isUserSignedAlready": "done.invoke.vc-item-machine.vcUtilitiesState.kebabPopUp.triggerAutoBackup:invocation[0]" | "done.invoke.vc-item-machine.vcUtilitiesState.verifyingCredential.triggerAutoBackupForVcDownload:invocation[0]";
|
||||
"loadDownloadLimitConfig": "done.invoke.vc-item-machine.vcUtilitiesState.loadVc.loadVcFromServer.loadDownloadLimitConfig:invocation[0]";
|
||||
"requestBindingOTP": "done.invoke.vc-item-machine.vcUtilitiesState.walletBinding.acceptingBindingOTP.resendOTP:invocation[0]" | "done.invoke.vc-item-machine.vcUtilitiesState.walletBinding.requestingBindingOTP:invocation[0]";
|
||||
"updatePrivateKey": "done.invoke.vc-item-machine.vcUtilitiesState.walletBinding.updatingPrivateKey:invocation[0]";
|
||||
"verifyCredential": "done.invoke.vc-item-machine.vcUtilitiesState.verifyingCredential:invocation[0]" | "done.invoke.vc-item-machine.verifyState.verifyingCredential:invocation[0]";
|
||||
};
|
||||
missingImplementations: {
|
||||
actions: "addVcToInProgressDownloads" | "closeViewVcModal" | "incrementDownloadCounter" | "logDownloaded" | "logRemovedVc" | "logWalletBindingFailure" | "logWalletBindingSuccess" | "refreshAllVcs" | "removeVcFromInProgressDownloads" | "removeVcItem" | "removeVcMetaDataFromStorage" | "removeVcMetaDataFromVcMachineContext" | "removeVerificationStatusFromVcMeta" | "requestVcContext" | "resetIsMachineInKebabPopupState" | "resetIsVerified" | "resetPrivateKey" | "resetVerificationStatus" | "sendActivationStartEvent" | "sendActivationSuccessEvent" | "sendBackupEvent" | "sendDownloadLimitExpire" | "sendDownloadingFailedToVcMeta" | "sendTelemetryEvents" | "sendUserCancelledActivationFailedEndEvent" | "sendVerificationError" | "sendVerificationStatusToVcMeta" | "sendWalletBindingErrorEvent" | "sendWalletBindingSuccess" | "setCommunicationDetails" | "setContext" | "setDownloadInterval" | "setErrorAsVerificationError" | "setErrorAsWalletBindingError" | "setIsVerified" | "setMaxDownloadCount" | "setOTP" | "setPinCard" | "setPrivateKey" | "setPublicKey" | "setThumbprintForWalletBindingId" | "setVcKey" | "setVcMetadata" | "setVerificationStatus" | "setWalletBindingResponse" | "showVerificationBannerStatus" | "storeContext" | "storeVcInContext" | "unSetBindingTransactionId" | "unSetError" | "unSetOTP" | "updateVcMetadata" | "updateWellknownResponse";
|
||||
delays: never;
|
||||
guards: "hasCredential" | "hasCredentialAndWellknown" | "isCustomSecureKeystore" | "isDownloadAllowed" | "isSignedIn" | "isVerificationPendingBecauseOfNetworkIssue";
|
||||
services: "addWalletBindingId" | "checkDownloadExpiryLimit" | "checkStatus" | "downloadCredential" | "fetchIssuerWellknown" | "generateKeyPair" | "isUserSignedAlready" | "loadDownloadLimitConfig" | "requestBindingOTP" | "updatePrivateKey" | "verifyCredential";
|
||||
};
|
||||
eventsCausingActions: {
|
||||
"addVcToInProgressDownloads": "GET_VC_RESPONSE";
|
||||
"closeViewVcModal": "CLOSE_VC_MODAL" | "STORE_RESPONSE";
|
||||
"incrementDownloadCounter": "POLL" | "done.invoke.vc-item-machine.vcUtilitiesState.loadVc.loadVcFromServer.loadDownloadLimitConfig:invocation[0]";
|
||||
"logDownloaded": "STORE_RESPONSE";
|
||||
"logRemovedVc": "STORE_RESPONSE" | "done.invoke.vc-item-machine.vcUtilitiesState.kebabPopUp.triggerAutoBackup:invocation[0]";
|
||||
"logWalletBindingFailure": "error.platform.vc-item-machine.vcUtilitiesState.walletBinding.addKeyPair:invocation[0]" | "error.platform.vc-item-machine.vcUtilitiesState.walletBinding.addingWalletBindingId:invocation[0]" | "error.platform.vc-item-machine.vcUtilitiesState.walletBinding.requestingBindingOTP:invocation[0]" | "error.platform.vc-item-machine.vcUtilitiesState.walletBinding.updatingPrivateKey:invocation[0]";
|
||||
"logWalletBindingSuccess": "done.invoke.vc-item-machine.vcUtilitiesState.walletBinding.addingWalletBindingId:invocation[0]" | "done.invoke.vc-item-machine.vcUtilitiesState.walletBinding.updatingPrivateKey:invocation[0]";
|
||||
"refreshAllVcs": "STORE_RESPONSE" | "done.invoke.vc-item-machine.vcUtilitiesState.kebabPopUp.triggerAutoBackup:invocation[0]";
|
||||
"removeVcFromInProgressDownloads": "STORE_RESPONSE" | "error.platform.downloadCredential" | "error.platform.vc-item-machine.vcUtilitiesState.verifyingCredential:invocation[0]";
|
||||
"removeVcItem": "CONFIRM";
|
||||
"removeVcMetaDataFromStorage": "STORE_ERROR" | "error.platform.vc-item-machine.vcUtilitiesState.verifyingCredential:invocation[0]";
|
||||
"removeVcMetaDataFromVcMachineContext": "DISMISS";
|
||||
"removeVerificationStatusFromVcMeta": "RESET_VERIFICATION_STATUS";
|
||||
"requestVcContext": "DISMISS" | "REFRESH" | "STORE_ERROR" | "xstate.init";
|
||||
"resetIsMachineInKebabPopupState": "" | "ADD_WALLET_BINDING_ID" | "CANCEL" | "CLOSE_VC_MODAL" | "DISMISS" | "REFRESH" | "REMOVE" | "SHOW_ACTIVITY" | "done.invoke.vc-item-machine.vcUtilitiesState.kebabPopUp.triggerAutoBackup:invocation[0]" | "xstate.stop";
|
||||
"resetIsVerified": "error.platform.vc-item-machine.vcUtilitiesState.verifyingCredential:invocation[0]" | "error.platform.vc-item-machine.verifyState.verifyingCredential:invocation[0]";
|
||||
"resetPrivateKey": "done.invoke.vc-item-machine.vcUtilitiesState.walletBinding.addingWalletBindingId:invocation[0]" | "done.invoke.vc-item-machine.vcUtilitiesState.walletBinding.updatingPrivateKey:invocation[0]";
|
||||
"resetVerificationStatus": "REMOVE_VERIFICATION_STATUS_BANNER" | "RESET_VERIFICATION_STATUS";
|
||||
"sendActivationStartEvent": "CONFIRM";
|
||||
"sendActivationSuccessEvent": "done.invoke.vc-item-machine.vcUtilitiesState.walletBinding.addingWalletBindingId:invocation[0]" | "done.invoke.vc-item-machine.vcUtilitiesState.walletBinding.updatingPrivateKey:invocation[0]";
|
||||
"sendBackupEvent": "done.invoke.vc-item-machine.vcUtilitiesState.kebabPopUp.triggerAutoBackup:invocation[0]" | "done.invoke.vc-item-machine.vcUtilitiesState.verifyingCredential.triggerAutoBackupForVcDownload:invocation[0]";
|
||||
"sendDownloadLimitExpire": "FAILED" | "error.platform.vc-item-machine.vcUtilitiesState.loadVc.loadVcFromServer.verifyingDownloadLimitExpiry:invocation[0]";
|
||||
"sendDownloadingFailedToVcMeta": "error.platform.downloadCredential";
|
||||
"sendTelemetryEvents": "STORE_RESPONSE";
|
||||
"sendUserCancelledActivationFailedEndEvent": "DISMISS";
|
||||
"sendVerificationError": "STORE_RESPONSE";
|
||||
"sendVerificationStatusToVcMeta": "STORE_RESPONSE";
|
||||
"sendWalletBindingErrorEvent": "error.platform.vc-item-machine.vcUtilitiesState.walletBinding.acceptingBindingOTP.resendOTP:invocation[0]" | "error.platform.vc-item-machine.vcUtilitiesState.walletBinding.addKeyPair:invocation[0]" | "error.platform.vc-item-machine.vcUtilitiesState.walletBinding.addingWalletBindingId:invocation[0]" | "error.platform.vc-item-machine.vcUtilitiesState.walletBinding.requestingBindingOTP:invocation[0]" | "error.platform.vc-item-machine.vcUtilitiesState.walletBinding.updatingPrivateKey:invocation[0]";
|
||||
"sendWalletBindingSuccess": "SHOW_BINDING_STATUS";
|
||||
"setCommunicationDetails": "done.invoke.vc-item-machine.vcUtilitiesState.walletBinding.acceptingBindingOTP.resendOTP:invocation[0]" | "done.invoke.vc-item-machine.vcUtilitiesState.walletBinding.requestingBindingOTP:invocation[0]";
|
||||
"setContext": "CREDENTIAL_DOWNLOADED" | "GET_VC_RESPONSE";
|
||||
"setDownloadInterval": "done.invoke.vc-item-machine.vcUtilitiesState.loadVc.loadVcFromServer.loadDownloadLimitConfig:invocation[0]";
|
||||
"setErrorAsVerificationError": "error.platform.vc-item-machine.vcUtilitiesState.verifyingCredential:invocation[0]";
|
||||
"setErrorAsWalletBindingError": "error.platform.vc-item-machine.vcUtilitiesState.walletBinding.acceptingBindingOTP.resendOTP:invocation[0]" | "error.platform.vc-item-machine.vcUtilitiesState.walletBinding.addKeyPair:invocation[0]" | "error.platform.vc-item-machine.vcUtilitiesState.walletBinding.addingWalletBindingId:invocation[0]" | "error.platform.vc-item-machine.vcUtilitiesState.walletBinding.requestingBindingOTP:invocation[0]" | "error.platform.vc-item-machine.vcUtilitiesState.walletBinding.updatingPrivateKey:invocation[0]";
|
||||
"setIsVerified": "done.invoke.vc-item-machine.vcUtilitiesState.verifyingCredential:invocation[0]" | "done.invoke.vc-item-machine.verifyState.verifyingCredential:invocation[0]";
|
||||
"setMaxDownloadCount": "done.invoke.vc-item-machine.vcUtilitiesState.loadVc.loadVcFromServer.loadDownloadLimitConfig:invocation[0]";
|
||||
"setOTP": "INPUT_OTP";
|
||||
"setPinCard": "PIN_CARD";
|
||||
"setPrivateKey": "done.invoke.vc-item-machine.vcUtilitiesState.walletBinding.addKeyPair:invocation[0]";
|
||||
"setPublicKey": "done.invoke.vc-item-machine.vcUtilitiesState.walletBinding.addKeyPair:invocation[0]";
|
||||
"setThumbprintForWalletBindingId": "done.invoke.vc-item-machine.vcUtilitiesState.walletBinding.addingWalletBindingId:invocation[0]" | "done.invoke.vc-item-machine.vcUtilitiesState.walletBinding.updatingPrivateKey:invocation[0]";
|
||||
"setVcKey": "REMOVE";
|
||||
"setVcMetadata": "UPDATE_VC_METADATA";
|
||||
"setVerificationStatus": "SET_VERIFICATION_STATUS" | "SHOW_VERIFICATION_STATUS_BANNER" | "STORE_RESPONSE";
|
||||
"setWalletBindingResponse": "done.invoke.vc-item-machine.vcUtilitiesState.walletBinding.addingWalletBindingId:invocation[0]";
|
||||
"showVerificationBannerStatus": "SHOW_VERIFICATION_STATUS_BANNER" | "STORE_RESPONSE" | "xstate.after(500)#vc-item-machine.verifyState.verifyingCredential";
|
||||
"storeContext": "done.invoke.vc-item-machine.vcUtilitiesState.verifyingCredential:invocation[0]" | "done.invoke.vc-item-machine.vcUtilitiesState.walletBinding.addingWalletBindingId:invocation[0]" | "done.invoke.vc-item-machine.vcUtilitiesState.walletBinding.updatingPrivateKey:invocation[0]" | "done.invoke.vc-item-machine.verifyState.verifyingCredential:invocation[0]" | "error.platform.vc-item-machine.vcUtilitiesState.verifyingCredential:invocation[0]" | "error.platform.vc-item-machine.verifyState.verifyingCredential:invocation[0]";
|
||||
"storeVcInContext": "STORE_RESPONSE" | "done.invoke.vc-item-machine.vcUtilitiesState.walletBinding.addingWalletBindingId:invocation[0]" | "done.invoke.vc-item-machine.vcUtilitiesState.walletBinding.updatingPrivateKey:invocation[0]";
|
||||
"unSetBindingTransactionId": "DISMISS";
|
||||
"unSetError": "CANCEL" | "done.invoke.vc-item-machine.vcUtilitiesState.walletBinding.addingWalletBindingId:invocation[0]" | "done.invoke.vc-item-machine.vcUtilitiesState.walletBinding.updatingPrivateKey:invocation[0]";
|
||||
"unSetOTP": "DISMISS" | "done.invoke.vc-item-machine.vcUtilitiesState.walletBinding.requestingBindingOTP:invocation[0]";
|
||||
"updateVcMetadata": "PIN_CARD" | "STORE_RESPONSE";
|
||||
"updateWellknownResponse": "done.invoke.vc-item-machine.vcUtilitiesState.loadVc.loadVcFromContext.fetchWellknown:invocation[0]";
|
||||
};
|
||||
eventsCausingDelays: {
|
||||
|
||||
};
|
||||
eventsCausingGuards: {
|
||||
"hasCredential": "GET_VC_RESPONSE";
|
||||
"hasCredentialAndWellknown": "GET_VC_RESPONSE";
|
||||
"isCustomSecureKeystore": "done.invoke.vc-item-machine.vcUtilitiesState.walletBinding.addKeyPair:invocation[0]" | "done.invoke.vc-item-machine.vcUtilitiesState.walletBinding.addingWalletBindingId:invocation[0]";
|
||||
"isDownloadAllowed": "POLL";
|
||||
"isSignedIn": "done.invoke.vc-item-machine.vcUtilitiesState.kebabPopUp.triggerAutoBackup:invocation[0]" | "done.invoke.vc-item-machine.vcUtilitiesState.verifyingCredential.triggerAutoBackupForVcDownload:invocation[0]";
|
||||
"isVerificationPendingBecauseOfNetworkIssue": "error.platform.vc-item-machine.vcUtilitiesState.verifyingCredential:invocation[0]" | "error.platform.vc-item-machine.verifyState.verifyingCredential:invocation[0]";
|
||||
};
|
||||
eventsCausingServices: {
|
||||
"addWalletBindingId": "done.invoke.vc-item-machine.vcUtilitiesState.walletBinding.addKeyPair:invocation[0]";
|
||||
"checkDownloadExpiryLimit": "POLL" | "done.invoke.vc-item-machine.vcUtilitiesState.loadVc.loadVcFromServer.loadDownloadLimitConfig:invocation[0]";
|
||||
"checkStatus": "done.invoke.vc-item-machine.vcUtilitiesState.loadVc.loadVcFromServer.verifyingDownloadLimitExpiry:invocation[0]";
|
||||
"downloadCredential": "DOWNLOAD_READY";
|
||||
"fetchIssuerWellknown": "GET_VC_RESPONSE";
|
||||
"generateKeyPair": "INPUT_OTP";
|
||||
"isUserSignedAlready": "STORE_RESPONSE";
|
||||
"loadDownloadLimitConfig": "GET_VC_RESPONSE" | "STORE_ERROR";
|
||||
"requestBindingOTP": "CONFIRM" | "RESEND_OTP";
|
||||
"updatePrivateKey": "done.invoke.vc-item-machine.vcUtilitiesState.walletBinding.addingWalletBindingId:invocation[0]";
|
||||
"verifyCredential": "CREDENTIAL_DOWNLOADED" | "VERIFY";
|
||||
};
|
||||
matchesStates: "vcUtilitiesState" | "vcUtilitiesState.idle" | "vcUtilitiesState.kebabPopUp" | "vcUtilitiesState.kebabPopUp.idle" | "vcUtilitiesState.kebabPopUp.pinCard" | "vcUtilitiesState.kebabPopUp.removeWallet" | "vcUtilitiesState.kebabPopUp.removingVc" | "vcUtilitiesState.kebabPopUp.showActivities" | "vcUtilitiesState.kebabPopUp.triggerAutoBackup" | "vcUtilitiesState.loadVc" | "vcUtilitiesState.loadVc.loadVcFromContext" | "vcUtilitiesState.loadVc.loadVcFromContext.fetchWellknown" | "vcUtilitiesState.loadVc.loadVcFromContext.idle" | "vcUtilitiesState.loadVc.loadVcFromServer" | "vcUtilitiesState.loadVc.loadVcFromServer.checkingStatus" | "vcUtilitiesState.loadVc.loadVcFromServer.downloadingCredential" | "vcUtilitiesState.loadVc.loadVcFromServer.loadDownloadLimitConfig" | "vcUtilitiesState.loadVc.loadVcFromServer.savingFailed" | "vcUtilitiesState.loadVc.loadVcFromServer.savingFailed.idle" | "vcUtilitiesState.loadVc.loadVcFromServer.savingFailed.viewingVc" | "vcUtilitiesState.loadVc.loadVcFromServer.verifyingDownloadLimitExpiry" | "vcUtilitiesState.verifyingCredential" | "vcUtilitiesState.verifyingCredential.handleVCVerificationFailure" | "vcUtilitiesState.verifyingCredential.idle" | "vcUtilitiesState.verifyingCredential.triggerAutoBackupForVcDownload" | "vcUtilitiesState.walletBinding" | "vcUtilitiesState.walletBinding.acceptingBindingOTP" | "vcUtilitiesState.walletBinding.acceptingBindingOTP.idle" | "vcUtilitiesState.walletBinding.acceptingBindingOTP.resendOTP" | "vcUtilitiesState.walletBinding.addKeyPair" | "vcUtilitiesState.walletBinding.addingWalletBindingId" | "vcUtilitiesState.walletBinding.requestingBindingOTP" | "vcUtilitiesState.walletBinding.showBindingWarning" | "vcUtilitiesState.walletBinding.showingWalletBindingError" | "vcUtilitiesState.walletBinding.updatingContextVariables" | "vcUtilitiesState.walletBinding.updatingPrivateKey" | "verifyState" | "verifyState.idle" | "verifyState.verificationCompleted" | "verifyState.verifyingCredential" | { "vcUtilitiesState"?: "idle" | "kebabPopUp" | "loadVc" | "verifyingCredential" | "walletBinding" | { "kebabPopUp"?: "idle" | "pinCard" | "removeWallet" | "removingVc" | "showActivities" | "triggerAutoBackup";
|
||||
"loadVc"?: "loadVcFromContext" | "loadVcFromServer" | { "loadVcFromContext"?: "fetchWellknown" | "idle";
|
||||
"loadVcFromServer"?: "checkingStatus" | "downloadingCredential" | "loadDownloadLimitConfig" | "savingFailed" | "verifyingDownloadLimitExpiry" | { "savingFailed"?: "idle" | "viewingVc"; }; };
|
||||
"verifyingCredential"?: "handleVCVerificationFailure" | "idle" | "triggerAutoBackupForVcDownload";
|
||||
"walletBinding"?: "acceptingBindingOTP" | "addKeyPair" | "addingWalletBindingId" | "requestingBindingOTP" | "showBindingWarning" | "showingWalletBindingError" | "updatingContextVariables" | "updatingPrivateKey" | { "acceptingBindingOTP"?: "idle" | "resendOTP"; }; };
|
||||
"verifyState"?: "idle" | "verificationCompleted" | "verifyingCredential"; };
|
||||
tags: never;
|
||||
}
|
||||
|
||||
@@ -1,25 +1,24 @@
|
||||
import {NativeModules} from 'react-native';
|
||||
import Cloud from '../../../shared/CloudBackupAndRestoreUtils';
|
||||
import {VCMetadata} from '../../../shared/VCMetadata';
|
||||
import getAllConfigurations, {
|
||||
API_URLS,
|
||||
CACHED_API,
|
||||
DownloadProps,
|
||||
} from '../../../shared/api';
|
||||
import {
|
||||
generateKeys,
|
||||
isHardwareKeystoreExists,
|
||||
fetchKeyPair,
|
||||
generateKeyPair,
|
||||
} from '../../../shared/cryptoutil/cryptoUtil';
|
||||
import {
|
||||
getBindingCertificateConstant,
|
||||
savePrivateKey,
|
||||
} from '../../../shared/keystore/SecureKeystore';
|
||||
import {CredentialDownloadResponse, request} from '../../../shared/request';
|
||||
import {WalletBindingResponse} from '../VCMetaMachine/vc';
|
||||
import {verifyCredential} from '../../../shared/vcjs/verifyCredential';
|
||||
import {getVerifiableCredential} from './VCItemSelectors';
|
||||
import {getMatchingCredentialIssuerMetadata, getSelectedCredentialTypeDetails} from '../../../shared/openId4VCI/Utils';
|
||||
import {
|
||||
getMatchingCredentialIssuerMetadata,
|
||||
getSelectedCredentialTypeDetails,
|
||||
} from '../../../shared/openId4VCI/Utils';
|
||||
import {getCredentialTypes} from '../../../components/VC/common/VCUtils';
|
||||
import {isIOS} from '../../../shared/constants';
|
||||
|
||||
const {RNSecureKeystoreModule} = NativeModules;
|
||||
export const VCItemServices = model => {
|
||||
@@ -28,16 +27,6 @@ export const VCItemServices = model => {
|
||||
return await Cloud.isSignedInAlready();
|
||||
},
|
||||
|
||||
updatePrivateKey: async context => {
|
||||
const hasSetPrivateKey: boolean = await savePrivateKey(
|
||||
context.walletBindingResponse.walletBindingId,
|
||||
context.privateKey,
|
||||
);
|
||||
if (!hasSetPrivateKey) {
|
||||
throw new Error('Could not store private key in keystore.');
|
||||
}
|
||||
return '';
|
||||
},
|
||||
loadDownloadLimitConfig: async context => {
|
||||
var resp = await getAllConfigurations();
|
||||
const maxLimit: number = resp.vcDownloadMaxRetry;
|
||||
@@ -89,17 +78,20 @@ export const VCItemServices = model => {
|
||||
};
|
||||
return walletResponse;
|
||||
},
|
||||
|
||||
generateKeyPair: async context => {
|
||||
if (!isHardwareKeystoreExists) {
|
||||
return await generateKeys();
|
||||
}
|
||||
const isBiometricsEnabled = RNSecureKeystoreModule.hasBiometricsEnabled();
|
||||
return RNSecureKeystoreModule.generateKeyPair(
|
||||
VCMetadata.fromVC(context.vcMetadata).id,
|
||||
isBiometricsEnabled,
|
||||
0,
|
||||
);
|
||||
fetchKeyPair: async context => {
|
||||
const keyType = context.vcMetadata?.downloadKeyType;
|
||||
return await fetchKeyPair(keyType);
|
||||
},
|
||||
generateKeypairAndStore: async context => {
|
||||
const keyType = context.vcMetadata?.downloadKeyType;
|
||||
const keypair = await generateKeyPair(keyType);
|
||||
if ((keyType != 'ES256' && keyType != 'RS256') || isIOS())
|
||||
await RNSecureKeystoreModule.storeGenericKey(
|
||||
keypair.publicKey as string,
|
||||
keypair.privateKey as string,
|
||||
keyType,
|
||||
);
|
||||
return keypair;
|
||||
},
|
||||
requestBindingOTP: async context => {
|
||||
const response = await request(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import NetInfo, {NetInfoStateType} from '@react-native-community/netinfo';
|
||||
import {AppState, AppStateStatus} from 'react-native';
|
||||
import {AppState, AppStateStatus, NativeModules} from 'react-native';
|
||||
import {getDeviceId, getDeviceName} from 'react-native-device-info';
|
||||
import {assign, EventFrom, send, spawn, StateFrom} from 'xstate';
|
||||
import {createModel} from 'xstate/lib/model';
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
changeEsignetUrl,
|
||||
ESIGNET_BASE_URL,
|
||||
isAndroid,
|
||||
isIOS,
|
||||
MIMOTO_BASE_URL,
|
||||
SETTINGS_STORE_KEY,
|
||||
} from '../shared/constants';
|
||||
@@ -32,6 +33,12 @@ import {
|
||||
createVcMetaMachine,
|
||||
vcMetaMachine,
|
||||
} from './VerifiableCredential/VCMetaMachine/VCMetaMachine';
|
||||
import {
|
||||
checkAllKeyPairs,
|
||||
generateKeyPairsAndStore,
|
||||
} from '../shared/cryptoutil/cryptoUtil';
|
||||
|
||||
const QrLoginIntent = NativeModules.QrLoginIntent;
|
||||
|
||||
const model = createModel(
|
||||
{
|
||||
@@ -40,6 +47,7 @@ const model = createModel(
|
||||
isReadError: false,
|
||||
isDecryptError: false,
|
||||
isKeyInvalidateError: false,
|
||||
linkCode: '',
|
||||
},
|
||||
{
|
||||
events: {
|
||||
@@ -56,6 +64,7 @@ const model = createModel(
|
||||
APP_INFO_RECEIVED: (info: AppInfo) => ({info}),
|
||||
STORE_RESPONSE: (response: unknown) => ({response}),
|
||||
RESET_KEY_INVALIDATE_ERROR_DISMISS: () => ({}),
|
||||
RESET_LINKCODE: () => ({}),
|
||||
},
|
||||
},
|
||||
);
|
||||
@@ -78,6 +87,9 @@ export const appMachine = model.createMachine(
|
||||
DECRYPT_ERROR: {
|
||||
actions: ['setIsDecryptError'],
|
||||
},
|
||||
RESET_LINKCODE: {
|
||||
actions: ['resetLinkCode'],
|
||||
},
|
||||
DECRYPT_ERROR_DISMISS: {
|
||||
actions: ['unsetIsDecryptError'],
|
||||
},
|
||||
@@ -103,13 +115,38 @@ export const appMachine = model.createMachine(
|
||||
'unsetIsDecryptError',
|
||||
'resetKeyInvalidateError',
|
||||
],
|
||||
target: 'services',
|
||||
target: 'checkKeyPairs',
|
||||
},
|
||||
ERROR: {
|
||||
actions: ['setIsReadError', 'updateKeyInvalidateError'],
|
||||
},
|
||||
},
|
||||
},
|
||||
checkKeyPairs: {
|
||||
invoke: {
|
||||
src: 'checkKeyPairs',
|
||||
onDone: [
|
||||
{
|
||||
target: 'services',
|
||||
},
|
||||
],
|
||||
onError: [
|
||||
{
|
||||
target: 'generateKeyPairs',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
generateKeyPairs: {
|
||||
invoke: {
|
||||
src: 'generateKeyPairsAndStore',
|
||||
onDone: [
|
||||
{
|
||||
target: 'checkKeyPairs',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
services: {
|
||||
entry: ['spawnServiceActors', 'logServiceEvents'],
|
||||
on: {
|
||||
@@ -166,6 +203,17 @@ export const appMachine = model.createMachine(
|
||||
checking: {},
|
||||
active: {
|
||||
entry: ['forwardToServices'],
|
||||
invoke: [
|
||||
{
|
||||
src: 'isQrLoginByDeepLink',
|
||||
onDone: {
|
||||
actions: ['setLinkCode'],
|
||||
},
|
||||
},
|
||||
{
|
||||
src: 'resetQRLoginDeepLinkData',
|
||||
},
|
||||
],
|
||||
},
|
||||
inactive: {
|
||||
entry: ['forwardToServices'],
|
||||
@@ -198,7 +246,17 @@ export const appMachine = model.createMachine(
|
||||
},
|
||||
{
|
||||
actions: {
|
||||
forwardToServices: pure((context, event) =>
|
||||
setLinkCode: assign({
|
||||
linkCode: (_, event) => {
|
||||
if (event.data != '')
|
||||
return new URL(event.data).searchParams.get('linkCode')!!;
|
||||
return '';
|
||||
},
|
||||
}),
|
||||
resetLinkCode: assign({
|
||||
linkCode: '',
|
||||
}),
|
||||
forwardToSerices: pure((context, event) =>
|
||||
Object.values(context.serviceRefs).map(serviceRef =>
|
||||
send({...event, type: `APP_${event.type}`}, {to: serviceRef}),
|
||||
),
|
||||
@@ -345,6 +403,15 @@ export const appMachine = model.createMachine(
|
||||
},
|
||||
|
||||
services: {
|
||||
isQrLoginByDeepLink: () => async () => {
|
||||
const data = await QrLoginIntent.isQrLoginByDeepLink();
|
||||
//console.log('DeepLink: ', data);
|
||||
return data;
|
||||
},
|
||||
resetQRLoginDeepLinkData: () => async () => {
|
||||
return await QrLoginIntent.resetQRLoginDeepLinkData();
|
||||
},
|
||||
|
||||
getAppInfo: () => async callback => {
|
||||
const appInfo = {
|
||||
deviceId: getDeviceId(),
|
||||
@@ -397,6 +464,13 @@ export const appMachine = model.createMachine(
|
||||
};
|
||||
},
|
||||
|
||||
checkKeyPairs: async () => {
|
||||
return await checkAllKeyPairs();
|
||||
},
|
||||
|
||||
generateKeyPairsAndStore: async () => {
|
||||
return await generateKeyPairsAndStore();
|
||||
},
|
||||
checkNetworkState: () => callback => {
|
||||
return NetInfo.addEventListener(state => {
|
||||
if (state.isConnected) {
|
||||
@@ -447,3 +521,7 @@ export function selectIsDecryptError(state: State) {
|
||||
export function selectIsKeyInvalidateError(state: State) {
|
||||
return state.context.isKeyInvalidateError;
|
||||
}
|
||||
|
||||
export function selectIsLinkCode(state: State) {
|
||||
return state.context.linkCode;
|
||||
}
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
|
||||
// This file was automatically generated. Edits will be overwritten
|
||||
|
||||
export interface Typegen0 {
|
||||
'@@xstate/typegen': true;
|
||||
internalEvents: {
|
||||
"xstate.init": { type: "xstate.init" };
|
||||
};
|
||||
invokeSrcNameMap: {
|
||||
"checkFocusState": "done.invoke.app.ready.focus:invocation[0]";
|
||||
"checkNetworkState": "done.invoke.app.ready.network:invocation[0]";
|
||||
"getAppInfo": "done.invoke.app.init.info:invocation[0]";
|
||||
};
|
||||
missingImplementations: {
|
||||
actions: never;
|
||||
delays: never;
|
||||
guards: never;
|
||||
services: never;
|
||||
};
|
||||
eventsCausingActions: {
|
||||
"forwardToServices": "ACTIVE" | "INACTIVE" | "OFFLINE" | "ONLINE";
|
||||
"loadCredentialRegistryHostFromStorage": "READY";
|
||||
"loadCredentialRegistryInConstants": "STORE_RESPONSE";
|
||||
"loadEsignetHostFromConstants": "STORE_RESPONSE";
|
||||
"loadEsignetHostFromStorage": "READY";
|
||||
"logServiceEvents": "READY";
|
||||
"logStoreEvents": "KEY_INVALIDATE_ERROR" | "RESET_KEY_INVALIDATE_ERROR_DISMISS" | "xstate.init";
|
||||
"requestDeviceInfo": "REQUEST_DEVICE_INFO";
|
||||
"resetKeyInvalidateError": "READY" | "RESET_KEY_INVALIDATE_ERROR_DISMISS";
|
||||
"setAppInfo": "APP_INFO_RECEIVED";
|
||||
"setIsDecryptError": "DECRYPT_ERROR";
|
||||
"setIsReadError": "ERROR";
|
||||
"spawnServiceActors": "READY";
|
||||
"spawnStoreActor": "KEY_INVALIDATE_ERROR" | "RESET_KEY_INVALIDATE_ERROR_DISMISS" | "xstate.init";
|
||||
"unsetIsDecryptError": "DECRYPT_ERROR_DISMISS" | "READY";
|
||||
"unsetIsReadError": "READY";
|
||||
"updateKeyInvalidateError": "ERROR" | "KEY_INVALIDATE_ERROR";
|
||||
};
|
||||
eventsCausingDelays: {
|
||||
|
||||
};
|
||||
eventsCausingGuards: {
|
||||
|
||||
};
|
||||
eventsCausingServices: {
|
||||
"checkFocusState": "APP_INFO_RECEIVED";
|
||||
"checkNetworkState": "APP_INFO_RECEIVED";
|
||||
"getAppInfo": "STORE_RESPONSE";
|
||||
};
|
||||
matchesStates: "init" | "init.credentialRegistry" | "init.info" | "init.services" | "init.store" | "ready" | "ready.focus" | "ready.focus.active" | "ready.focus.checking" | "ready.focus.inactive" | "ready.network" | "ready.network.checking" | "ready.network.offline" | "ready.network.online" | "waiting" | { "init"?: "credentialRegistry" | "info" | "services" | "store";
|
||||
"ready"?: "focus" | "network" | { "focus"?: "active" | "checking" | "inactive";
|
||||
"network"?: "checking" | "offline" | "online"; }; };
|
||||
tags: never;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// This file was automatically generated. Edits will be overwritten
|
||||
|
||||
export interface Typegen0 {
|
||||
'@@xstate/typegen': true;
|
||||
internalEvents: {
|
||||
@@ -28,11 +30,16 @@ export interface Typegen0 {
|
||||
requestStoredContext: 'xstate.init';
|
||||
setBiometrics: 'SETUP_BIOMETRICS';
|
||||
setContext: 'STORE_RESPONSE';
|
||||
setInitialDownloadDone: 'INITIAL_DOWNLOAD_DONE';
|
||||
setIsToggleFromSettings: 'CHANGE_METHOD';
|
||||
setLanguage: 'SETUP_BIOMETRICS' | 'SETUP_PASSCODE';
|
||||
setOnboardingDone: 'ONBOARDING_DONE';
|
||||
setPasscode: 'SETUP_PASSCODE';
|
||||
setPasscodeSalt: 'done.invoke.auth.introSlider:invocation[0]';
|
||||
setTourGuide: 'SET_TOUR_GUIDE';
|
||||
storeContext:
|
||||
| 'INITIAL_DOWNLOAD_DONE'
|
||||
| 'ONBOARDING_DONE'
|
||||
| 'SETUP_BIOMETRICS'
|
||||
| 'SETUP_PASSCODE'
|
||||
| 'STORE_RESPONSE'
|
||||
|
||||
@@ -46,12 +46,14 @@ export const ScanActions = (model: any, QR_LOGIN_REF_ID: any) => {
|
||||
},
|
||||
}),
|
||||
|
||||
resetLinkCode: model.assign({
|
||||
linkcode: '',
|
||||
}),
|
||||
updateShowFaceAuthConsent: model.assign({
|
||||
showFaceAuthConsent: (_, event) => {
|
||||
return event.response || event.response === null;
|
||||
},
|
||||
}),
|
||||
|
||||
setShowFaceAuthConsent: model.assign({
|
||||
showFaceAuthConsent: (_, event) => {
|
||||
return !event.isDoNotAskAgainChecked;
|
||||
@@ -230,6 +232,10 @@ export const ScanActions = (model: any, QR_LOGIN_REF_ID: any) => {
|
||||
linkCode: (_, event) =>
|
||||
new URL(event.params).searchParams.get('linkCode'),
|
||||
}),
|
||||
|
||||
setLinkCodeFromDeepLink: assign({
|
||||
linkCode: (_, event) => event.linkCode,
|
||||
}),
|
||||
setQuickShareData: assign({
|
||||
quickShareData: (_, event) =>
|
||||
JSON.parse(
|
||||
|
||||
@@ -82,6 +82,20 @@ export const scanMachine =
|
||||
cond: 'isMinimumStorageRequiredForAuditEntryReached',
|
||||
target: 'restrictSharingVc',
|
||||
},
|
||||
{
|
||||
target: 'qrLoginViaDeepLink',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
qrLoginViaDeepLink: {
|
||||
on: {
|
||||
QRLOGIN_VIA_DEEP_LINK: [
|
||||
{
|
||||
actions: ['setChildRef', 'setLinkCodeFromDeepLink'],
|
||||
cond: (_, event) => event.linkCode != '',
|
||||
target: '#scan.showQrLogin',
|
||||
},
|
||||
{
|
||||
target: 'startPermissionCheck',
|
||||
},
|
||||
@@ -362,7 +376,10 @@ export const scanMachine =
|
||||
},
|
||||
},
|
||||
on: {
|
||||
DISMISS: '#scan.checkFaceAuthConsent',
|
||||
DISMISS: {
|
||||
target: '#scan.checkFaceAuthConsent',
|
||||
actions: ['resetLinkCode'],
|
||||
},
|
||||
},
|
||||
initial: 'idle',
|
||||
states: {
|
||||
@@ -385,6 +402,7 @@ export const scanMachine =
|
||||
getStartEventData(TelemetryConstants.FlowType.qrLogin),
|
||||
),
|
||||
],
|
||||
exit: ['resetLinkCode'],
|
||||
},
|
||||
connecting: {
|
||||
invoke: {
|
||||
|
||||
@@ -18,6 +18,7 @@ export interface Typegen0 {
|
||||
type: 'xstate.after(DESTROY_TIMEOUT)#scan.clearingConnection';
|
||||
};
|
||||
'xstate.init': {type: 'xstate.init'};
|
||||
'xstate.stop': {type: 'xstate.stop'};
|
||||
};
|
||||
invokeSrcNameMap: {
|
||||
checkBluetoothPermission: 'done.invoke.scan.checkBluetoothPermission.checking:invocation[0]';
|
||||
@@ -55,6 +56,7 @@ export interface Typegen0 {
|
||||
| 'removeLoggers'
|
||||
| 'resetFaceCaptureBannerStatus'
|
||||
| 'resetFlowType'
|
||||
| 'resetLinkCode'
|
||||
| 'resetSelectedVc'
|
||||
| 'resetShowQuickShareSuccessBanner'
|
||||
| 'sendBLEConnectionErrorEvent'
|
||||
@@ -67,6 +69,7 @@ export interface Typegen0 {
|
||||
| 'setChildRef'
|
||||
| 'setFlowType'
|
||||
| 'setLinkCode'
|
||||
| 'setLinkCodeFromDeepLink'
|
||||
| 'setQuickShareData'
|
||||
| 'setReadyForBluetoothStateCheck'
|
||||
| 'setReceiverInfo'
|
||||
@@ -132,7 +135,10 @@ export interface Typegen0 {
|
||||
| 'SCREEN_BLUR'
|
||||
| 'STORE_RESPONSE'
|
||||
| 'xstate.init';
|
||||
resetFaceCaptureBannerStatus: 'ACCEPT_REQUEST' | 'CLOSE_BANNER';
|
||||
resetFaceCaptureBannerStatus:
|
||||
| 'ACCEPT_REQUEST'
|
||||
| 'CLOSE_BANNER'
|
||||
| 'STORE_RESPONSE';
|
||||
resetFlowType:
|
||||
| 'DISCONNECT'
|
||||
| 'DISMISS'
|
||||
@@ -141,6 +147,15 @@ export interface Typegen0 {
|
||||
| 'RESET'
|
||||
| 'SCREEN_BLUR'
|
||||
| 'xstate.init';
|
||||
resetLinkCode:
|
||||
| 'BLE_ERROR'
|
||||
| 'DISMISS'
|
||||
| 'DISMISS_QUICK_SHARE_BANNER'
|
||||
| 'RESET'
|
||||
| 'SCREEN_BLUR'
|
||||
| 'SCREEN_FOCUS'
|
||||
| 'SELECT_VC'
|
||||
| 'xstate.stop';
|
||||
resetSelectedVc:
|
||||
| 'DISCONNECT'
|
||||
| 'DISMISS'
|
||||
@@ -151,15 +166,16 @@ export interface Typegen0 {
|
||||
| 'xstate.init';
|
||||
resetShowQuickShareSuccessBanner: 'DISMISS' | 'DISMISS_QUICK_SHARE_BANNER';
|
||||
sendBLEConnectionErrorEvent: 'BLE_ERROR';
|
||||
sendScanData: 'SCAN';
|
||||
sendScanData: 'QRLOGIN_VIA_DEEP_LINK' | 'SCAN';
|
||||
sendVCShareFlowCancelEndEvent: 'CANCEL';
|
||||
sendVCShareFlowTimeoutEndEvent: 'CANCEL' | 'RETRY';
|
||||
sendVcShareSuccessEvent: 'VC_ACCEPTED';
|
||||
sendVcSharingStartEvent: 'SCAN';
|
||||
setBleError: 'BLE_ERROR';
|
||||
setChildRef: 'STORE_RESPONSE';
|
||||
setChildRef: 'QRLOGIN_VIA_DEEP_LINK' | 'STORE_RESPONSE';
|
||||
setFlowType: 'SELECT_VC';
|
||||
setLinkCode: 'SCAN';
|
||||
setLinkCodeFromDeepLink: 'QRLOGIN_VIA_DEEP_LINK';
|
||||
setQuickShareData: 'SCAN';
|
||||
setReadyForBluetoothStateCheck: 'BLUETOOTH_PERMISSION_ENABLED';
|
||||
setReceiverInfo: 'CONNECTED';
|
||||
@@ -194,7 +210,7 @@ export interface Typegen0 {
|
||||
uptoAndroid11: '' | 'START_PERMISSION_CHECK';
|
||||
};
|
||||
eventsCausingServices: {
|
||||
QrLogin: 'SCAN';
|
||||
QrLogin: 'QRLOGIN_VIA_DEEP_LINK' | 'SCAN';
|
||||
checkBluetoothPermission:
|
||||
| ''
|
||||
| 'BLUETOOTH_STATE_DISABLED'
|
||||
@@ -251,6 +267,7 @@ export interface Typegen0 {
|
||||
| 'loadVCS.idle'
|
||||
| 'loadVCS.navigatingToHome'
|
||||
| 'nearByDevicesPermissionDenied'
|
||||
| 'qrLoginViaDeepLink'
|
||||
| 'recheckBluetoothState'
|
||||
| 'recheckBluetoothState.checking'
|
||||
| 'recheckBluetoothState.enabled'
|
||||
|
||||
@@ -55,6 +55,7 @@ const ScanEvents = {
|
||||
}),
|
||||
ALLOWED: () => ({}),
|
||||
DENIED: () => ({}),
|
||||
QRLOGIN_VIA_DEEP_LINK: (linkCode: string) => ({linkCode}),
|
||||
};
|
||||
|
||||
export const ScanModel = createModel(
|
||||
|
||||
@@ -125,3 +125,7 @@ export function selectIsMinimumStorageRequiredForAuditEntryLimitReached(
|
||||
export function selectIsFaceVerificationConsent(state: State) {
|
||||
return state.matches('reviewing.faceVerificationConsent');
|
||||
}
|
||||
|
||||
export function selectIsQrLoginViaDeepLink(state: State) {
|
||||
return state.matches('qrLoginViaDeepLink');
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import * as Keychain from 'react-native-keychain';
|
||||
import Storage, {MMKV} from '../shared/storage';
|
||||
import binaryToBase64 from 'react-native/Libraries/Utilities/binaryToBase64';
|
||||
import {
|
||||
@@ -10,8 +9,7 @@ import {
|
||||
StateFrom,
|
||||
} from 'xstate';
|
||||
import {createModel} from 'xstate/lib/model';
|
||||
import {generateSecureRandom} from 'react-native-securerandom';
|
||||
import {error, log} from 'xstate/lib/actions';
|
||||
import {log} from 'xstate/lib/actions';
|
||||
import {
|
||||
isIOS,
|
||||
MY_VCS_STORE_KEY,
|
||||
@@ -37,7 +35,6 @@ import {
|
||||
sendErrorEvent,
|
||||
getErrorEventData,
|
||||
} from '../shared/telemetry/TelemetryUtils';
|
||||
import RNSecureKeyStore from 'react-native-secure-key-store';
|
||||
import {Buffer} from 'buffer';
|
||||
import {VC} from './VerifiableCredential/VCMetaMachine/vc';
|
||||
|
||||
@@ -100,13 +97,41 @@ export const storeMachine =
|
||||
events: {} as EventFrom<typeof model>,
|
||||
},
|
||||
id: 'store',
|
||||
initial: !isHardwareKeystoreExists
|
||||
? 'gettingEncryptionKey'
|
||||
: 'checkEncryptionKey',
|
||||
initial: 'checkFreshInstall',
|
||||
states: {
|
||||
checkFreshInstall: {
|
||||
invoke: {
|
||||
src: 'checkFreshInstall',
|
||||
},
|
||||
on: {
|
||||
READY: [
|
||||
{
|
||||
cond: 'hasData',
|
||||
target: !isHardwareKeystoreExists
|
||||
? 'gettingEncryptionKey'
|
||||
: 'checkEncryptionKey',
|
||||
},
|
||||
{
|
||||
target: 'clearIosKeys',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
clearIosKeys: {
|
||||
invoke: {
|
||||
src: 'clearKeys',
|
||||
onDone: [
|
||||
{
|
||||
target: !isHardwareKeystoreExists
|
||||
? 'gettingEncryptionKey'
|
||||
: 'checkEncryptionKey',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
checkEncryptionKey: {
|
||||
invoke: {
|
||||
src: 'hasAndroidEncryptionKey',
|
||||
src: 'hasEncryptionKey',
|
||||
},
|
||||
on: {
|
||||
READY: {
|
||||
@@ -298,15 +323,32 @@ export const storeMachine =
|
||||
|
||||
services: {
|
||||
clear: () => clear(),
|
||||
hasAndroidEncryptionKey: () => async callback => {
|
||||
const hasSetCredentials =
|
||||
RNSecureKeystoreModule.hasAlias(ENCRYPTION_ID);
|
||||
clearKeys: () => async _ => {
|
||||
if (isIOS()) {
|
||||
const {RNSecureKeystoreModule} = NativeModules;
|
||||
await RNSecureKeystoreModule.clearKeys();
|
||||
}
|
||||
return;
|
||||
},
|
||||
checkFreshInstall: () => async callback => {
|
||||
const response = await getItem('auth', null, '');
|
||||
callback(model.events.READY());
|
||||
},
|
||||
hasEncryptionKey: () => async callback => {
|
||||
let hasSetCredentials;
|
||||
try {
|
||||
hasSetCredentials = await RNSecureKeystoreModule.hasAlias(
|
||||
ENCRYPTION_ID,
|
||||
);
|
||||
} catch (e) {
|
||||
hasSetCredentials = false;
|
||||
}
|
||||
if (hasSetCredentials) {
|
||||
try {
|
||||
const base64EncodedString =
|
||||
Buffer.from('Dummy').toString('base64');
|
||||
await RNSecureKeystoreModule.encryptData(
|
||||
DUMMY_KEY_FOR_BIOMETRIC_ALIAS,
|
||||
ENCRYPTION_ID,
|
||||
base64EncodedString,
|
||||
);
|
||||
} catch (e) {
|
||||
@@ -492,7 +534,7 @@ export const storeMachine =
|
||||
if (isIOS()) {
|
||||
RNSecureKeyStore.setResetOnAppUninstallTo(false);
|
||||
}
|
||||
const existingCredentials = await Keychain.getGenericPassword();
|
||||
const existingCredentials = '';
|
||||
if (existingCredentials) {
|
||||
console.log('Credentials successfully loaded for user');
|
||||
callback(model.events.KEY_RECEIVED(existingCredentials.password));
|
||||
@@ -513,50 +555,30 @@ export const storeMachine =
|
||||
}
|
||||
},
|
||||
generateEncryptionKey: () => async callback => {
|
||||
const randomBytes = await generateSecureRandom(32);
|
||||
const randomBytesString = binaryToBase64(randomBytes);
|
||||
if (!isHardwareKeystoreExists) {
|
||||
const hasSetCredentials = await Keychain.setGenericPassword(
|
||||
ENCRYPTION_ID,
|
||||
randomBytesString,
|
||||
callback(
|
||||
model.events.ERROR(
|
||||
new Error('Could not generate keychain credentials.'),
|
||||
),
|
||||
);
|
||||
|
||||
if (hasSetCredentials) {
|
||||
callback(model.events.KEY_RECEIVED(randomBytesString));
|
||||
} else {
|
||||
sendErrorEvent(
|
||||
getErrorEventData(
|
||||
TelemetryConstants.FlowType.fetchData,
|
||||
'',
|
||||
'Could not generate keychain credentials',
|
||||
),
|
||||
);
|
||||
callback(
|
||||
model.events.ERROR(
|
||||
new Error('Could not generate keychain credentials.'),
|
||||
),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
const isBiometricsEnabled =
|
||||
RNSecureKeystoreModule.hasBiometricsEnabled();
|
||||
await RNSecureKeystoreModule.hasBiometricsEnabled();
|
||||
await RNSecureKeystoreModule.generateKey(
|
||||
ENCRYPTION_ID,
|
||||
isBiometricsEnabled,
|
||||
AUTH_TIMEOUT,
|
||||
);
|
||||
RNSecureKeystoreModule.generateHmacshaKey(HMAC_ALIAS);
|
||||
RNSecureKeystoreModule.generateKey(
|
||||
DUMMY_KEY_FOR_BIOMETRIC_ALIAS,
|
||||
isBiometricsEnabled,
|
||||
0,
|
||||
);
|
||||
callback(model.events.KEY_RECEIVED(''));
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
guards: {
|
||||
hasData: (_, event: any) => {
|
||||
return event.data !== null;
|
||||
},
|
||||
isCustomSecureKeystore: () => isHardwareKeystoreExists,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,48 +1,85 @@
|
||||
// This file was automatically generated. Edits will be overwritten
|
||||
|
||||
// This file was automatically generated. Edits will be overwritten
|
||||
|
||||
export interface Typegen0 {
|
||||
'@@xstate/typegen': true;
|
||||
internalEvents: {
|
||||
"done.invoke._store": { type: "done.invoke._store"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
|
||||
"done.invoke.store.resettingStorage:invocation[0]": { type: "done.invoke.store.resettingStorage:invocation[0]"; data: unknown; __tip: "See the XState TS docs to learn how to strongly type this." };
|
||||
"error.platform._store": { type: "error.platform._store"; data: unknown };
|
||||
"xstate.init": { type: "xstate.init" };
|
||||
};
|
||||
invokeSrcNameMap: {
|
||||
"checkStorageInitialisedOrNot": "done.invoke.store.checkStorageInitialisation:invocation[0]";
|
||||
"clear": "done.invoke.store.resettingStorage:invocation[0]";
|
||||
"generateEncryptionKey": "done.invoke.store.generatingEncryptionKey:invocation[0]";
|
||||
"getEncryptionKey": "done.invoke.store.gettingEncryptionKey:invocation[0]";
|
||||
"hasAndroidEncryptionKey": "done.invoke.store.checkEncryptionKey:invocation[0]";
|
||||
"store": "done.invoke._store";
|
||||
};
|
||||
missingImplementations: {
|
||||
actions: never;
|
||||
delays: never;
|
||||
guards: never;
|
||||
services: never;
|
||||
};
|
||||
eventsCausingActions: {
|
||||
"forwardStoreRequest": "APPEND" | "CLEAR" | "EXPORT" | "FETCH_ALL_WELLKNOWN_CONFIG" | "GET" | "GET_VCS_DATA" | "PREPEND" | "REMOVE" | "REMOVE_ITEMS" | "REMOVE_VC_METADATA" | "RESTORE_BACKUP" | "SET" | "UPDATE";
|
||||
"notifyParent": "KEY_RECEIVED" | "READY" | "done.invoke.store.resettingStorage:invocation[0]";
|
||||
"setEncryptionKey": "KEY_RECEIVED";
|
||||
};
|
||||
eventsCausingDelays: {
|
||||
|
||||
};
|
||||
eventsCausingGuards: {
|
||||
"isCustomSecureKeystore": "KEY_RECEIVED";
|
||||
};
|
||||
eventsCausingServices: {
|
||||
"checkStorageInitialisedOrNot": "ERROR";
|
||||
"clear": "KEY_RECEIVED";
|
||||
"generateEncryptionKey": "ERROR" | "IGNORE" | "READY";
|
||||
"getEncryptionKey": "TRY_AGAIN";
|
||||
"hasAndroidEncryptionKey": never;
|
||||
"store": "KEY_RECEIVED" | "READY" | "done.invoke.store.resettingStorage:invocation[0]";
|
||||
};
|
||||
matchesStates: "checkEncryptionKey" | "checkStorageInitialisation" | "failedReadingKey" | "generatingEncryptionKey" | "gettingEncryptionKey" | "ready" | "resettingStorage";
|
||||
tags: never;
|
||||
}
|
||||
|
||||
export interface Typegen0 {
|
||||
'@@xstate/typegen': true;
|
||||
internalEvents: {
|
||||
'done.invoke._store': {
|
||||
type: 'done.invoke._store';
|
||||
data: unknown;
|
||||
__tip: 'See the XState TS docs to learn how to strongly type this.';
|
||||
};
|
||||
'done.invoke.store.resettingStorage:invocation[0]': {
|
||||
type: 'done.invoke.store.resettingStorage:invocation[0]';
|
||||
data: unknown;
|
||||
__tip: 'See the XState TS docs to learn how to strongly type this.';
|
||||
};
|
||||
'error.platform._store': {type: 'error.platform._store'; data: unknown};
|
||||
'xstate.init': {type: 'xstate.init'};
|
||||
};
|
||||
invokeSrcNameMap: {
|
||||
checkFreshInstall: 'done.invoke.store.checkFreshInstall:invocation[0]';
|
||||
checkStorageInitialisedOrNot: 'done.invoke.store.checkStorageInitialisation:invocation[0]';
|
||||
clear: 'done.invoke.store.resettingStorage:invocation[0]';
|
||||
clearKeys: 'done.invoke.store.clearIosKeys:invocation[0]';
|
||||
generateEncryptionKey: 'done.invoke.store.generatingEncryptionKey:invocation[0]';
|
||||
getEncryptionKey: 'done.invoke.store.gettingEncryptionKey:invocation[0]';
|
||||
hasEncryptionKey: 'done.invoke.store.checkEncryptionKey:invocation[0]';
|
||||
store: 'done.invoke._store';
|
||||
};
|
||||
missingImplementations: {
|
||||
actions: never;
|
||||
delays: never;
|
||||
guards: never;
|
||||
services: never;
|
||||
};
|
||||
eventsCausingActions: {
|
||||
forwardStoreRequest:
|
||||
| 'APPEND'
|
||||
| 'CLEAR'
|
||||
| 'EXPORT'
|
||||
| 'FETCH_ALL_WELLKNOWN_CONFIG'
|
||||
| 'GET'
|
||||
| 'GET_VCS_DATA'
|
||||
| 'PREPEND'
|
||||
| 'REMOVE'
|
||||
| 'REMOVE_ITEMS'
|
||||
| 'REMOVE_VC_METADATA'
|
||||
| 'RESTORE_BACKUP'
|
||||
| 'SET'
|
||||
| 'UPDATE';
|
||||
notifyParent:
|
||||
| 'KEY_RECEIVED'
|
||||
| 'READY'
|
||||
| 'done.invoke.store.resettingStorage:invocation[0]';
|
||||
setEncryptionKey: 'KEY_RECEIVED';
|
||||
};
|
||||
eventsCausingDelays: {};
|
||||
eventsCausingGuards: {
|
||||
hasData: 'READY';
|
||||
isCustomSecureKeystore: 'KEY_RECEIVED';
|
||||
};
|
||||
eventsCausingServices: {
|
||||
checkFreshInstall: 'xstate.init';
|
||||
checkStorageInitialisedOrNot: 'ERROR';
|
||||
clear: 'KEY_RECEIVED';
|
||||
clearKeys: 'READY';
|
||||
generateEncryptionKey: 'ERROR' | 'IGNORE' | 'READY';
|
||||
getEncryptionKey: 'TRY_AGAIN';
|
||||
hasEncryptionKey: never;
|
||||
store:
|
||||
| 'KEY_RECEIVED'
|
||||
| 'READY'
|
||||
| 'done.invoke.store.resettingStorage:invocation[0]';
|
||||
};
|
||||
matchesStates:
|
||||
| 'checkEncryptionKey'
|
||||
| 'checkFreshInstall'
|
||||
| 'checkStorageInitialisation'
|
||||
| 'clearIosKeys'
|
||||
| 'failedReadingKey'
|
||||
| 'generatingEncryptionKey'
|
||||
| 'gettingEncryptionKey'
|
||||
| 'ready'
|
||||
| 'resettingStorage';
|
||||
tags: never;
|
||||
}
|
||||
|
||||
137
package-lock.json
generated
137
package-lock.json
generated
@@ -18,6 +18,8 @@
|
||||
"@expo/metro-config": "~0.10.0",
|
||||
"@invertase/react-native-apple-authentication": "^2.3.0",
|
||||
"@iriscan/biometric-sdk-react-native": "0.2.6",
|
||||
"@noble/hashes": "^1.4.0",
|
||||
"@noble/secp256k1": "2.0.0",
|
||||
"@react-native-clipboard/clipboard": "^1.10.0",
|
||||
"@react-native-community/image-editor": "^4.2.0",
|
||||
"@react-native-community/netinfo": "9.3.7",
|
||||
@@ -28,6 +30,7 @@
|
||||
"@react-navigation/native-stack": "^6.1.0",
|
||||
"@robinbobin/react-native-google-drive-api-wrapper": "^1.2.4",
|
||||
"@xstate/react": "^3.0.1",
|
||||
"asn1.js": "^5.4.1",
|
||||
"base45-web": "^1.0.2",
|
||||
"base64url-universal": "^1.1.0",
|
||||
"buffer": "^6.0.3",
|
||||
@@ -70,8 +73,8 @@
|
||||
"react-native-elements": "3.4.3",
|
||||
"react-native-fs": "^2.18.0",
|
||||
"react-native-gesture-handler": "~2.9.0",
|
||||
"react-native-get-random-values": "^1.11.0",
|
||||
"react-native-image-colors": "^2.4.0",
|
||||
"react-native-keychain": "^8.0.0",
|
||||
"react-native-linear-gradient": "^2.8.0",
|
||||
"react-native-localize": "^3.0.2",
|
||||
"react-native-location": "^2.5.0",
|
||||
@@ -83,7 +86,6 @@
|
||||
"react-native-rsa-native": "^2.0.5",
|
||||
"react-native-safe-area-context": "4.5.0",
|
||||
"react-native-screens": "~3.20.0",
|
||||
"react-native-secure-key-store": "^2.0.10",
|
||||
"react-native-securerandom": "^1.0.1",
|
||||
"react-native-shimmer-placeholder": "^2.0.9",
|
||||
"react-native-spinkit": "^1.5.1",
|
||||
@@ -5851,6 +5853,30 @@
|
||||
"eslint-scope": "5.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@noble/hashes": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz",
|
||||
"integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@noble/secp256k1": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-2.0.0.tgz",
|
||||
"integrity": "sha512-rUGBd95e2a45rlmFTqQJYEFA4/gdIARFfuTuTqLglz0PZ6AKyzyXsEZZq7UZn8hZsvaBgpCzKKBJizT2cJERXw==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@nodelib/fs.scandir": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||
@@ -9685,6 +9711,18 @@
|
||||
"resolved": "https://registry.npmjs.org/asmcrypto.js/-/asmcrypto.js-0.22.0.tgz",
|
||||
"integrity": "sha512-usgMoyXjMbx/ZPdzTSXExhMPur2FTdz/Vo5PVx2gIaBcdAAJNOFlsdgqveM8Cff7W0v+xrf9BwjOV26JSAF9qA=="
|
||||
},
|
||||
"node_modules/asn1.js": {
|
||||
"version": "5.4.1",
|
||||
"resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz",
|
||||
"integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bn.js": "^4.0.0",
|
||||
"inherits": "^2.0.1",
|
||||
"minimalistic-assert": "^1.0.0",
|
||||
"safer-buffer": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/asn1js": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/asn1js/-/asn1js-2.3.2.tgz",
|
||||
@@ -10437,6 +10475,12 @@
|
||||
"resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.1.0.tgz",
|
||||
"integrity": "sha512-vHdS19CnY3hwiNdkaqk93DvjVLfbEcI8mys4UjuWrlX1haDmroo8o4xCzh4wD6DGV6HxRCyauwhHRqMTfERtjw=="
|
||||
},
|
||||
"node_modules/bn.js": {
|
||||
"version": "4.12.0",
|
||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
|
||||
"integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/body-parser": {
|
||||
"version": "1.20.2",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
|
||||
@@ -14321,6 +14365,7 @@
|
||||
"version": "13.3.0",
|
||||
"resolved": "https://registry.npmjs.org/expo-local-authentication/-/expo-local-authentication-13.3.0.tgz",
|
||||
"integrity": "sha512-HZ2L9GOQGooV+6kT2wLrR42BlxczT4N18kPY6HF82S31/a/YHslgUUt1lmHNU64NJViSCOBaTeVCjh8t/BeNEA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"invariant": "^2.2.4"
|
||||
},
|
||||
@@ -14890,6 +14935,12 @@
|
||||
"node": "> 0.1.90"
|
||||
}
|
||||
},
|
||||
"node_modules/fast-base64-decode": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-base64-decode/-/fast-base64-decode-1.0.0.tgz",
|
||||
"integrity": "sha512-qwaScUgUGBYeDNRnbc/KyllVU88Jk1pRHPStuF/lO7B0/RTRLj7U0lkdTAutlBblY08rwZDff6tNU9cjv6j//Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-deep-equal": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||
@@ -22347,6 +22398,12 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/minimalistic-assert": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
|
||||
"integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
@@ -25117,6 +25174,18 @@
|
||||
"react-native": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-get-random-values": {
|
||||
"version": "1.11.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-get-random-values/-/react-native-get-random-values-1.11.0.tgz",
|
||||
"integrity": "sha512-4BTbDbRmS7iPdhYLRcz3PGFIpFJBwNZg9g42iwa2P6FOv9vZj/xJc678RZXnLNZzd0qd7Q3CCF6Yd+CU2eoXKQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-base64-decode": "^1.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react-native": ">=0.56"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-gradle-plugin": {
|
||||
"version": "0.71.19",
|
||||
"resolved": "https://registry.npmjs.org/react-native-gradle-plugin/-/react-native-gradle-plugin-0.71.19.tgz",
|
||||
@@ -25135,11 +25204,6 @@
|
||||
"react-native": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-keychain": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-keychain/-/react-native-keychain-8.0.0.tgz",
|
||||
"integrity": "sha512-c7Cs+YQN26UaQsRG1dmlXL7VL2ctnXwH/dl0IOMEQ7ZaL2NdN313YSAI8ZEZZjrVhNmPsyWEuvTFqWrdpItqQg=="
|
||||
},
|
||||
"node_modules/react-native-linear-gradient": {
|
||||
"version": "2.8.3",
|
||||
"resolved": "https://registry.npmjs.org/react-native-linear-gradient/-/react-native-linear-gradient-2.8.3.tgz",
|
||||
@@ -25285,11 +25349,6 @@
|
||||
"react-native": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-secure-key-store": {
|
||||
"version": "2.0.10",
|
||||
"resolved": "https://registry.npmjs.org/react-native-secure-key-store/-/react-native-secure-key-store-2.0.10.tgz",
|
||||
"integrity": "sha512-K7aVlIGxyklnjhCidVexVgZF3LsgUD9GIxMy2NB/xkQsS9E2SJWkD/fJ56e25L2I6a9Mp1zuJrKnCtfBs1CvAw=="
|
||||
},
|
||||
"node_modules/react-native-securerandom": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/react-native-securerandom/-/react-native-securerandom-1.0.1.tgz",
|
||||
@@ -34069,6 +34128,16 @@
|
||||
"eslint-scope": "5.1.1"
|
||||
}
|
||||
},
|
||||
"@noble/hashes": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz",
|
||||
"integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg=="
|
||||
},
|
||||
"@noble/secp256k1": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-2.0.0.tgz",
|
||||
"integrity": "sha512-rUGBd95e2a45rlmFTqQJYEFA4/gdIARFfuTuTqLglz0PZ6AKyzyXsEZZq7UZn8hZsvaBgpCzKKBJizT2cJERXw=="
|
||||
},
|
||||
"@nodelib/fs.scandir": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||
@@ -36908,6 +36977,17 @@
|
||||
"resolved": "https://registry.npmjs.org/asmcrypto.js/-/asmcrypto.js-0.22.0.tgz",
|
||||
"integrity": "sha512-usgMoyXjMbx/ZPdzTSXExhMPur2FTdz/Vo5PVx2gIaBcdAAJNOFlsdgqveM8Cff7W0v+xrf9BwjOV26JSAF9qA=="
|
||||
},
|
||||
"asn1.js": {
|
||||
"version": "5.4.1",
|
||||
"resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz",
|
||||
"integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==",
|
||||
"requires": {
|
||||
"bn.js": "^4.0.0",
|
||||
"inherits": "^2.0.1",
|
||||
"minimalistic-assert": "^1.0.0",
|
||||
"safer-buffer": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"asn1js": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/asn1js/-/asn1js-2.3.2.tgz",
|
||||
@@ -37501,6 +37581,11 @@
|
||||
"resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.1.0.tgz",
|
||||
"integrity": "sha512-vHdS19CnY3hwiNdkaqk93DvjVLfbEcI8mys4UjuWrlX1haDmroo8o4xCzh4wD6DGV6HxRCyauwhHRqMTfERtjw=="
|
||||
},
|
||||
"bn.js": {
|
||||
"version": "4.12.0",
|
||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
|
||||
"integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA=="
|
||||
},
|
||||
"body-parser": {
|
||||
"version": "1.20.2",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
|
||||
@@ -40953,6 +41038,11 @@
|
||||
"resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz",
|
||||
"integrity": "sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ=="
|
||||
},
|
||||
"fast-base64-decode": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-base64-decode/-/fast-base64-decode-1.0.0.tgz",
|
||||
"integrity": "sha512-qwaScUgUGBYeDNRnbc/KyllVU88Jk1pRHPStuF/lO7B0/RTRLj7U0lkdTAutlBblY08rwZDff6tNU9cjv6j//Q=="
|
||||
},
|
||||
"fast-deep-equal": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||
@@ -46517,6 +46607,11 @@
|
||||
"resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz",
|
||||
"integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg=="
|
||||
},
|
||||
"minimalistic-assert": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
|
||||
"integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
@@ -48604,6 +48699,14 @@
|
||||
"prop-types": "^15.7.2"
|
||||
}
|
||||
},
|
||||
"react-native-get-random-values": {
|
||||
"version": "1.11.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-get-random-values/-/react-native-get-random-values-1.11.0.tgz",
|
||||
"integrity": "sha512-4BTbDbRmS7iPdhYLRcz3PGFIpFJBwNZg9g42iwa2P6FOv9vZj/xJc678RZXnLNZzd0qd7Q3CCF6Yd+CU2eoXKQ==",
|
||||
"requires": {
|
||||
"fast-base64-decode": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"react-native-gradle-plugin": {
|
||||
"version": "0.71.19",
|
||||
"resolved": "https://registry.npmjs.org/react-native-gradle-plugin/-/react-native-gradle-plugin-0.71.19.tgz",
|
||||
@@ -48617,11 +48720,6 @@
|
||||
"node-vibrant": "3.1.6"
|
||||
}
|
||||
},
|
||||
"react-native-keychain": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-keychain/-/react-native-keychain-8.0.0.tgz",
|
||||
"integrity": "sha512-c7Cs+YQN26UaQsRG1dmlXL7VL2ctnXwH/dl0IOMEQ7ZaL2NdN313YSAI8ZEZZjrVhNmPsyWEuvTFqWrdpItqQg=="
|
||||
},
|
||||
"react-native-linear-gradient": {
|
||||
"version": "2.8.3",
|
||||
"resolved": "https://registry.npmjs.org/react-native-linear-gradient/-/react-native-linear-gradient-2.8.3.tgz",
|
||||
@@ -48714,11 +48812,6 @@
|
||||
"warn-once": "^0.1.0"
|
||||
}
|
||||
},
|
||||
"react-native-secure-key-store": {
|
||||
"version": "2.0.10",
|
||||
"resolved": "https://registry.npmjs.org/react-native-secure-key-store/-/react-native-secure-key-store-2.0.10.tgz",
|
||||
"integrity": "sha512-K7aVlIGxyklnjhCidVexVgZF3LsgUD9GIxMy2NB/xkQsS9E2SJWkD/fJ56e25L2I6a9Mp1zuJrKnCtfBs1CvAw=="
|
||||
},
|
||||
"react-native-securerandom": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/react-native-securerandom/-/react-native-securerandom-1.0.1.tgz",
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
"@expo/metro-config": "~0.10.0",
|
||||
"@invertase/react-native-apple-authentication": "^2.3.0",
|
||||
"@iriscan/biometric-sdk-react-native": "0.2.6",
|
||||
"@noble/hashes": "^1.4.0",
|
||||
"@noble/secp256k1": "2.0.0",
|
||||
"@react-native-clipboard/clipboard": "^1.10.0",
|
||||
"@react-native-community/image-editor": "^4.2.0",
|
||||
"@react-native-community/netinfo": "9.3.7",
|
||||
@@ -31,6 +33,7 @@
|
||||
"@react-navigation/native-stack": "^6.1.0",
|
||||
"@robinbobin/react-native-google-drive-api-wrapper": "^1.2.4",
|
||||
"@xstate/react": "^3.0.1",
|
||||
"asn1.js": "^5.4.1",
|
||||
"base45-web": "^1.0.2",
|
||||
"base64url-universal": "^1.1.0",
|
||||
"buffer": "^6.0.3",
|
||||
@@ -73,8 +76,8 @@
|
||||
"react-native-elements": "3.4.3",
|
||||
"react-native-fs": "^2.18.0",
|
||||
"react-native-gesture-handler": "~2.9.0",
|
||||
"react-native-get-random-values": "^1.11.0",
|
||||
"react-native-image-colors": "^2.4.0",
|
||||
"react-native-keychain": "^8.0.0",
|
||||
"react-native-linear-gradient": "^2.8.0",
|
||||
"react-native-localize": "^3.0.2",
|
||||
"react-native-location": "^2.5.0",
|
||||
@@ -86,7 +89,6 @@
|
||||
"react-native-rsa-native": "^2.0.5",
|
||||
"react-native-safe-area-context": "4.5.0",
|
||||
"react-native-screens": "~3.20.0",
|
||||
"react-native-secure-key-store": "^2.0.10",
|
||||
"react-native-securerandom": "^1.0.1",
|
||||
"react-native-shimmer-placeholder": "^2.0.9",
|
||||
"react-native-spinkit": "^1.5.1",
|
||||
|
||||
@@ -12,8 +12,8 @@ import {NotificationsScreen} from '../screens/NotificationsScreen';
|
||||
import {SetupLanguageScreen} from '../screens/SetupLanguageScreen';
|
||||
import {IntroSlidersScreen} from '../screens/Home/IntroSlidersScreen';
|
||||
import {RequestLayout} from '../screens/Request/RequestLayout';
|
||||
import {RequestStackParamList} from '../screens/Request/RequestLayoutController';
|
||||
import {SplashScreen} from '../screens/SplashScreen';
|
||||
import {RequestStackParamList} from './routesConstants';
|
||||
|
||||
export const baseRoutes: Screen[] = [
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, {useContext} from 'react';
|
||||
import React, {useContext, useEffect} from 'react';
|
||||
import {
|
||||
BottomTabNavigationOptions,
|
||||
createBottomTabNavigator,
|
||||
@@ -17,9 +17,18 @@ import {CopilotProvider} from 'react-native-copilot';
|
||||
import {View} from 'react-native';
|
||||
import {CopilotTooltip} from '../components/CopilotTooltip';
|
||||
import {Copilot} from '../components/ui/Copilot';
|
||||
import {useSelector} from '@xstate/react';
|
||||
import {selectIsLinkCode} from '../machines/app';
|
||||
import {NavigationProp, useNavigation} from '@react-navigation/native';
|
||||
import {BOTTOM_TAB_ROUTES, ScanStackParamList} from '../routes/routesConstants';
|
||||
import {MainBottomTabParamList} from '../routes/routeTypes';
|
||||
|
||||
const {Navigator, Screen} = createBottomTabNavigator();
|
||||
|
||||
type ScanLayoutNavigation = NavigationProp<
|
||||
ScanStackParamList & MainBottomTabParamList
|
||||
>;
|
||||
|
||||
export const MainLayout: React.FC = () => {
|
||||
const {t} = useTranslation('MainLayout');
|
||||
|
||||
@@ -31,6 +40,15 @@ export const MainLayout: React.FC = () => {
|
||||
tabBarActiveTintColor: Theme.Colors.IconBg,
|
||||
...Theme.BottomTabBarStyle,
|
||||
};
|
||||
const navigation = useNavigation<ScanLayoutNavigation>();
|
||||
|
||||
const linkCode = useSelector(appService, selectIsLinkCode);
|
||||
|
||||
useEffect(() => {
|
||||
if (linkCode != '') {
|
||||
navigation.navigate(BOTTOM_TAB_ROUTES.share);
|
||||
}
|
||||
}, [linkCode]);
|
||||
|
||||
return (
|
||||
<CopilotProvider
|
||||
|
||||
@@ -24,6 +24,7 @@ import {
|
||||
selectIsFaceIdentityVerified,
|
||||
selectCredential,
|
||||
selectVerifiableCredentialData,
|
||||
selectIsQrLoginViaDeepLink,
|
||||
} from '../../machines/bleShare/scan/scanSelectors';
|
||||
import {
|
||||
selectBleError,
|
||||
@@ -43,6 +44,7 @@ import {BOTTOM_TAB_ROUTES, SCAN_ROUTES} from '../../routes/routesConstants';
|
||||
import {ScanStackParamList} from '../../routes/routesConstants';
|
||||
import {VCShareFlowType} from '../../shared/Utils';
|
||||
import {Theme} from '../../components/ui/styleUtils';
|
||||
import {APP_EVENTS, selectIsLinkCode} from '../../machines/app';
|
||||
|
||||
type ScanLayoutNavigation = NavigationProp<
|
||||
ScanStackParamList & MainBottomTabParamList
|
||||
@@ -75,6 +77,10 @@ export function useScanLayout() {
|
||||
scanService,
|
||||
selectVerifiableCredentialData,
|
||||
);
|
||||
const isQrLoginViaDeepLink = useSelector(
|
||||
scanService,
|
||||
selectIsQrLoginViaDeepLink,
|
||||
);
|
||||
|
||||
const locationError = {message: '', button: ''};
|
||||
|
||||
@@ -121,6 +127,7 @@ export function useScanLayout() {
|
||||
scanService,
|
||||
selectIsExchangingDeviceInfoTimeout,
|
||||
);
|
||||
const linkCode = useSelector(appService, selectIsLinkCode);
|
||||
const isAccepted = useSelector(scanService, selectIsAccepted);
|
||||
const isRejected = useSelector(scanService, selectIsRejected);
|
||||
const isSent = useSelector(scanService, selectIsSent);
|
||||
@@ -262,6 +269,9 @@ export function useScanLayout() {
|
||||
if (isDone) {
|
||||
changeTabBarVisible('flex');
|
||||
navigation.navigate(BOTTOM_TAB_ROUTES.home);
|
||||
} else if (isQrLoginViaDeepLink) {
|
||||
scanService.send(ScanEvents.QRLOGIN_VIA_DEEP_LINK(linkCode));
|
||||
appService.send(APP_EVENTS.RESET_LINKCODE());
|
||||
} else if (
|
||||
isReviewing &&
|
||||
flowType === VCShareFlowType.SIMPLE_SHARE &&
|
||||
@@ -284,6 +294,7 @@ export function useScanLayout() {
|
||||
isBleError,
|
||||
flowType,
|
||||
isAccepted,
|
||||
isQrLoginViaDeepLink,
|
||||
]);
|
||||
|
||||
return {
|
||||
|
||||
@@ -2,16 +2,11 @@ import {
|
||||
GoogleSignin,
|
||||
statusCodes,
|
||||
} from '@react-native-google-signin/google-signin';
|
||||
import RNSecureStorage, {ACCESSIBLE} from 'react-native-secure-key-store';
|
||||
import {CloudStorage, CloudStorageScope} from 'react-native-cloud-storage';
|
||||
import {GOOGLE_ANDROID_CLIENT_ID} from 'react-native-dotenv';
|
||||
import {readFile, writeFile} from 'react-native-fs';
|
||||
import {BackupDetails} from '../types/backup-and-restore/backup';
|
||||
import {
|
||||
AppleButton,
|
||||
appleAuth,
|
||||
} from '@invertase/react-native-apple-authentication';
|
||||
import jwt_decode from 'jwt-decode';
|
||||
import {appleAuth} from '@invertase/react-native-apple-authentication';
|
||||
import {bytesToMB, sleep} from './commonUtil';
|
||||
import {
|
||||
IOS_SIGNIN_FAILED,
|
||||
@@ -21,6 +16,7 @@ import {
|
||||
} from './constants';
|
||||
import fileStorage, {backupDirectoryPath, zipFilePath} from './fileStorage';
|
||||
import {API} from './api';
|
||||
import {NativeModules} from 'react-native';
|
||||
|
||||
class Cloud {
|
||||
static status = {
|
||||
@@ -133,6 +129,7 @@ class Cloud {
|
||||
}
|
||||
|
||||
static async signIn(): Promise<SignInResult | IsIOSResult> {
|
||||
const {RNSecureKeystoreModule} = NativeModules;
|
||||
if (isIOS()) {
|
||||
let profileInfo;
|
||||
|
||||
@@ -145,10 +142,9 @@ class Cloud {
|
||||
const {email, nonce, identityToken, realUserStatus /* etc */} =
|
||||
appleAuthRequestResponse;
|
||||
profileInfo = {email: email, picture: null};
|
||||
await RNSecureStorage.set(
|
||||
await RNSecureKeystoreModule.storeData(
|
||||
'userIdentifier',
|
||||
JSON.stringify(appleAuthRequestResponse),
|
||||
{accessible: ACCESSIBLE.WHEN_UNLOCKED},
|
||||
);
|
||||
|
||||
return {status: this.status.SUCCESS, profileInfo: profileInfo};
|
||||
@@ -203,11 +199,14 @@ class Cloud {
|
||||
}
|
||||
|
||||
static async isSignedInAlready(): Promise<isSignedInResult> {
|
||||
const {RNSecureKeystoreModule} = NativeModules;
|
||||
try {
|
||||
if (isIOS()) {
|
||||
const isSignedIn = await CloudStorage.isCloudAvailable();
|
||||
|
||||
const userIdentifier = await RNSecureStorage.get('userIdentifier');
|
||||
const userIdentifier = await RNSecureKeystoreModule.getData(
|
||||
'userIdentifier',
|
||||
)[0];
|
||||
const userToken = JSON.parse(userIdentifier + '');
|
||||
const user = userToken.user;
|
||||
const email = userToken.email;
|
||||
|
||||
@@ -24,6 +24,7 @@ export class VCMetadata {
|
||||
displayId: string = '';
|
||||
format: string = '';
|
||||
|
||||
downloadKeyType: string = '';
|
||||
constructor({
|
||||
idType = '',
|
||||
requestId = '',
|
||||
@@ -35,6 +36,7 @@ export class VCMetadata {
|
||||
isVerified = false,
|
||||
displayId = '',
|
||||
format = '',
|
||||
downloadKeyType = '',
|
||||
} = {}) {
|
||||
this.idType = idType;
|
||||
this.requestId = requestId;
|
||||
@@ -46,6 +48,7 @@ export class VCMetadata {
|
||||
this.isVerified = isVerified;
|
||||
this.displayId = displayId;
|
||||
this.format = format;
|
||||
this.downloadKeyType = downloadKeyType;
|
||||
}
|
||||
|
||||
//TODO: Remove any typing and use appropriate typing
|
||||
@@ -65,6 +68,7 @@ export class VCMetadata {
|
||||
: vc.vcMetadata
|
||||
? vc.vcMetadata.displayId
|
||||
: getDisplayId(vc.verifiableCredential),
|
||||
downloadKeyType: vc.downloadKeyType,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -104,7 +108,7 @@ export function parseMetadatas(metadataStrings: object[]) {
|
||||
return metadataStrings.map(o => new VCMetadata(o));
|
||||
}
|
||||
|
||||
export const getVCMetadata = (context: object) => {
|
||||
export const getVCMetadata = (context: object, keyType: string) => {
|
||||
const [issuer, protocol, credentialId] =
|
||||
context.credentialWrapper?.identifier.split(':');
|
||||
|
||||
@@ -117,6 +121,7 @@ export const getVCMetadata = (context: object) => {
|
||||
isVerified: context.vcMetadata.isVerified ?? false,
|
||||
displayId: getDisplayId(context.verifiableCredential),
|
||||
format: context.credentialWrapper.format,
|
||||
downloadKeyType: keyType,
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import {
|
||||
COMMON_PROPS_KEY,
|
||||
} from './constants';
|
||||
import {INITIAL_CONFIG} from './InitialConfig';
|
||||
import Keychain from 'react-native-keychain';
|
||||
import {getItem, setItem} from '../machines/store';
|
||||
import {faceMatchConfig} from './commonUtil';
|
||||
import {configure} from '@iriscan/biometric-sdk-react-native';
|
||||
@@ -193,19 +192,14 @@ async function generateCacheAPIFunctionWithCachePreference(
|
||||
fetchCall: (...props: any[]) => any,
|
||||
onErrorHardCodedValue?: any,
|
||||
) {
|
||||
const existingCredentials = await Keychain.getGenericPassword();
|
||||
try {
|
||||
const response = await getItem(
|
||||
cacheKey,
|
||||
null,
|
||||
existingCredentials?.password,
|
||||
);
|
||||
const response = await getItem(cacheKey, null, '');
|
||||
|
||||
if (response) {
|
||||
return response;
|
||||
} else {
|
||||
const response = await fetchCall();
|
||||
setItem(cacheKey, response, existingCredentials?.password).then(() =>
|
||||
setItem(cacheKey, response, '').then(() =>
|
||||
console.log('Cached response for ' + cacheKey),
|
||||
);
|
||||
|
||||
@@ -231,10 +225,9 @@ async function generateCacheAPIFunctionWithAPIPreference(
|
||||
fetchCall: (...props: any[]) => any,
|
||||
onErrorHardCodedValue?: any,
|
||||
) {
|
||||
const existingCredentials = await Keychain.getGenericPassword();
|
||||
try {
|
||||
const response = await fetchCall();
|
||||
setItem(cacheKey, response, existingCredentials.password).then(() =>
|
||||
setItem(cacheKey, response, '').then(() =>
|
||||
console.log('Cached response for ' + cacheKey),
|
||||
);
|
||||
return response;
|
||||
@@ -246,11 +239,7 @@ async function generateCacheAPIFunctionWithAPIPreference(
|
||||
|
||||
console.log(error);
|
||||
|
||||
const response = await getItem(
|
||||
cacheKey,
|
||||
null,
|
||||
existingCredentials.password,
|
||||
);
|
||||
const response = await getItem(cacheKey, null, '');
|
||||
|
||||
if (response) {
|
||||
return response;
|
||||
|
||||
6
shared/cryptoutil/KeyTypes.ts
Normal file
6
shared/cryptoutil/KeyTypes.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export enum KeyTypes {
|
||||
RS256 = 'RS256',
|
||||
ES256 = 'ES256',
|
||||
ES256K = 'ES256K',
|
||||
ED25519 = 'ED25519',
|
||||
}
|
||||
@@ -1,27 +1,167 @@
|
||||
import {KeyPair, RSA} from 'react-native-rsa-native';
|
||||
import {RSA} from 'react-native-rsa-native';
|
||||
import forge from 'node-forge';
|
||||
import {BIOMETRIC_CANCELLED, DEBUG_MODE_ENABLED, isIOS} from '../constants';
|
||||
import jose from 'node-jose';
|
||||
import {
|
||||
BIOMETRIC_CANCELLED,
|
||||
DEBUG_MODE_ENABLED,
|
||||
isAndroid,
|
||||
isIOS,
|
||||
} from '../constants';
|
||||
import {NativeModules} from 'react-native';
|
||||
import {BiometricCancellationError} from '../error/BiometricCancellationError';
|
||||
import {EncryptedOutput} from './encryptedOutput';
|
||||
import {Buffer} from 'buffer';
|
||||
import base64url from 'base64url';
|
||||
import {hmac} from '@noble/hashes/hmac';
|
||||
import {sha256} from '@noble/hashes/sha256';
|
||||
import 'react-native-get-random-values';
|
||||
import * as secp from '@noble/secp256k1';
|
||||
import base64 from 'react-native-base64';
|
||||
import {KeyTypes} from './KeyTypes';
|
||||
import convertDerToRsFormat from './signFormatConverter';
|
||||
|
||||
//polyfills setup
|
||||
secp.etc.hmacSha256Sync = (k, ...m) =>
|
||||
hmac(sha256, k, secp.etc.concatBytes(...m));
|
||||
secp.etc.hmacSha256Async = (k, ...m) =>
|
||||
Promise.resolve(secp.etc.hmacSha256Sync(k, ...m));
|
||||
const {RNSecureKeystoreModule} = NativeModules;
|
||||
// 5min
|
||||
export const AUTH_TIMEOUT = 5 * 60;
|
||||
export const ENCRYPTION_ID = 'c7c22a6c-9759-4605-ac88-46f4041d863d';
|
||||
export const ENCRYPTION_ID = 'c7c22a6c-9759-4605-ac88-46f4041d863k';
|
||||
export const HMAC_ALIAS = '860cc320-4248-11ee-be56-0242ac120002';
|
||||
//This key is used to request biometric at app open to reset auth timeout which is used by encryption key
|
||||
export const DUMMY_KEY_FOR_BIOMETRIC_ALIAS =
|
||||
'9a6cfc0e-4248-11ee-be56-0242ac120002';
|
||||
|
||||
export function generateKeys(): Promise<KeyPair> {
|
||||
return Promise.resolve(RSA.generateKeys(2048));
|
||||
export async function generateKeyPairRSA() {
|
||||
if (isAndroid() && isHardwareKeystoreExists) {
|
||||
const isBiometricsEnabled =
|
||||
await RNSecureKeystoreModule.hasBiometricsEnabled();
|
||||
return {
|
||||
publicKey: await RNSecureKeystoreModule.generateKeyPair(
|
||||
KeyTypes.RS256,
|
||||
KeyTypes.RS256,
|
||||
isBiometricsEnabled,
|
||||
0,
|
||||
),
|
||||
privateKey: '',
|
||||
};
|
||||
}
|
||||
const keyPair = await Promise.resolve(RSA.generateKeys(2048));
|
||||
return {
|
||||
publicKey: keyPair.public,
|
||||
privateKey: keyPair.private,
|
||||
};
|
||||
}
|
||||
|
||||
export function generateKeyPairECK1() {
|
||||
const privKey = secp.utils.randomPrivateKey();
|
||||
const decoder = new TextDecoder();
|
||||
const pubKey = secp.getPublicKey(privKey, false);
|
||||
return {
|
||||
publicKey: Buffer.from(pubKey).toString('base64'),
|
||||
privateKey: Buffer.from(privKey).toString('base64'),
|
||||
};
|
||||
}
|
||||
|
||||
export async function generateKeyPairECR1() {
|
||||
if (isAndroid()) {
|
||||
const isBiometricsEnabled =
|
||||
await RNSecureKeystoreModule.hasBiometricsEnabled();
|
||||
return {
|
||||
publicKey: await RNSecureKeystoreModule.generateKeyPair(
|
||||
KeyTypes.ES256,
|
||||
KeyTypes.ES256,
|
||||
isBiometricsEnabled,
|
||||
0,
|
||||
),
|
||||
privateKey: '',
|
||||
};
|
||||
}
|
||||
const keystore = jose.JWK.createKeyStore();
|
||||
const key = await keystore.generate('EC', 'P-256');
|
||||
const jwkPublicKey = key.toJSON(); // Public key JWK
|
||||
const jwkPrivateKey = key.toJSON(true); // Private key JWK (include private part)
|
||||
return {
|
||||
publicKey: JSON.stringify(jwkPublicKey),
|
||||
privateKey: JSON.stringify(jwkPrivateKey),
|
||||
};
|
||||
}
|
||||
|
||||
export async function generateKeyPairED() {
|
||||
return {
|
||||
privateKey: '',
|
||||
publicKey: '',
|
||||
};
|
||||
}
|
||||
|
||||
export async function generateKeyPair(keyType: any): Promise<any> {
|
||||
switch (keyType) {
|
||||
case KeyTypes.RS256:
|
||||
return generateKeyPairRSA();
|
||||
case KeyTypes.ES256:
|
||||
return generateKeyPairECR1();
|
||||
case KeyTypes.ES256K:
|
||||
return generateKeyPairECK1();
|
||||
case KeyTypes.ED25519:
|
||||
return generateKeyPairED();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
export async function checkAllKeyPairs() {
|
||||
const RSAKey = await fetchKeyPair(KeyTypes.RS256);
|
||||
const ECR1Key = await fetchKeyPair(KeyTypes.ES256);
|
||||
const ECK1Key = await fetchKeyPair(KeyTypes.ES256K);
|
||||
const EDKey = 'key';
|
||||
if (
|
||||
!(
|
||||
!!RSAKey.publicKey &&
|
||||
!!ECR1Key.publicKey &&
|
||||
!!ECK1Key.publicKey &&
|
||||
!!EDKey
|
||||
)
|
||||
)
|
||||
throw Error('Keys not present');
|
||||
}
|
||||
|
||||
export async function generateKeyPairsAndStore() {
|
||||
const {RNSecureKeystoreModule} = NativeModules;
|
||||
const RSAKeyPair = await generateKeyPair(KeyTypes.RS256);
|
||||
const ECR1KeyPair = await generateKeyPair(KeyTypes.ES256);
|
||||
const ECK1KeyPair = await generateKeyPair(KeyTypes.ES256K);
|
||||
//const EDKeyPair = generateKeyPair(KeyTypes.ED25519);
|
||||
await RNSecureKeystoreModule.storeGenericKey(
|
||||
ECK1KeyPair.publicKey,
|
||||
ECK1KeyPair.privateKey,
|
||||
KeyTypes.ES256K,
|
||||
);
|
||||
// await RNSecureKeystoreModule.storeGenericKey(
|
||||
// EDKeyPair.publicKey,
|
||||
// EDKeyPair.privateKey,
|
||||
// KeyTypes.ED25519,
|
||||
// );
|
||||
|
||||
if (isIOS()) {
|
||||
await RNSecureKeystoreModule.storeGenericKey(
|
||||
RSAKeyPair.publicKey,
|
||||
RSAKeyPair.privateKey,
|
||||
KeyTypes.RS256,
|
||||
);
|
||||
await RNSecureKeystoreModule.storeGenericKey(
|
||||
ECR1KeyPair.publicKey,
|
||||
ECR1KeyPair.privateKey,
|
||||
KeyTypes.ES256,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* isCustomKeystore is a cached check of existence of a hardware keystore.
|
||||
*/
|
||||
const {RNSecureKeystoreModule} = NativeModules;
|
||||
|
||||
export const isHardwareKeystoreExists = isCustomSecureKeystore();
|
||||
|
||||
export async function getJWT(
|
||||
@@ -29,12 +169,21 @@ export async function getJWT(
|
||||
payLoad: object,
|
||||
alias: string,
|
||||
privateKey: string,
|
||||
keyType: string,
|
||||
) {
|
||||
try {
|
||||
const header64 = encodeB64(JSON.stringify(header));
|
||||
const payLoad64 = encodeB64(JSON.stringify(payLoad));
|
||||
const preHash = header64 + '.' + payLoad64;
|
||||
const signature64 = await createSignature(privateKey, preHash, alias);
|
||||
const signature64 = await createSignature(
|
||||
privateKey,
|
||||
alias,
|
||||
preHash,
|
||||
keyType,
|
||||
header,
|
||||
payLoad,
|
||||
);
|
||||
if (keyType == KeyTypes.ES256 && isIOS()) return signature64;
|
||||
return header64 + '.' + payLoad64 + '.' + signature64;
|
||||
} catch (e) {
|
||||
console.error('Exception Occurred While Constructing JWT ', e);
|
||||
@@ -43,32 +192,97 @@ export async function getJWT(
|
||||
}
|
||||
|
||||
export async function createSignature(
|
||||
privateKey: string,
|
||||
preHash: string,
|
||||
alias: string,
|
||||
privateKey,
|
||||
alias,
|
||||
preHash,
|
||||
keyType: string,
|
||||
header,
|
||||
payload,
|
||||
) {
|
||||
switch (keyType) {
|
||||
case KeyTypes.RS256:
|
||||
return createSignatureRSA(privateKey, preHash);
|
||||
case KeyTypes.ES256:
|
||||
return createSignatureECR1(privateKey, header, payload, preHash);
|
||||
case KeyTypes.ES256K:
|
||||
return createSignatureECK1(privateKey, preHash);
|
||||
case KeyTypes.ED25519:
|
||||
return createSignatureED(privateKey, preHash);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
export async function createSignatureRSA(privateKey: string, preHash: string) {
|
||||
let signature64;
|
||||
|
||||
if (!isHardwareKeystoreExists) {
|
||||
const key = forge.pki.privateKeyFromPem(privateKey);
|
||||
const md = forge.md.sha256.create();
|
||||
md.update(preHash, 'utf8');
|
||||
|
||||
const signature = key.sign(md);
|
||||
return encodeB64(signature);
|
||||
throw Error;
|
||||
} else {
|
||||
try {
|
||||
signature64 = await RNSecureKeystoreModule.sign(alias, preHash);
|
||||
} catch (error) {
|
||||
console.error('Error in creating signature:', error);
|
||||
if (error.toString().includes(BIOMETRIC_CANCELLED)) {
|
||||
throw new BiometricCancellationError(error.toString());
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
if (isAndroid())
|
||||
signature64 = await RNSecureKeystoreModule.sign(
|
||||
KeyTypes.RS256,
|
||||
KeyTypes.RS256,
|
||||
preHash,
|
||||
);
|
||||
else {
|
||||
const key = forge.pki.privateKeyFromPem(privateKey);
|
||||
const md = forge.md.sha256.create();
|
||||
md.update(preHash, 'utf8');
|
||||
|
||||
return replaceCharactersInB64(signature64);
|
||||
const signature = key.sign(md);
|
||||
signature64 = encodeB64(signature);
|
||||
}
|
||||
}
|
||||
return replaceCharactersInB64(signature64);
|
||||
}
|
||||
|
||||
export async function createSignatureECK1(privateKey, prehash) {
|
||||
const sha = sha256(prehash);
|
||||
const sign = await secp.signAsync(sha, privateKey, {lowS: false});
|
||||
return base64url(Buffer.from(sign.toCompactRawBytes()));
|
||||
}
|
||||
|
||||
export async function createSignatureED(privateKey, prehash) {
|
||||
const sha = sha256(prehash);
|
||||
const sign = await secp.signAsync(sha, privateKey);
|
||||
return base64url(Buffer.from(sign.toCompactRawBytes()));
|
||||
}
|
||||
|
||||
export async function createSignatureECR1(
|
||||
privateKey,
|
||||
header,
|
||||
payload,
|
||||
preHash,
|
||||
) {
|
||||
if (!isHardwareKeystoreExists) {
|
||||
throw Error;
|
||||
} else {
|
||||
if (isAndroid()) {
|
||||
let signature64 = RNSecureKeystoreModule.sign(
|
||||
KeyTypes.ES256,
|
||||
KeyTypes.ES256,
|
||||
preHash,
|
||||
);
|
||||
const base64DeodedSignature = base64.decode(
|
||||
signature64.replace(/\n/g, ''),
|
||||
);
|
||||
const derSignature = Uint8Array.from(base64DeodedSignature, char =>
|
||||
char.charCodeAt(0),
|
||||
);
|
||||
signature64 = convertDerToRsFormat(derSignature);
|
||||
return replaceCharactersInB64(signature64);
|
||||
}
|
||||
}
|
||||
|
||||
const key = await jose.JWK.asKey(JSON.parse(privateKey));
|
||||
|
||||
const signer = await jose.JWS.createSign(
|
||||
{format: 'compact', fields: header},
|
||||
{key, reference: false},
|
||||
);
|
||||
const jws = await signer.update(JSON.stringify(payload)).final();
|
||||
return jws;
|
||||
}
|
||||
|
||||
function replaceCharactersInB64(encodedB64: string) {
|
||||
@@ -87,7 +301,7 @@ export function encodeB64(str: string) {
|
||||
* use the isCustomKeystore constant in the app lifeycle instead.
|
||||
*/
|
||||
function isCustomSecureKeystore() {
|
||||
return !isIOS() ? RNSecureKeystoreModule.deviceSupportsHardware() : false;
|
||||
return isAndroid() ? RNSecureKeystoreModule.deviceSupportsHardware() : true;
|
||||
}
|
||||
|
||||
export async function encryptJson(
|
||||
@@ -134,11 +348,11 @@ export async function decryptJson(
|
||||
if (!isHardwareKeystoreExists) {
|
||||
return decryptWithForge(encryptedData, encryptionKey);
|
||||
}
|
||||
|
||||
return await RNSecureKeystoreModule.decryptData(
|
||||
const decryptedData = await RNSecureKeystoreModule.decryptData(
|
||||
ENCRYPTION_ID,
|
||||
encryptedData,
|
||||
);
|
||||
return isIOS() ? base64.decode(decryptedData) : decryptedData;
|
||||
} catch (e) {
|
||||
console.error('error decryptJson:', e);
|
||||
|
||||
@@ -188,3 +402,42 @@ export function hmacSHA(encryptionKey: string, data: string) {
|
||||
const resultBytes = hmac.digest().getBytes().toString();
|
||||
return resultBytes;
|
||||
}
|
||||
|
||||
export async function fetchKeyPair(keyType: any) {
|
||||
try {
|
||||
const {RNSecureKeystoreModule} = NativeModules;
|
||||
if (keyType == KeyTypes.RS256 || keyType == KeyTypes.ES256) {
|
||||
if (isAndroid()) {
|
||||
const publicKey = await RNSecureKeystoreModule.retrieveKey(keyType);
|
||||
return {
|
||||
publicKey: publicKey,
|
||||
privateKey: '',
|
||||
};
|
||||
} else {
|
||||
const keyPair = await RNSecureKeystoreModule.retrieveGenericKey(
|
||||
keyType,
|
||||
);
|
||||
const publicKey = keyPair[0];
|
||||
const privateKey = keyPair[1];
|
||||
return {
|
||||
publicKey: publicKey,
|
||||
privateKey: privateKey,
|
||||
};
|
||||
}
|
||||
} else {
|
||||
const keyPair = await RNSecureKeystoreModule.retrieveGenericKey(keyType);
|
||||
const publicKey = Buffer.from(keyPair[0], 'base64');
|
||||
const privateKey = Buffer.from(keyPair[1], 'base64');
|
||||
return {
|
||||
publicKey: publicKey,
|
||||
privateKey: privateKey,
|
||||
};
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('error getting key', e);
|
||||
return {
|
||||
publicKey: '',
|
||||
privateKey: '',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
23
shared/cryptoutil/signFormatConverter.ts
Normal file
23
shared/cryptoutil/signFormatConverter.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import asn1 from 'asn1.js';
|
||||
import {Buffer} from 'buffer';
|
||||
|
||||
const convertDerToRsFormat = derSignature => {
|
||||
const ASN1Integer = asn1.define('ASN1Integer', function () {
|
||||
this.int();
|
||||
});
|
||||
|
||||
const ASN1Sequence = asn1.define('ASN1Sequence', function () {
|
||||
this.seq().obj(this.key('r').int(), this.key('s').int());
|
||||
});
|
||||
|
||||
const derBuffer = Buffer.from(derSignature);
|
||||
const seq = ASN1Sequence.decode(derBuffer, 'der');
|
||||
const r = seq.r.toArrayLike(Buffer, 'be', 32);
|
||||
const s = seq.s.toArrayLike(Buffer, 'be', 32);
|
||||
|
||||
const rsBuffer = Buffer.concat([r, s]);
|
||||
|
||||
return rsBuffer.toString('base64');
|
||||
};
|
||||
|
||||
export default convertDerToRsFormat;
|
||||
@@ -1,14 +1,5 @@
|
||||
import RNSecureKeyStore, { ACCESSIBLE } from 'react-native-secure-key-store';
|
||||
|
||||
const bindingCertificate = '-bindingCertificate';
|
||||
|
||||
export async function savePrivateKey(id: string, privateKey: string) {
|
||||
var result = await RNSecureKeyStore.set(id, privateKey, {
|
||||
accessible: ACCESSIBLE.ALWAYS_THIS_DEVICE_ONLY,
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function getPrivateKey(id: string) {
|
||||
var result = await RNSecureKeyStore.get(id);
|
||||
return result;
|
||||
|
||||
@@ -4,7 +4,7 @@ import {isIOS} from '../constants';
|
||||
import pem2jwk from 'simple-pem2jwk';
|
||||
import {displayType, issuerType} from '../../machines/Issuers/IssuersMachine';
|
||||
import getAllConfigurations, {CACHED_API} from '../api';
|
||||
|
||||
import base64url from 'base64url';
|
||||
import i18next from 'i18next';
|
||||
import {getJWT} from '../cryptoutil/cryptoUtil';
|
||||
import i18n from '../../i18n';
|
||||
@@ -23,7 +23,8 @@ import {getVerifiableCredential} from '../../machines/VerifiableCredential/VCIte
|
||||
import {vcVerificationBannerDetails} from '../../components/BannerNotificationContainer';
|
||||
import {getErrorEventData, sendErrorEvent} from '../telemetry/TelemetryUtils';
|
||||
import {TelemetryConstants} from '../telemetry/TelemetryConstants';
|
||||
|
||||
import {NativeModules} from 'react-native';
|
||||
import {KeyTypes} from '../cryptoutil/KeyTypes';
|
||||
export const Protocols = {
|
||||
OpenId4VCI: 'OpenId4VCI',
|
||||
OTP: 'OTP',
|
||||
@@ -160,31 +161,6 @@ export const constructAuthorizationConfiguration = (
|
||||
};
|
||||
};
|
||||
|
||||
export const getJWK = async publicKey => {
|
||||
try {
|
||||
let publicKeyJWKString;
|
||||
let publicKeyJWK;
|
||||
if (isIOS()) {
|
||||
publicKeyJWKString = await jose.JWK.asKey(publicKey, 'pem');
|
||||
publicKeyJWK = publicKeyJWKString.toJSON();
|
||||
} else {
|
||||
publicKeyJWK = await pem2jwk(publicKey);
|
||||
}
|
||||
return {
|
||||
...publicKeyJWK,
|
||||
alg: 'RS256',
|
||||
use: 'sig',
|
||||
};
|
||||
} catch (e) {
|
||||
console.error(
|
||||
'Exception occurred while constructing JWK from PEM : ' +
|
||||
publicKey +
|
||||
' Exception is ',
|
||||
e,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const getSelectedCredentialTypeDetails = (
|
||||
wellknown: any,
|
||||
vcCredentialTypes: Object[],
|
||||
@@ -357,10 +333,11 @@ export async function constructProofJWT(
|
||||
privateKey: string,
|
||||
accessToken: string,
|
||||
selectedIssuer: issuerType,
|
||||
keyType: string,
|
||||
): Promise<string> {
|
||||
const jwtHeader = {
|
||||
alg: 'RS256',
|
||||
jwk: await getJWK(publicKey),
|
||||
alg: keyType,
|
||||
jwk: await getJWK(publicKey, keyType),
|
||||
typ: 'openid4vci-proof+jwt',
|
||||
};
|
||||
const decodedToken = jwtDecode(accessToken);
|
||||
@@ -372,7 +349,90 @@ export async function constructProofJWT(
|
||||
exp: Math.floor(new Date().getTime() / 1000) + 18000,
|
||||
};
|
||||
|
||||
return await getJWT(jwtHeader, jwtPayload, Issuers_Key_Ref, privateKey);
|
||||
return await getJWT(
|
||||
jwtHeader,
|
||||
jwtPayload,
|
||||
Issuers_Key_Ref,
|
||||
privateKey,
|
||||
keyType,
|
||||
);
|
||||
}
|
||||
|
||||
export const getJWK = async (publicKey, keyType) => {
|
||||
try {
|
||||
let publicKeyJWK;
|
||||
switch (keyType) {
|
||||
case KeyTypes.RS256:
|
||||
publicKeyJWK = await getJWKRSA(publicKey);
|
||||
break;
|
||||
case KeyTypes.ES256:
|
||||
publicKeyJWK = await getJWKECR1(publicKey);
|
||||
break;
|
||||
case KeyTypes.ES256K:
|
||||
publicKeyJWK = await getJWKECK1(publicKey);
|
||||
break;
|
||||
case KeyTypes.ED25519:
|
||||
publicKeyJWK = await getJWKED(publicKey);
|
||||
break;
|
||||
default:
|
||||
throw Error;
|
||||
}
|
||||
return {
|
||||
...publicKeyJWK,
|
||||
alg: keyType,
|
||||
use: 'sig',
|
||||
};
|
||||
} catch (e) {
|
||||
console.error(
|
||||
'Exception occurred while constructing JWK from PEM : ' +
|
||||
publicKey +
|
||||
' Exception is ',
|
||||
e,
|
||||
);
|
||||
}
|
||||
};
|
||||
async function getJWKRSA(publicKey): Promise<any> {
|
||||
const publicKeyJWKString = await jose.JWK.asKey(publicKey, 'pem');
|
||||
return publicKeyJWKString.toJSON();
|
||||
}
|
||||
function getJWKECR1(publicKey): any {
|
||||
return JSON.parse(publicKey);
|
||||
}
|
||||
function getJWKECK1(publicKey): any {
|
||||
const x = base64url(Buffer.from(publicKey.slice(1, 33))); // Skip the first byte (0x04) in the uncompressed public key
|
||||
const y = base64url(Buffer.from(publicKey.slice(33)));
|
||||
const jwk = {
|
||||
kty: 'EC',
|
||||
crv: 'secp256k1',
|
||||
x: x,
|
||||
y: y,
|
||||
};
|
||||
return jwk;
|
||||
}
|
||||
function getJWKED(publicKey): any {
|
||||
throw new Error('Function not implemented.');
|
||||
}
|
||||
export async function hasKeyPair(keyType: any): Promise<boolean> {
|
||||
const {RNSecureKeystoreModule} = NativeModules;
|
||||
try {
|
||||
return await RNSecureKeystoreModule.hasAlias(keyType);
|
||||
} catch (e) {
|
||||
console.warn('key not found');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function selectCredentialRequestKey(keyTypes: string[]) {
|
||||
const availableKeys = [
|
||||
KeyTypes.RS256,
|
||||
KeyTypes.ES256K,
|
||||
KeyTypes.ES256,
|
||||
KeyTypes.ED25519,
|
||||
];
|
||||
for (const key of availableKeys) {
|
||||
if (keyTypes.includes(key)) return key;
|
||||
}
|
||||
throw Error;
|
||||
}
|
||||
|
||||
export const constructIssuerMetaData = (
|
||||
|
||||
Reference in New Issue
Block a user