@trustchex/react-native-sdk 1.381.0 → 1.464.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/src/main/java/com/trustchex/reactnativesdk/TrustchexSDKModule.kt +2 -8
- package/android/src/main/java/com/trustchex/reactnativesdk/camera/TrustchexCameraView.kt +60 -13
- package/android/src/main/java/com/trustchex/reactnativesdk/mlkit/MLKitModule.kt +1 -1
- package/ios/Camera/TrustchexCameraView.swift +10 -13
- package/ios/MLKit/MLKitModule.swift +1 -1
- package/lib/module/Screens/Debug/BarcodeTestScreen.js +308 -0
- package/lib/module/Screens/Debug/MRZTestScreen.js +105 -13
- package/lib/module/Screens/Debug/NFCScanTestScreen.js +635 -0
- package/lib/module/Screens/Dynamic/ContractAcceptanceScreen.js +49 -32
- package/lib/module/Screens/Dynamic/IdentityDocumentEIDScanningScreen.js +22 -4
- package/lib/module/Screens/Dynamic/IdentityDocumentScanningScreen.js +5 -0
- package/lib/module/Screens/Dynamic/LivenessDetectionScreen.js +126 -27
- package/lib/module/Screens/Dynamic/VerbalConsentScreen.js +1079 -0
- package/lib/module/Screens/Dynamic/VideoCallScreen.js +678 -0
- package/lib/module/Screens/Static/OTPVerificationScreen.js +6 -0
- package/lib/module/Screens/Static/QrCodeScanningScreen.js +7 -1
- package/lib/module/Screens/Static/ResultScreen.js +154 -34
- package/lib/module/Screens/Static/VerificationSessionCheckScreen.js +59 -51
- package/lib/module/Shared/Animations/recording.json +1 -0
- package/lib/module/Shared/Animations/video-call.json +1 -0
- package/lib/module/Shared/Components/DebugNavigationPanel.js +231 -67
- package/lib/module/Shared/Components/EIDScanner.js +213 -112
- package/lib/module/Shared/Components/IdentityDocumentCamera.flows.js +5 -3
- package/lib/module/Shared/Components/IdentityDocumentCamera.js +77 -39
- package/lib/module/Shared/Components/IdentityDocumentCamera.utils.js +13 -4
- package/lib/module/Shared/Components/NavigationManager.js +39 -19
- package/lib/module/Shared/Contexts/AppContext.js +1 -0
- package/lib/module/Shared/EIDReader/aesSecureMessagingWrapper.js +51 -0
- package/lib/module/Shared/EIDReader/apduLevelPACECapable.js +3 -0
- package/lib/module/Shared/EIDReader/bacKey.js +16 -2
- package/lib/module/Shared/EIDReader/eidReader.js +354 -13
- package/lib/module/Shared/EIDReader/eidService.js +25 -1
- package/lib/module/Shared/EIDReader/nfcManagerCardService.js +4 -7
- package/lib/module/Shared/EIDReader/paceInfo.js +85 -0
- package/lib/module/Shared/EIDReader/paceKeySpec.js +51 -0
- package/lib/module/Shared/EIDReader/protocol/paceAPDUSender.js +100 -0
- package/lib/module/Shared/EIDReader/protocol/paceProtocol.js +655 -0
- package/lib/module/Shared/EIDReader/protocol/paceResult.js +37 -0
- package/lib/module/Shared/EIDReader/secureMessagingWrapper.js +27 -4
- package/lib/module/Shared/EIDReader/smartcards/commandAPDU.js +2 -1
- package/lib/module/Shared/EIDReader/tlv/tlv.helpers.js +1 -1
- package/lib/module/Shared/EIDReader/tlv/tlv.utils.js +6 -3
- package/lib/module/Shared/EIDReader/utils/aesCrypto.utils.js +189 -0
- package/lib/module/Shared/Libs/SignalingClient.js +128 -0
- package/lib/module/Shared/Libs/analytics.utils.js +8 -0
- package/lib/module/Shared/Libs/contains.js +1 -40
- package/lib/module/Shared/Libs/country-display.utils.js +34 -0
- package/lib/module/Shared/Libs/deeplink.utils.js +9 -1
- package/lib/module/Shared/Libs/demo.utils.js +8 -0
- package/lib/module/Shared/Libs/http-client.js +9 -0
- package/lib/module/Shared/Libs/mrz.utils.js +3 -2
- package/lib/module/Shared/Libs/promise.utils.js +16 -2
- package/lib/module/Shared/Libs/status-bar.utils.js +23 -0
- package/lib/module/Shared/Services/DataUploadService.js +294 -0
- package/lib/module/Shared/Services/VideoSessionService.js +156 -0
- package/lib/module/Shared/Services/WebRTCService.js +510 -0
- package/lib/module/Shared/Types/analytics.types.js +4 -0
- package/lib/module/Translation/Resources/en.js +61 -2
- package/lib/module/Translation/Resources/tr.js +61 -2
- package/lib/module/Trustchex.js +64 -20
- package/lib/module/version.js +1 -1
- package/lib/typescript/src/Screens/Debug/BarcodeTestScreen.d.ts +3 -0
- package/lib/typescript/src/Screens/Debug/BarcodeTestScreen.d.ts.map +1 -0
- package/lib/typescript/src/Screens/Debug/MRZTestScreen.d.ts.map +1 -1
- package/lib/typescript/src/Screens/Debug/NFCScanTestScreen.d.ts +3 -0
- package/lib/typescript/src/Screens/Debug/NFCScanTestScreen.d.ts.map +1 -0
- package/lib/typescript/src/Screens/Dynamic/ContractAcceptanceScreen.d.ts.map +1 -1
- package/lib/typescript/src/Screens/Dynamic/IdentityDocumentEIDScanningScreen.d.ts.map +1 -1
- package/lib/typescript/src/Screens/Dynamic/IdentityDocumentScanningScreen.d.ts.map +1 -1
- package/lib/typescript/src/Screens/Dynamic/LivenessDetectionScreen.d.ts.map +1 -1
- package/lib/typescript/src/Screens/Dynamic/VerbalConsentScreen.d.ts +3 -0
- package/lib/typescript/src/Screens/Dynamic/VerbalConsentScreen.d.ts.map +1 -0
- package/lib/typescript/src/Screens/Dynamic/VideoCallScreen.d.ts +3 -0
- package/lib/typescript/src/Screens/Dynamic/VideoCallScreen.d.ts.map +1 -0
- package/lib/typescript/src/Screens/Static/OTPVerificationScreen.d.ts.map +1 -1
- package/lib/typescript/src/Screens/Static/QrCodeScanningScreen.d.ts.map +1 -1
- package/lib/typescript/src/Screens/Static/ResultScreen.d.ts.map +1 -1
- package/lib/typescript/src/Screens/Static/VerificationSessionCheckScreen.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Components/DebugNavigationPanel.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Components/EIDScanner.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.flows.d.ts +1 -1
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.flows.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.utils.d.ts +5 -0
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.utils.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Components/NavigationManager.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Contexts/AppContext.d.ts +1 -0
- package/lib/typescript/src/Shared/Contexts/AppContext.d.ts.map +1 -1
- package/lib/typescript/src/Shared/EIDReader/aesSecureMessagingWrapper.d.ts +18 -0
- package/lib/typescript/src/Shared/EIDReader/aesSecureMessagingWrapper.d.ts.map +1 -0
- package/lib/typescript/src/Shared/EIDReader/apduLevelPACECapable.d.ts +23 -0
- package/lib/typescript/src/Shared/EIDReader/apduLevelPACECapable.d.ts.map +1 -0
- package/lib/typescript/src/Shared/EIDReader/bacKey.d.ts +6 -0
- package/lib/typescript/src/Shared/EIDReader/bacKey.d.ts.map +1 -1
- package/lib/typescript/src/Shared/EIDReader/eidReader.d.ts.map +1 -1
- package/lib/typescript/src/Shared/EIDReader/eidService.d.ts +9 -0
- package/lib/typescript/src/Shared/EIDReader/eidService.d.ts.map +1 -1
- package/lib/typescript/src/Shared/EIDReader/nfcManagerCardService.d.ts.map +1 -1
- package/lib/typescript/src/Shared/EIDReader/paceInfo.d.ts +50 -0
- package/lib/typescript/src/Shared/EIDReader/paceInfo.d.ts.map +1 -0
- package/lib/typescript/src/Shared/EIDReader/paceKeySpec.d.ts +30 -0
- package/lib/typescript/src/Shared/EIDReader/paceKeySpec.d.ts.map +1 -0
- package/lib/typescript/src/Shared/EIDReader/protocol/paceAPDUSender.d.ts +17 -0
- package/lib/typescript/src/Shared/EIDReader/protocol/paceAPDUSender.d.ts.map +1 -0
- package/lib/typescript/src/Shared/EIDReader/protocol/paceProtocol.d.ts +105 -0
- package/lib/typescript/src/Shared/EIDReader/protocol/paceProtocol.d.ts.map +1 -0
- package/lib/typescript/src/Shared/EIDReader/protocol/paceResult.d.ts +24 -0
- package/lib/typescript/src/Shared/EIDReader/protocol/paceResult.d.ts.map +1 -0
- package/lib/typescript/src/Shared/EIDReader/secureMessagingWrapper.d.ts +15 -0
- package/lib/typescript/src/Shared/EIDReader/secureMessagingWrapper.d.ts.map +1 -1
- package/lib/typescript/src/Shared/EIDReader/smartcards/commandAPDU.d.ts.map +1 -1
- package/lib/typescript/src/Shared/EIDReader/tlv/tlv.utils.d.ts.map +1 -1
- package/lib/typescript/src/Shared/EIDReader/utils/aesCrypto.utils.d.ts +39 -0
- package/lib/typescript/src/Shared/EIDReader/utils/aesCrypto.utils.d.ts.map +1 -0
- package/lib/typescript/src/Shared/Libs/SignalingClient.d.ts +24 -0
- package/lib/typescript/src/Shared/Libs/SignalingClient.d.ts.map +1 -0
- package/lib/typescript/src/Shared/Libs/analytics.utils.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Libs/contains.d.ts +0 -7
- package/lib/typescript/src/Shared/Libs/contains.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Libs/country-display.utils.d.ts +2 -0
- package/lib/typescript/src/Shared/Libs/country-display.utils.d.ts.map +1 -0
- package/lib/typescript/src/Shared/Libs/deeplink.utils.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Libs/demo.utils.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Libs/http-client.d.ts +1 -1
- package/lib/typescript/src/Shared/Libs/http-client.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Libs/mrz.utils.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Libs/promise.utils.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Libs/status-bar.utils.d.ts +9 -0
- package/lib/typescript/src/Shared/Libs/status-bar.utils.d.ts.map +1 -0
- package/lib/typescript/src/Shared/Services/DataUploadService.d.ts +25 -0
- package/lib/typescript/src/Shared/Services/DataUploadService.d.ts.map +1 -0
- package/lib/typescript/src/Shared/Services/VideoSessionService.d.ts +33 -0
- package/lib/typescript/src/Shared/Services/VideoSessionService.d.ts.map +1 -0
- package/lib/typescript/src/Shared/Services/WebRTCService.d.ts +58 -0
- package/lib/typescript/src/Shared/Services/WebRTCService.d.ts.map +1 -0
- package/lib/typescript/src/Shared/Types/analytics.types.d.ts +4 -0
- package/lib/typescript/src/Shared/Types/analytics.types.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Types/identificationInfo.d.ts +13 -1
- package/lib/typescript/src/Shared/Types/identificationInfo.d.ts.map +1 -1
- package/lib/typescript/src/Translation/Resources/en.d.ts +60 -1
- package/lib/typescript/src/Translation/Resources/en.d.ts.map +1 -1
- package/lib/typescript/src/Translation/Resources/tr.d.ts +60 -1
- package/lib/typescript/src/Translation/Resources/tr.d.ts.map +1 -1
- package/lib/typescript/src/Trustchex.d.ts.map +1 -1
- package/lib/typescript/src/version.d.ts +1 -1
- package/package.json +35 -5
- package/src/Screens/Debug/BarcodeTestScreen.tsx +317 -0
- package/src/Screens/Debug/MRZTestScreen.tsx +107 -13
- package/src/Screens/Debug/NFCScanTestScreen.tsx +692 -0
- package/src/Screens/Dynamic/ContractAcceptanceScreen.tsx +58 -35
- package/src/Screens/Dynamic/IdentityDocumentEIDScanningScreen.tsx +27 -4
- package/src/Screens/Dynamic/IdentityDocumentScanningScreen.tsx +6 -0
- package/src/Screens/Dynamic/LivenessDetectionScreen.tsx +156 -27
- package/src/Screens/Dynamic/VerbalConsentScreen.tsx +1401 -0
- package/src/Screens/Dynamic/VideoCallScreen.tsx +766 -0
- package/src/Screens/Static/OTPVerificationScreen.tsx +6 -0
- package/src/Screens/Static/QrCodeScanningScreen.tsx +7 -1
- package/src/Screens/Static/ResultScreen.tsx +235 -48
- package/src/Screens/Static/VerificationSessionCheckScreen.tsx +67 -72
- package/src/Shared/Animations/recording.json +1 -0
- package/src/Shared/Animations/video-call.json +1 -0
- package/src/Shared/Components/DebugNavigationPanel.tsx +252 -51
- package/src/Shared/Components/EIDScanner.tsx +223 -116
- package/src/Shared/Components/IdentityDocumentCamera.flows.ts +7 -4
- package/src/Shared/Components/IdentityDocumentCamera.tsx +224 -188
- package/src/Shared/Components/IdentityDocumentCamera.utils.ts +13 -4
- package/src/Shared/Components/NavigationManager.tsx +41 -19
- package/src/Shared/Contexts/AppContext.ts +2 -0
- package/src/Shared/EIDReader/aesSecureMessagingWrapper.ts +69 -0
- package/src/Shared/EIDReader/apduLevelPACECapable.ts +34 -0
- package/src/Shared/EIDReader/bacKey.ts +24 -8
- package/src/Shared/EIDReader/eidReader.ts +398 -12
- package/src/Shared/EIDReader/eidService.ts +49 -1
- package/src/Shared/EIDReader/nfcManagerCardService.ts +4 -6
- package/src/Shared/EIDReader/paceInfo.ts +159 -0
- package/src/Shared/EIDReader/paceKeySpec.ts +56 -0
- package/src/Shared/EIDReader/protocol/paceAPDUSender.ts +163 -0
- package/src/Shared/EIDReader/protocol/paceProtocol.ts +946 -0
- package/src/Shared/EIDReader/protocol/paceResult.ts +62 -0
- package/src/Shared/EIDReader/secureMessagingWrapper.ts +28 -10
- package/src/Shared/EIDReader/smartcards/commandAPDU.ts +2 -1
- package/src/Shared/EIDReader/tlv/tlv.helpers.ts +1 -1
- package/src/Shared/EIDReader/tlv/tlv.utils.ts +8 -5
- package/src/Shared/EIDReader/utils/aesCrypto.utils.ts +217 -0
- package/src/Shared/Libs/SignalingClient.ts +189 -0
- package/src/Shared/Libs/analytics.utils.ts +8 -0
- package/src/Shared/Libs/contains.ts +0 -53
- package/src/Shared/Libs/country-display.utils.ts +55 -0
- package/src/Shared/Libs/crypto.utils.ts +2 -2
- package/src/Shared/Libs/deeplink.utils.ts +12 -1
- package/src/Shared/Libs/demo.utils.ts +10 -0
- package/src/Shared/Libs/http-client.ts +19 -1
- package/src/Shared/Libs/mrz.utils.ts +3 -2
- package/src/Shared/Libs/promise.utils.ts +16 -2
- package/src/Shared/Libs/status-bar.utils.ts +21 -0
- package/src/Shared/Services/DataUploadService.ts +395 -0
- package/src/Shared/Services/VideoSessionService.ts +190 -0
- package/src/Shared/Services/WebRTCService.ts +636 -0
- package/src/Shared/Types/analytics.types.ts +4 -0
- package/src/Shared/Types/identificationInfo.ts +16 -1
- package/src/Translation/Resources/en.ts +88 -3
- package/src/Translation/Resources/tr.ts +89 -3
- package/src/Trustchex.tsx +65 -19
- package/src/version.ts +1 -1
|
@@ -56,7 +56,7 @@ export class SecureMessagingWrapper {
|
|
|
56
56
|
const dataOutputStream = Array.from(this.getEncodedSendSequenceCounter());
|
|
57
57
|
const paddedData = cryptoUtils.pad(Buffer.from(rapdu), 0, rapdu.length - 2 - 8 - 2, this.getPadLength());
|
|
58
58
|
dataOutputStream.push(...paddedData);
|
|
59
|
-
let cc2 = Uint8Array.from(Buffer.from(
|
|
59
|
+
let cc2 = Uint8Array.from(Buffer.from(this.computeMAC(Buffer.from(dataOutputStream).toString('hex'), this.ksMac), 'hex'));
|
|
60
60
|
if (cc2.length > 8 && cc.length === 8) {
|
|
61
61
|
const newCC2 = new Uint8Array(8);
|
|
62
62
|
for (let i = 0; i < 8; i++) {
|
|
@@ -70,6 +70,29 @@ export class SecureMessagingWrapper {
|
|
|
70
70
|
return false;
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
|
+
/**
|
|
74
|
+
* Encrypt data using the session encryption key.
|
|
75
|
+
* Subclasses override for AES vs 3DES.
|
|
76
|
+
*/
|
|
77
|
+
encrypt(dataHex, keyHex) {
|
|
78
|
+
return cryptoUtils.encryptWith3DES(dataHex, keyHex);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Decrypt data using the session encryption key.
|
|
83
|
+
* Subclasses override for AES vs 3DES.
|
|
84
|
+
*/
|
|
85
|
+
decrypt(dataHex, keyHex) {
|
|
86
|
+
return cryptoUtils.decryptWith3DES(dataHex, keyHex);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Compute MAC over data using the session MAC key.
|
|
91
|
+
* Subclasses override for AES-CMAC vs 3DES MAC.
|
|
92
|
+
*/
|
|
93
|
+
computeMAC(dataHex, keyHex) {
|
|
94
|
+
return cryptoUtils.computeMac(dataHex, keyHex);
|
|
95
|
+
}
|
|
73
96
|
wrapCommandAPDU(commandAPDU) {
|
|
74
97
|
const cla = commandAPDU.getCLA();
|
|
75
98
|
const ins = commandAPDU.getINS();
|
|
@@ -88,7 +111,7 @@ export class SecureMessagingWrapper {
|
|
|
88
111
|
}
|
|
89
112
|
if (lc > 0) {
|
|
90
113
|
const data = cryptoUtils.pad(Buffer.from(commandAPDU.getData()), 0, commandAPDU.getData().length, this.getPadLength());
|
|
91
|
-
const ciphertext = Buffer.from(
|
|
114
|
+
const ciphertext = Buffer.from(this.encrypt(Buffer.from(data).toString('hex'), this.ksEnc), 'hex');
|
|
92
115
|
output.push(hasDO85 ? 0x85 : 0x87);
|
|
93
116
|
output.push(...TLVUtil.getLengthAsBytes(ciphertext.length + (hasDO85 ? 0 : 1)));
|
|
94
117
|
if (!hasDO85) {
|
|
@@ -99,7 +122,7 @@ export class SecureMessagingWrapper {
|
|
|
99
122
|
}
|
|
100
123
|
output = [...this.getEncodedSendSequenceCounter(), ...paddedMaskedHeader, ...do8587, ...do97];
|
|
101
124
|
const n = cryptoUtils.pad(Buffer.from(output), 0, output.length, this.getPadLength());
|
|
102
|
-
const cc = Buffer.from(
|
|
125
|
+
const cc = Buffer.from(this.computeMAC(Buffer.from(n).toString('hex'), this.ksMac), 'hex');
|
|
103
126
|
let ccLength = cc.length;
|
|
104
127
|
if (ccLength !== 8) {
|
|
105
128
|
ccLength = 8;
|
|
@@ -194,7 +217,7 @@ export class SecureMessagingWrapper {
|
|
|
194
217
|
}
|
|
195
218
|
const ciphertext = Buffer.alloc(length);
|
|
196
219
|
inputStream.readFully(ciphertext);
|
|
197
|
-
const paddedData = Array.from(Buffer.from(
|
|
220
|
+
const paddedData = Array.from(Buffer.from(this.decrypt(Buffer.from(ciphertext).toString('hex'), this.ksEnc), 'hex'));
|
|
198
221
|
return cryptoUtils.unpad(paddedData);
|
|
199
222
|
}
|
|
200
223
|
async readDO99(inputStream) {
|
|
@@ -8,7 +8,8 @@ export class CommandAPDU {
|
|
|
8
8
|
dataOffset = 0;
|
|
9
9
|
constructor(cla, ins, p1, p2, data, dataOffset, dataLength, ne) {
|
|
10
10
|
this.nc = dataLength || 0;
|
|
11
|
-
|
|
11
|
+
// Java JMRTD uses -1 to mean "no Le field" (ne=0). Guard against negatives.
|
|
12
|
+
this.ne = ne == null || ne < 0 ? 0 : ne;
|
|
12
13
|
this.apdu = this.buildAPDU(cla, ins, p1, p2, data, dataOffset || 0, this.nc, this.ne);
|
|
13
14
|
}
|
|
14
15
|
buildAPDU(cla, ins, p1, p2, data, dataOffset = 0, dataLength = 0, ne = 0) {
|
|
@@ -39,7 +39,7 @@ class TLVUtil extends TLVHelpers {
|
|
|
39
39
|
try {
|
|
40
40
|
const actualTag = await tlvInputStream.readTag();
|
|
41
41
|
if (actualTag !== expectedTag) {
|
|
42
|
-
throw new Error(`Expected tag ${expectedTag.toString(16)}, found
|
|
42
|
+
throw new Error(`Expected TLV tag 0x${expectedTag.toString(16)}, found 0x${actualTag.toString(16)} in data: ${Buffer.from(wrappedData.slice(0, 16)).toString('hex')}`);
|
|
43
43
|
}
|
|
44
44
|
const length = await tlvInputStream.readLength();
|
|
45
45
|
const value = await tlvInputStream.readValue();
|
|
@@ -49,8 +49,11 @@ class TLVUtil extends TLVHelpers {
|
|
|
49
49
|
}
|
|
50
50
|
return result;
|
|
51
51
|
} catch (ioe) {
|
|
52
|
-
//
|
|
53
|
-
|
|
52
|
+
// Re-throw original error with context rather than masking it
|
|
53
|
+
if (ioe instanceof Error && ioe.message.includes('Expected TLV tag')) {
|
|
54
|
+
throw ioe;
|
|
55
|
+
}
|
|
56
|
+
throw new Error(`TLV unwrap error (expected tag 0x${expectedTag.toString(16)}): ${ioe instanceof Error ? ioe.message : String(ioe)}`);
|
|
54
57
|
} finally {
|
|
55
58
|
try {
|
|
56
59
|
tlvInputStream.close();
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import CryptoJS from 'crypto-js';
|
|
4
|
+
import { Buffer } from 'buffer';
|
|
5
|
+
import 'react-native-get-random-values';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* AES cryptographic utilities for PACE protocol.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Encrypt data using AES in CBC mode with zero IV.
|
|
13
|
+
*/
|
|
14
|
+
export const aesEncryptCBC = (dataHex, keyHex, ivHex = '00000000000000000000000000000000') => {
|
|
15
|
+
const data = CryptoJS.enc.Hex.parse(dataHex);
|
|
16
|
+
const key = CryptoJS.enc.Hex.parse(keyHex);
|
|
17
|
+
const iv = CryptoJS.enc.Hex.parse(ivHex);
|
|
18
|
+
const encrypted = CryptoJS.AES.encrypt(data, key, {
|
|
19
|
+
mode: CryptoJS.mode.CBC,
|
|
20
|
+
padding: CryptoJS.pad.NoPadding,
|
|
21
|
+
iv
|
|
22
|
+
});
|
|
23
|
+
return encrypted.ciphertext.toString(CryptoJS.enc.Hex);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Decrypt data using AES in CBC mode with zero IV.
|
|
28
|
+
*/
|
|
29
|
+
export const aesDecryptCBC = (dataHex, keyHex, ivHex = '00000000000000000000000000000000') => {
|
|
30
|
+
const data = CryptoJS.enc.Hex.parse(dataHex);
|
|
31
|
+
const key = CryptoJS.enc.Hex.parse(keyHex);
|
|
32
|
+
const iv = CryptoJS.enc.Hex.parse(ivHex);
|
|
33
|
+
const cipherParams = CryptoJS.lib.CipherParams.create({
|
|
34
|
+
ciphertext: data
|
|
35
|
+
});
|
|
36
|
+
const decrypted = CryptoJS.AES.decrypt(cipherParams, key, {
|
|
37
|
+
mode: CryptoJS.mode.CBC,
|
|
38
|
+
padding: CryptoJS.pad.NoPadding,
|
|
39
|
+
iv
|
|
40
|
+
});
|
|
41
|
+
return decrypted.toString(CryptoJS.enc.Hex);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Encrypt a single block with AES in ECB mode (for SSC encryption).
|
|
46
|
+
*/
|
|
47
|
+
export const aesEncryptECB = (dataHex, keyHex) => {
|
|
48
|
+
const data = CryptoJS.enc.Hex.parse(dataHex);
|
|
49
|
+
const key = CryptoJS.enc.Hex.parse(keyHex);
|
|
50
|
+
const encrypted = CryptoJS.AES.encrypt(data, key, {
|
|
51
|
+
mode: CryptoJS.mode.ECB,
|
|
52
|
+
padding: CryptoJS.pad.NoPadding
|
|
53
|
+
});
|
|
54
|
+
return encrypted.ciphertext.toString(CryptoJS.enc.Hex);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Compute AES CMAC (OMAC1) as defined in RFC 4493.
|
|
59
|
+
*/
|
|
60
|
+
export const aesCMAC = (dataHex, keyHex) => {
|
|
61
|
+
const key = Buffer.from(keyHex, 'hex');
|
|
62
|
+
const data = Buffer.from(dataHex, 'hex');
|
|
63
|
+
const blockSize = 16;
|
|
64
|
+
|
|
65
|
+
// Step 1: Generate subkeys
|
|
66
|
+
const zeroBlock = '00000000000000000000000000000000';
|
|
67
|
+
const L = Buffer.from(aesEncryptECB(zeroBlock, keyHex), 'hex');
|
|
68
|
+
const K1 = generateSubkey(L);
|
|
69
|
+
const K2 = generateSubkey(K1);
|
|
70
|
+
|
|
71
|
+
// Step 2: Determine if we need to pad
|
|
72
|
+
const n = data.length === 0 ? 1 : Math.ceil(data.length / blockSize);
|
|
73
|
+
const lastBlockComplete = data.length > 0 && data.length % blockSize === 0;
|
|
74
|
+
|
|
75
|
+
// Step 3: Process last block
|
|
76
|
+
const lastBlock = Buffer.alloc(blockSize);
|
|
77
|
+
if (lastBlockComplete) {
|
|
78
|
+
const start = (n - 1) * blockSize;
|
|
79
|
+
for (let i = 0; i < blockSize; i++) {
|
|
80
|
+
lastBlock[i] = data[start + i] ^ K1[i];
|
|
81
|
+
}
|
|
82
|
+
} else {
|
|
83
|
+
// Pad with 10*
|
|
84
|
+
const start = (n - 1) * blockSize;
|
|
85
|
+
const remaining = data.length - start;
|
|
86
|
+
const padded = Buffer.alloc(blockSize);
|
|
87
|
+
for (let i = 0; i < remaining; i++) {
|
|
88
|
+
padded[i] = data[start + i];
|
|
89
|
+
}
|
|
90
|
+
padded[remaining] = 0x80;
|
|
91
|
+
for (let i = 0; i < blockSize; i++) {
|
|
92
|
+
lastBlock[i] = padded[i] ^ K2[i];
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Step 4: CBC-MAC
|
|
97
|
+
let X = Buffer.alloc(blockSize);
|
|
98
|
+
for (let i = 0; i < n - 1; i++) {
|
|
99
|
+
const block = data.subarray(i * blockSize, (i + 1) * blockSize);
|
|
100
|
+
const Y = Buffer.alloc(blockSize);
|
|
101
|
+
for (let j = 0; j < blockSize; j++) {
|
|
102
|
+
Y[j] = X[j] ^ block[j];
|
|
103
|
+
}
|
|
104
|
+
X = Buffer.from(aesEncryptECB(Y.toString('hex'), keyHex), 'hex');
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Process last block
|
|
108
|
+
const Y = Buffer.alloc(blockSize);
|
|
109
|
+
for (let j = 0; j < blockSize; j++) {
|
|
110
|
+
Y[j] = X[j] ^ lastBlock[j];
|
|
111
|
+
}
|
|
112
|
+
const T = aesEncryptECB(Y.toString('hex'), keyHex);
|
|
113
|
+
return T;
|
|
114
|
+
};
|
|
115
|
+
function generateSubkey(input) {
|
|
116
|
+
const blockSize = 16;
|
|
117
|
+
const Rb = Buffer.alloc(blockSize);
|
|
118
|
+
Rb[blockSize - 1] = 0x87;
|
|
119
|
+
|
|
120
|
+
// Left shift by 1
|
|
121
|
+
const output = Buffer.alloc(blockSize);
|
|
122
|
+
const msb = (input[0] & 0x80) !== 0;
|
|
123
|
+
for (let i = 0; i < blockSize - 1; i++) {
|
|
124
|
+
output[i] = (input[i] << 1 | input[i + 1] >> 7) & 0xff;
|
|
125
|
+
}
|
|
126
|
+
output[blockSize - 1] = input[blockSize - 1] << 1 & 0xff;
|
|
127
|
+
if (msb) {
|
|
128
|
+
for (let i = 0; i < blockSize; i++) {
|
|
129
|
+
output[i] ^= Rb[i];
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return output;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Derive AES key from shared secret using KDF as per TR-SAC.
|
|
137
|
+
*
|
|
138
|
+
* @param sharedSecretHex the shared secret bytes as hex
|
|
139
|
+
* @param cipherAlg 'AES' or 'DESede'
|
|
140
|
+
* @param keyLength 128, 192, or 256
|
|
141
|
+
* @param mode 1 = ENC, 2 = MAC, 3 = PACE
|
|
142
|
+
*/
|
|
143
|
+
export const deriveKey = (sharedSecretHex, cipherAlg, keyLength, mode) => {
|
|
144
|
+
const input = sharedSecretHex + '000000' + mode.toString(16).padStart(2, '0');
|
|
145
|
+
let hash;
|
|
146
|
+
if (cipherAlg === 'AES' && keyLength > 128) {
|
|
147
|
+
// Use SHA-256 for AES-192 and AES-256
|
|
148
|
+
hash = CryptoJS.SHA256(CryptoJS.enc.Hex.parse(input)).toString(CryptoJS.enc.Hex);
|
|
149
|
+
} else {
|
|
150
|
+
// Use SHA-1 for DESede and AES-128
|
|
151
|
+
hash = CryptoJS.SHA1(CryptoJS.enc.Hex.parse(input)).toString(CryptoJS.enc.Hex);
|
|
152
|
+
}
|
|
153
|
+
if (cipherAlg === 'DESede') {
|
|
154
|
+
// 3DES: 24-byte key (K1 || K2 || K1)
|
|
155
|
+
const ka = hash.substring(0, 16);
|
|
156
|
+
const kb = hash.substring(16, 32);
|
|
157
|
+
return ka + kb + ka;
|
|
158
|
+
} else {
|
|
159
|
+
// AES: return first keyLength/8 bytes
|
|
160
|
+
const keyBytes = keyLength / 4; // hex chars = keyLength / 8 * 2
|
|
161
|
+
return hash.substring(0, keyBytes);
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Pad data using ISO 9797-1 method 2 with given block size.
|
|
167
|
+
*/
|
|
168
|
+
export const padISO9797 = (data, blockSize = 16) => {
|
|
169
|
+
const n = data.length;
|
|
170
|
+
const padLength = blockSize - n % blockSize;
|
|
171
|
+
const out = Buffer.alloc(n + padLength);
|
|
172
|
+
data.copy(out);
|
|
173
|
+
out[n] = 0x80;
|
|
174
|
+
return out;
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Unpad ISO 9797-1 method 2.
|
|
179
|
+
*/
|
|
180
|
+
export const unpadISO9797 = data => {
|
|
181
|
+
let i = data.length - 1;
|
|
182
|
+
while (i >= 0 && data[i] === 0x00) {
|
|
183
|
+
i--;
|
|
184
|
+
}
|
|
185
|
+
if (i >= 0 && data[i] === 0x80) {
|
|
186
|
+
return data.subarray(0, i);
|
|
187
|
+
}
|
|
188
|
+
return data;
|
|
189
|
+
};
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import EventSource from 'react-native-sse';
|
|
4
|
+
export class SignalingClient {
|
|
5
|
+
eventSource = null;
|
|
6
|
+
constructor(baseUrl, sessionId, onMessage, identificationId, onSessionEnded) {
|
|
7
|
+
this.baseUrl = baseUrl;
|
|
8
|
+
this.sessionId = sessionId;
|
|
9
|
+
this.onMessage = onMessage;
|
|
10
|
+
this.identificationId = identificationId;
|
|
11
|
+
this.onSessionEnded = onSessionEnded;
|
|
12
|
+
}
|
|
13
|
+
connect() {
|
|
14
|
+
if (this.eventSource) {
|
|
15
|
+
this.eventSource.close();
|
|
16
|
+
}
|
|
17
|
+
const urlParams = new URLSearchParams();
|
|
18
|
+
if (this.identificationId) {
|
|
19
|
+
urlParams.append('identificationId', this.identificationId);
|
|
20
|
+
}
|
|
21
|
+
const url = `${this.baseUrl}/api/app/mobile/video-sessions/${this.sessionId}/signaling/stream?${urlParams.toString()}`;
|
|
22
|
+
console.log('[SignalingClient] Connecting to SSE:', url);
|
|
23
|
+
this.eventSource = new EventSource(url, {
|
|
24
|
+
pollingInterval: 0 // 0 means default/no polling if SSE is real
|
|
25
|
+
});
|
|
26
|
+
const listener = event => {
|
|
27
|
+
if (event.type === 'open') {
|
|
28
|
+
console.log('[SignalingClient] Connected');
|
|
29
|
+
this.onConnected?.();
|
|
30
|
+
} else if (event.type === 'error') {
|
|
31
|
+
console.error('[SignalingClient] Connection error:', JSON.stringify(event, null, 2));
|
|
32
|
+
this.onDisconnected?.();
|
|
33
|
+
} else if (event.type === 'message') {
|
|
34
|
+
try {
|
|
35
|
+
const data = JSON.parse(event.data || '{}');
|
|
36
|
+
if (['offer', 'answer', 'ice-candidate', 'command'].includes(data.type)) {
|
|
37
|
+
this.onMessage({
|
|
38
|
+
id: data.id,
|
|
39
|
+
type: data.type,
|
|
40
|
+
payload: data.payload,
|
|
41
|
+
createdAt: data.createdAt
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
} catch (e) {
|
|
45
|
+
console.error('[SignalingClient] Error parsing message:', e);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
const customEventListener = event => {
|
|
50
|
+
try {
|
|
51
|
+
const data = JSON.parse(event.data || '{}');
|
|
52
|
+
const eventType = event.type;
|
|
53
|
+
if (['offer', 'answer', 'ice-candidate', 'command'].includes(eventType)) {
|
|
54
|
+
this.onMessage({
|
|
55
|
+
id: data.id,
|
|
56
|
+
type: eventType,
|
|
57
|
+
payload: data.payload,
|
|
58
|
+
createdAt: data.createdAt
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
} catch (e) {
|
|
62
|
+
console.error('[SignalingClient] Error parsing custom event:', e);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// Ping handler - server sends pings to keep connection alive
|
|
67
|
+
const pingListener = event => {
|
|
68
|
+
console.log('[SignalingClient] Received ping');
|
|
69
|
+
// Just acknowledge by doing nothing, server-side heartbeat keeps connection alive
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
// Session-ended event listener
|
|
73
|
+
const sessionEndedListener = event => {
|
|
74
|
+
try {
|
|
75
|
+
const data = JSON.parse(event.data || '{}');
|
|
76
|
+
console.log('[SignalingClient] Session ended:', data.state);
|
|
77
|
+
if (this.onSessionEnded) {
|
|
78
|
+
this.onSessionEnded(data.state);
|
|
79
|
+
}
|
|
80
|
+
} catch (e) {
|
|
81
|
+
console.error('[SignalingClient] Error parsing session-ended event:', e);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
this.eventSource.addEventListener('open', listener);
|
|
85
|
+
this.eventSource.addEventListener('message', listener);
|
|
86
|
+
this.eventSource.addEventListener('error', listener);
|
|
87
|
+
this.eventSource.addEventListener('ping', pingListener);
|
|
88
|
+
this.eventSource.addEventListener('offer', customEventListener);
|
|
89
|
+
this.eventSource.addEventListener('answer', customEventListener);
|
|
90
|
+
this.eventSource.addEventListener('ice-candidate', customEventListener);
|
|
91
|
+
this.eventSource.addEventListener('command', customEventListener);
|
|
92
|
+
this.eventSource.addEventListener('session-ended', sessionEndedListener);
|
|
93
|
+
}
|
|
94
|
+
async send(type, payload) {
|
|
95
|
+
const urlParams = new URLSearchParams();
|
|
96
|
+
if (this.identificationId) {
|
|
97
|
+
urlParams.append('identificationId', this.identificationId);
|
|
98
|
+
}
|
|
99
|
+
const url = `${this.baseUrl}/api/app/mobile/video-sessions/${this.sessionId}/signaling?${urlParams.toString()}`;
|
|
100
|
+
try {
|
|
101
|
+
const body = {
|
|
102
|
+
type,
|
|
103
|
+
data: payload
|
|
104
|
+
};
|
|
105
|
+
const response = await fetch(url, {
|
|
106
|
+
method: 'POST',
|
|
107
|
+
headers: {
|
|
108
|
+
'Content-Type': 'application/json'
|
|
109
|
+
},
|
|
110
|
+
body: JSON.stringify(body)
|
|
111
|
+
});
|
|
112
|
+
if (!response.ok) {
|
|
113
|
+
throw new Error(`Failed to send signaling message: ${response.statusText}`);
|
|
114
|
+
}
|
|
115
|
+
} catch (error) {
|
|
116
|
+
console.error('[SignalingClient] Error sending message:', error);
|
|
117
|
+
throw error;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
disconnect() {
|
|
121
|
+
if (this.eventSource) {
|
|
122
|
+
this.eventSource.removeAllEventListeners();
|
|
123
|
+
this.eventSource.close();
|
|
124
|
+
this.eventSource = null;
|
|
125
|
+
this.onDisconnected?.();
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
@@ -188,6 +188,14 @@ const STEP_EVENT_MAP = {
|
|
|
188
188
|
liveness_check: {
|
|
189
189
|
started: AnalyticsEventName.LIVENESS_CHECK_STARTED,
|
|
190
190
|
completed: AnalyticsEventName.LIVENESS_CHECK_COMPLETED
|
|
191
|
+
},
|
|
192
|
+
verbal_consent: {
|
|
193
|
+
started: AnalyticsEventName.VERBAL_CONSENT_VIDEO_STARTED,
|
|
194
|
+
completed: AnalyticsEventName.VERBAL_CONSENT_VIDEO_COMPLETED
|
|
195
|
+
},
|
|
196
|
+
video_call: {
|
|
197
|
+
started: AnalyticsEventName.VIDEO_CALL_STARTED,
|
|
198
|
+
completed: AnalyticsEventName.VIDEO_CALL_COMPLETED
|
|
191
199
|
}
|
|
192
200
|
};
|
|
193
201
|
function getStepEventName(stepType, suffix) {
|
|
@@ -1,40 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
export function contains({
|
|
4
|
-
outside,
|
|
5
|
-
inside
|
|
6
|
-
}) {
|
|
7
|
-
const outsideMaxX = outside.minX + outside.width;
|
|
8
|
-
const insideMaxX = inside.minX + inside.width;
|
|
9
|
-
const outsideMaxY = outside.minY + outside.height;
|
|
10
|
-
const insideMaxY = inside.minY + inside.height;
|
|
11
|
-
if (inside.minX < outside.minX) {
|
|
12
|
-
return false;
|
|
13
|
-
}
|
|
14
|
-
if (insideMaxX > outsideMaxX) {
|
|
15
|
-
return false;
|
|
16
|
-
}
|
|
17
|
-
if (inside.minY < outside.minY) {
|
|
18
|
-
return false;
|
|
19
|
-
}
|
|
20
|
-
if (insideMaxY > outsideMaxY) {
|
|
21
|
-
return false;
|
|
22
|
-
}
|
|
23
|
-
return true;
|
|
24
|
-
}
|
|
25
|
-
export function contains2({
|
|
26
|
-
outside,
|
|
27
|
-
inside
|
|
28
|
-
}) {
|
|
29
|
-
const outsideMaxX = outside.minX + outside.width;
|
|
30
|
-
const insideMaxX = inside.minX + inside.width;
|
|
31
|
-
const outsideMaxY = outside.minY + outside.height;
|
|
32
|
-
const insideMaxY = inside.minY + inside.height;
|
|
33
|
-
const xIntersect = Math.max(0, Math.min(insideMaxX, outsideMaxX) - Math.max(inside.minX, outside.minX));
|
|
34
|
-
const yIntersect = Math.max(0, Math.min(insideMaxY, outsideMaxY) - Math.max(inside.minY, outside.minY));
|
|
35
|
-
const intersectArea = xIntersect * yIntersect;
|
|
36
|
-
const insideArea = inside.width * inside.height;
|
|
37
|
-
const outsideArea = outside.width * outside.height;
|
|
38
|
-
const unionArea = insideArea + outsideArea - intersectArea;
|
|
39
|
-
return unionArea === outsideArea;
|
|
40
|
-
}
|
|
1
|
+
"use strict";
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { ISOCountry } from "../EIDReader/data/isoCountry.js";
|
|
4
|
+
import { UnicodeCountry } from "../EIDReader/data/unicodeCountry.js";
|
|
5
|
+
const COUNTRY_NAME_OVERRIDES = {
|
|
6
|
+
TUR: 'Türkiye'
|
|
7
|
+
};
|
|
8
|
+
const findCountryByAlpha3 = alpha3Code => {
|
|
9
|
+
return [...UnicodeCountry.VALUES, ...ISOCountry.VALUES].find(country => country.toAlpha3Code() === alpha3Code);
|
|
10
|
+
};
|
|
11
|
+
export const getLocalizedCountryName = (alpha3Code, _language) => {
|
|
12
|
+
if (!alpha3Code) return '';
|
|
13
|
+
const normalizedCode = alpha3Code.trim().toUpperCase();
|
|
14
|
+
const overrideName = COUNTRY_NAME_OVERRIDES[normalizedCode];
|
|
15
|
+
if (overrideName) {
|
|
16
|
+
return overrideName;
|
|
17
|
+
}
|
|
18
|
+
const country = findCountryByAlpha3(normalizedCode);
|
|
19
|
+
if (!country) {
|
|
20
|
+
return normalizedCode;
|
|
21
|
+
}
|
|
22
|
+
if (typeof Intl !== 'undefined' && 'Locale' in Intl && typeof Intl.Locale === 'function' && 'DisplayNames' in Intl && typeof Intl.DisplayNames === 'function') {
|
|
23
|
+
const nativeLanguage = new Intl.Locale(`und-${country.toAlpha2Code()}`).maximize().language;
|
|
24
|
+
if (nativeLanguage) {
|
|
25
|
+
const nativeName = new Intl.DisplayNames([nativeLanguage], {
|
|
26
|
+
type: 'region'
|
|
27
|
+
}).of(country.toAlpha2Code());
|
|
28
|
+
if (nativeName) {
|
|
29
|
+
return nativeName;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return country.getName();
|
|
34
|
+
};
|
|
@@ -12,7 +12,7 @@ const handleDeepLink = ({
|
|
|
12
12
|
let baseUrl = '';
|
|
13
13
|
let sessionId = '';
|
|
14
14
|
for (let i = 0; i < segments.length; i++) {
|
|
15
|
-
if (segments[i] === 'verification-session') {
|
|
15
|
+
if (segments[i] === 'verification-session' || segments[i] === 'verification-sessions') {
|
|
16
16
|
sessionId = segments[i + 1] ?? '';
|
|
17
17
|
debugLog('handleDeepLink', 'Found sessionId:', sessionId);
|
|
18
18
|
} else if (segments[i] === 'app-url') {
|
|
@@ -20,6 +20,14 @@ const handleDeepLink = ({
|
|
|
20
20
|
debugLog('handleDeepLink', 'Found baseUrl:', baseUrl);
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
|
+
|
|
24
|
+
// If no app-url segment found, derive baseUrl from the URL itself
|
|
25
|
+
if (!baseUrl && sessionId) {
|
|
26
|
+
const match = url.match(/^(https?:\/\/[^/]+)/);
|
|
27
|
+
if (match) {
|
|
28
|
+
baseUrl = match[1];
|
|
29
|
+
}
|
|
30
|
+
}
|
|
23
31
|
debugLog('handleDeepLink', 'Returning:', {
|
|
24
32
|
baseUrl,
|
|
25
33
|
sessionId
|
|
@@ -28,6 +28,14 @@ const demoSession = {
|
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
|
+
}, {
|
|
32
|
+
type: 'VERBAL_CONSENT',
|
|
33
|
+
required: false,
|
|
34
|
+
data: {
|
|
35
|
+
voiceGuidanceActive: true,
|
|
36
|
+
verbalConsentTitle: 'Verbal Consent',
|
|
37
|
+
verbalConsentText: 'I hereby provide my verbal consent for identity verification purposes. I confirm that all information provided is accurate and complete.'
|
|
38
|
+
}
|
|
31
39
|
}, {
|
|
32
40
|
type: 'IDENTITY_DOCUMENT_SCAN',
|
|
33
41
|
required: false,
|
|
@@ -47,6 +47,10 @@ const request = async (httpMethod, url, body, simulatedResponse) => {
|
|
|
47
47
|
const startTime = Date.now();
|
|
48
48
|
let statusCode = 0;
|
|
49
49
|
let success = false;
|
|
50
|
+
console.log(`[HTTP] ${httpMethod} ${url}`);
|
|
51
|
+
if (body) {
|
|
52
|
+
console.log('[HTTP] Request body:', JSON.stringify(body).substring(0, 200) + '...');
|
|
53
|
+
}
|
|
50
54
|
try {
|
|
51
55
|
const response = await fetch(url, {
|
|
52
56
|
method: httpMethod,
|
|
@@ -57,11 +61,16 @@ const request = async (httpMethod, url, body, simulatedResponse) => {
|
|
|
57
61
|
});
|
|
58
62
|
statusCode = response.status;
|
|
59
63
|
success = response.ok;
|
|
64
|
+
console.log(`[HTTP] Response status: ${statusCode} ${response.ok ? '✓' : '✗'}`);
|
|
60
65
|
let responseJson = null;
|
|
61
66
|
try {
|
|
62
67
|
responseJson = await response.json();
|
|
68
|
+
if (responseJson) {
|
|
69
|
+
console.log('[HTTP] Response body:', JSON.stringify(responseJson).substring(0, 200) + '...');
|
|
70
|
+
}
|
|
63
71
|
} catch (error) {
|
|
64
72
|
// Invalid JSON response
|
|
73
|
+
console.log('[HTTP] Response body: (non-JSON)');
|
|
65
74
|
}
|
|
66
75
|
|
|
67
76
|
// Track API call performance (non-blocking)
|
|
@@ -108,8 +108,9 @@ const fixMRZ = rawText => {
|
|
|
108
108
|
// Must contain filler characters
|
|
109
109
|
if (!/<+/.test(line)) return false;
|
|
110
110
|
|
|
111
|
-
//
|
|
112
|
-
|
|
111
|
+
// Lines starting with a valid MRZ document code (ICAO 9303):
|
|
112
|
+
// I (ID cards), A (residence permits, admin docs), C (crew), P (passport), V (visa)
|
|
113
|
+
if (/^[IACPV][<A-Z]/.test(line)) return true;
|
|
113
114
|
|
|
114
115
|
// Or lines with numbers and fillers (second line of TD1: birth date, expiry, etc.)
|
|
115
116
|
if (/\d{5,}/.test(line) && /<{3,}/.test(line)) return true;
|
|
@@ -2,16 +2,30 @@
|
|
|
2
2
|
|
|
3
3
|
const runWithRetry = async (fn, maxRetries = 3, delay = 1000) => {
|
|
4
4
|
let retries = 0;
|
|
5
|
+
let lastError;
|
|
5
6
|
let result;
|
|
6
7
|
while (retries < maxRetries) {
|
|
7
8
|
try {
|
|
9
|
+
if (retries > 0) {
|
|
10
|
+
console.log(`[Retry] Attempt ${retries + 1}/${maxRetries}...`);
|
|
11
|
+
}
|
|
8
12
|
result = await fn();
|
|
13
|
+
if (retries > 0) {
|
|
14
|
+
console.log(`[Retry] ✓ Success on attempt ${retries + 1}`);
|
|
15
|
+
}
|
|
9
16
|
return result;
|
|
10
17
|
} catch (error) {
|
|
18
|
+
lastError = error;
|
|
11
19
|
retries++;
|
|
12
|
-
|
|
20
|
+
console.error(`[Retry] ✗ Attempt ${retries}/${maxRetries} failed:`, error instanceof Error ? error.message : error);
|
|
21
|
+
if (retries < maxRetries) {
|
|
22
|
+
const waitTime = delay * retries;
|
|
23
|
+
console.log(`[Retry] Waiting ${waitTime}ms before retry...`);
|
|
24
|
+
await new Promise(resolve => setTimeout(resolve, waitTime));
|
|
25
|
+
}
|
|
13
26
|
}
|
|
14
27
|
}
|
|
15
|
-
|
|
28
|
+
console.error('[Retry] ✗ All retries exhausted. Last error:', lastError);
|
|
29
|
+
throw new Error(`Max retries (${maxRetries}) exceeded. Last error: ${lastError instanceof Error ? lastError.message : String(lastError)}`);
|
|
16
30
|
};
|
|
17
31
|
export { runWithRetry };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { Platform, StatusBar } from 'react-native';
|
|
4
|
+
import { useEffect } from 'react';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Configure status bar for white background with dark content for better contrast
|
|
8
|
+
*/
|
|
9
|
+
export const configureStatusBarForWhiteBackground = () => {
|
|
10
|
+
StatusBar.setBarStyle('dark-content', true);
|
|
11
|
+
if (Platform.OS === 'android') {
|
|
12
|
+
StatusBar.setBackgroundColor('#ffffff', true);
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Hook to configure status bar for white background on mount
|
|
18
|
+
*/
|
|
19
|
+
export const useStatusBarWhiteBackground = () => {
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
configureStatusBarForWhiteBackground();
|
|
22
|
+
}, []);
|
|
23
|
+
};
|