@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.
Files changed (204) hide show
  1. package/android/src/main/java/com/trustchex/reactnativesdk/TrustchexSDKModule.kt +2 -8
  2. package/android/src/main/java/com/trustchex/reactnativesdk/camera/TrustchexCameraView.kt +60 -13
  3. package/android/src/main/java/com/trustchex/reactnativesdk/mlkit/MLKitModule.kt +1 -1
  4. package/ios/Camera/TrustchexCameraView.swift +10 -13
  5. package/ios/MLKit/MLKitModule.swift +1 -1
  6. package/lib/module/Screens/Debug/BarcodeTestScreen.js +308 -0
  7. package/lib/module/Screens/Debug/MRZTestScreen.js +105 -13
  8. package/lib/module/Screens/Debug/NFCScanTestScreen.js +635 -0
  9. package/lib/module/Screens/Dynamic/ContractAcceptanceScreen.js +49 -32
  10. package/lib/module/Screens/Dynamic/IdentityDocumentEIDScanningScreen.js +22 -4
  11. package/lib/module/Screens/Dynamic/IdentityDocumentScanningScreen.js +5 -0
  12. package/lib/module/Screens/Dynamic/LivenessDetectionScreen.js +126 -27
  13. package/lib/module/Screens/Dynamic/VerbalConsentScreen.js +1079 -0
  14. package/lib/module/Screens/Dynamic/VideoCallScreen.js +678 -0
  15. package/lib/module/Screens/Static/OTPVerificationScreen.js +6 -0
  16. package/lib/module/Screens/Static/QrCodeScanningScreen.js +7 -1
  17. package/lib/module/Screens/Static/ResultScreen.js +154 -34
  18. package/lib/module/Screens/Static/VerificationSessionCheckScreen.js +59 -51
  19. package/lib/module/Shared/Animations/recording.json +1 -0
  20. package/lib/module/Shared/Animations/video-call.json +1 -0
  21. package/lib/module/Shared/Components/DebugNavigationPanel.js +231 -67
  22. package/lib/module/Shared/Components/EIDScanner.js +213 -112
  23. package/lib/module/Shared/Components/IdentityDocumentCamera.flows.js +5 -3
  24. package/lib/module/Shared/Components/IdentityDocumentCamera.js +77 -39
  25. package/lib/module/Shared/Components/IdentityDocumentCamera.utils.js +13 -4
  26. package/lib/module/Shared/Components/NavigationManager.js +39 -19
  27. package/lib/module/Shared/Contexts/AppContext.js +1 -0
  28. package/lib/module/Shared/EIDReader/aesSecureMessagingWrapper.js +51 -0
  29. package/lib/module/Shared/EIDReader/apduLevelPACECapable.js +3 -0
  30. package/lib/module/Shared/EIDReader/bacKey.js +16 -2
  31. package/lib/module/Shared/EIDReader/eidReader.js +354 -13
  32. package/lib/module/Shared/EIDReader/eidService.js +25 -1
  33. package/lib/module/Shared/EIDReader/nfcManagerCardService.js +4 -7
  34. package/lib/module/Shared/EIDReader/paceInfo.js +85 -0
  35. package/lib/module/Shared/EIDReader/paceKeySpec.js +51 -0
  36. package/lib/module/Shared/EIDReader/protocol/paceAPDUSender.js +100 -0
  37. package/lib/module/Shared/EIDReader/protocol/paceProtocol.js +655 -0
  38. package/lib/module/Shared/EIDReader/protocol/paceResult.js +37 -0
  39. package/lib/module/Shared/EIDReader/secureMessagingWrapper.js +27 -4
  40. package/lib/module/Shared/EIDReader/smartcards/commandAPDU.js +2 -1
  41. package/lib/module/Shared/EIDReader/tlv/tlv.helpers.js +1 -1
  42. package/lib/module/Shared/EIDReader/tlv/tlv.utils.js +6 -3
  43. package/lib/module/Shared/EIDReader/utils/aesCrypto.utils.js +189 -0
  44. package/lib/module/Shared/Libs/SignalingClient.js +128 -0
  45. package/lib/module/Shared/Libs/analytics.utils.js +8 -0
  46. package/lib/module/Shared/Libs/contains.js +1 -40
  47. package/lib/module/Shared/Libs/country-display.utils.js +34 -0
  48. package/lib/module/Shared/Libs/deeplink.utils.js +9 -1
  49. package/lib/module/Shared/Libs/demo.utils.js +8 -0
  50. package/lib/module/Shared/Libs/http-client.js +9 -0
  51. package/lib/module/Shared/Libs/mrz.utils.js +3 -2
  52. package/lib/module/Shared/Libs/promise.utils.js +16 -2
  53. package/lib/module/Shared/Libs/status-bar.utils.js +23 -0
  54. package/lib/module/Shared/Services/DataUploadService.js +294 -0
  55. package/lib/module/Shared/Services/VideoSessionService.js +156 -0
  56. package/lib/module/Shared/Services/WebRTCService.js +510 -0
  57. package/lib/module/Shared/Types/analytics.types.js +4 -0
  58. package/lib/module/Translation/Resources/en.js +61 -2
  59. package/lib/module/Translation/Resources/tr.js +61 -2
  60. package/lib/module/Trustchex.js +64 -20
  61. package/lib/module/version.js +1 -1
  62. package/lib/typescript/src/Screens/Debug/BarcodeTestScreen.d.ts +3 -0
  63. package/lib/typescript/src/Screens/Debug/BarcodeTestScreen.d.ts.map +1 -0
  64. package/lib/typescript/src/Screens/Debug/MRZTestScreen.d.ts.map +1 -1
  65. package/lib/typescript/src/Screens/Debug/NFCScanTestScreen.d.ts +3 -0
  66. package/lib/typescript/src/Screens/Debug/NFCScanTestScreen.d.ts.map +1 -0
  67. package/lib/typescript/src/Screens/Dynamic/ContractAcceptanceScreen.d.ts.map +1 -1
  68. package/lib/typescript/src/Screens/Dynamic/IdentityDocumentEIDScanningScreen.d.ts.map +1 -1
  69. package/lib/typescript/src/Screens/Dynamic/IdentityDocumentScanningScreen.d.ts.map +1 -1
  70. package/lib/typescript/src/Screens/Dynamic/LivenessDetectionScreen.d.ts.map +1 -1
  71. package/lib/typescript/src/Screens/Dynamic/VerbalConsentScreen.d.ts +3 -0
  72. package/lib/typescript/src/Screens/Dynamic/VerbalConsentScreen.d.ts.map +1 -0
  73. package/lib/typescript/src/Screens/Dynamic/VideoCallScreen.d.ts +3 -0
  74. package/lib/typescript/src/Screens/Dynamic/VideoCallScreen.d.ts.map +1 -0
  75. package/lib/typescript/src/Screens/Static/OTPVerificationScreen.d.ts.map +1 -1
  76. package/lib/typescript/src/Screens/Static/QrCodeScanningScreen.d.ts.map +1 -1
  77. package/lib/typescript/src/Screens/Static/ResultScreen.d.ts.map +1 -1
  78. package/lib/typescript/src/Screens/Static/VerificationSessionCheckScreen.d.ts.map +1 -1
  79. package/lib/typescript/src/Shared/Components/DebugNavigationPanel.d.ts.map +1 -1
  80. package/lib/typescript/src/Shared/Components/EIDScanner.d.ts.map +1 -1
  81. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.d.ts.map +1 -1
  82. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.flows.d.ts +1 -1
  83. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.flows.d.ts.map +1 -1
  84. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.utils.d.ts +5 -0
  85. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.utils.d.ts.map +1 -1
  86. package/lib/typescript/src/Shared/Components/NavigationManager.d.ts.map +1 -1
  87. package/lib/typescript/src/Shared/Contexts/AppContext.d.ts +1 -0
  88. package/lib/typescript/src/Shared/Contexts/AppContext.d.ts.map +1 -1
  89. package/lib/typescript/src/Shared/EIDReader/aesSecureMessagingWrapper.d.ts +18 -0
  90. package/lib/typescript/src/Shared/EIDReader/aesSecureMessagingWrapper.d.ts.map +1 -0
  91. package/lib/typescript/src/Shared/EIDReader/apduLevelPACECapable.d.ts +23 -0
  92. package/lib/typescript/src/Shared/EIDReader/apduLevelPACECapable.d.ts.map +1 -0
  93. package/lib/typescript/src/Shared/EIDReader/bacKey.d.ts +6 -0
  94. package/lib/typescript/src/Shared/EIDReader/bacKey.d.ts.map +1 -1
  95. package/lib/typescript/src/Shared/EIDReader/eidReader.d.ts.map +1 -1
  96. package/lib/typescript/src/Shared/EIDReader/eidService.d.ts +9 -0
  97. package/lib/typescript/src/Shared/EIDReader/eidService.d.ts.map +1 -1
  98. package/lib/typescript/src/Shared/EIDReader/nfcManagerCardService.d.ts.map +1 -1
  99. package/lib/typescript/src/Shared/EIDReader/paceInfo.d.ts +50 -0
  100. package/lib/typescript/src/Shared/EIDReader/paceInfo.d.ts.map +1 -0
  101. package/lib/typescript/src/Shared/EIDReader/paceKeySpec.d.ts +30 -0
  102. package/lib/typescript/src/Shared/EIDReader/paceKeySpec.d.ts.map +1 -0
  103. package/lib/typescript/src/Shared/EIDReader/protocol/paceAPDUSender.d.ts +17 -0
  104. package/lib/typescript/src/Shared/EIDReader/protocol/paceAPDUSender.d.ts.map +1 -0
  105. package/lib/typescript/src/Shared/EIDReader/protocol/paceProtocol.d.ts +105 -0
  106. package/lib/typescript/src/Shared/EIDReader/protocol/paceProtocol.d.ts.map +1 -0
  107. package/lib/typescript/src/Shared/EIDReader/protocol/paceResult.d.ts +24 -0
  108. package/lib/typescript/src/Shared/EIDReader/protocol/paceResult.d.ts.map +1 -0
  109. package/lib/typescript/src/Shared/EIDReader/secureMessagingWrapper.d.ts +15 -0
  110. package/lib/typescript/src/Shared/EIDReader/secureMessagingWrapper.d.ts.map +1 -1
  111. package/lib/typescript/src/Shared/EIDReader/smartcards/commandAPDU.d.ts.map +1 -1
  112. package/lib/typescript/src/Shared/EIDReader/tlv/tlv.utils.d.ts.map +1 -1
  113. package/lib/typescript/src/Shared/EIDReader/utils/aesCrypto.utils.d.ts +39 -0
  114. package/lib/typescript/src/Shared/EIDReader/utils/aesCrypto.utils.d.ts.map +1 -0
  115. package/lib/typescript/src/Shared/Libs/SignalingClient.d.ts +24 -0
  116. package/lib/typescript/src/Shared/Libs/SignalingClient.d.ts.map +1 -0
  117. package/lib/typescript/src/Shared/Libs/analytics.utils.d.ts.map +1 -1
  118. package/lib/typescript/src/Shared/Libs/contains.d.ts +0 -7
  119. package/lib/typescript/src/Shared/Libs/contains.d.ts.map +1 -1
  120. package/lib/typescript/src/Shared/Libs/country-display.utils.d.ts +2 -0
  121. package/lib/typescript/src/Shared/Libs/country-display.utils.d.ts.map +1 -0
  122. package/lib/typescript/src/Shared/Libs/deeplink.utils.d.ts.map +1 -1
  123. package/lib/typescript/src/Shared/Libs/demo.utils.d.ts.map +1 -1
  124. package/lib/typescript/src/Shared/Libs/http-client.d.ts +1 -1
  125. package/lib/typescript/src/Shared/Libs/http-client.d.ts.map +1 -1
  126. package/lib/typescript/src/Shared/Libs/mrz.utils.d.ts.map +1 -1
  127. package/lib/typescript/src/Shared/Libs/promise.utils.d.ts.map +1 -1
  128. package/lib/typescript/src/Shared/Libs/status-bar.utils.d.ts +9 -0
  129. package/lib/typescript/src/Shared/Libs/status-bar.utils.d.ts.map +1 -0
  130. package/lib/typescript/src/Shared/Services/DataUploadService.d.ts +25 -0
  131. package/lib/typescript/src/Shared/Services/DataUploadService.d.ts.map +1 -0
  132. package/lib/typescript/src/Shared/Services/VideoSessionService.d.ts +33 -0
  133. package/lib/typescript/src/Shared/Services/VideoSessionService.d.ts.map +1 -0
  134. package/lib/typescript/src/Shared/Services/WebRTCService.d.ts +58 -0
  135. package/lib/typescript/src/Shared/Services/WebRTCService.d.ts.map +1 -0
  136. package/lib/typescript/src/Shared/Types/analytics.types.d.ts +4 -0
  137. package/lib/typescript/src/Shared/Types/analytics.types.d.ts.map +1 -1
  138. package/lib/typescript/src/Shared/Types/identificationInfo.d.ts +13 -1
  139. package/lib/typescript/src/Shared/Types/identificationInfo.d.ts.map +1 -1
  140. package/lib/typescript/src/Translation/Resources/en.d.ts +60 -1
  141. package/lib/typescript/src/Translation/Resources/en.d.ts.map +1 -1
  142. package/lib/typescript/src/Translation/Resources/tr.d.ts +60 -1
  143. package/lib/typescript/src/Translation/Resources/tr.d.ts.map +1 -1
  144. package/lib/typescript/src/Trustchex.d.ts.map +1 -1
  145. package/lib/typescript/src/version.d.ts +1 -1
  146. package/package.json +35 -5
  147. package/src/Screens/Debug/BarcodeTestScreen.tsx +317 -0
  148. package/src/Screens/Debug/MRZTestScreen.tsx +107 -13
  149. package/src/Screens/Debug/NFCScanTestScreen.tsx +692 -0
  150. package/src/Screens/Dynamic/ContractAcceptanceScreen.tsx +58 -35
  151. package/src/Screens/Dynamic/IdentityDocumentEIDScanningScreen.tsx +27 -4
  152. package/src/Screens/Dynamic/IdentityDocumentScanningScreen.tsx +6 -0
  153. package/src/Screens/Dynamic/LivenessDetectionScreen.tsx +156 -27
  154. package/src/Screens/Dynamic/VerbalConsentScreen.tsx +1401 -0
  155. package/src/Screens/Dynamic/VideoCallScreen.tsx +766 -0
  156. package/src/Screens/Static/OTPVerificationScreen.tsx +6 -0
  157. package/src/Screens/Static/QrCodeScanningScreen.tsx +7 -1
  158. package/src/Screens/Static/ResultScreen.tsx +235 -48
  159. package/src/Screens/Static/VerificationSessionCheckScreen.tsx +67 -72
  160. package/src/Shared/Animations/recording.json +1 -0
  161. package/src/Shared/Animations/video-call.json +1 -0
  162. package/src/Shared/Components/DebugNavigationPanel.tsx +252 -51
  163. package/src/Shared/Components/EIDScanner.tsx +223 -116
  164. package/src/Shared/Components/IdentityDocumentCamera.flows.ts +7 -4
  165. package/src/Shared/Components/IdentityDocumentCamera.tsx +224 -188
  166. package/src/Shared/Components/IdentityDocumentCamera.utils.ts +13 -4
  167. package/src/Shared/Components/NavigationManager.tsx +41 -19
  168. package/src/Shared/Contexts/AppContext.ts +2 -0
  169. package/src/Shared/EIDReader/aesSecureMessagingWrapper.ts +69 -0
  170. package/src/Shared/EIDReader/apduLevelPACECapable.ts +34 -0
  171. package/src/Shared/EIDReader/bacKey.ts +24 -8
  172. package/src/Shared/EIDReader/eidReader.ts +398 -12
  173. package/src/Shared/EIDReader/eidService.ts +49 -1
  174. package/src/Shared/EIDReader/nfcManagerCardService.ts +4 -6
  175. package/src/Shared/EIDReader/paceInfo.ts +159 -0
  176. package/src/Shared/EIDReader/paceKeySpec.ts +56 -0
  177. package/src/Shared/EIDReader/protocol/paceAPDUSender.ts +163 -0
  178. package/src/Shared/EIDReader/protocol/paceProtocol.ts +946 -0
  179. package/src/Shared/EIDReader/protocol/paceResult.ts +62 -0
  180. package/src/Shared/EIDReader/secureMessagingWrapper.ts +28 -10
  181. package/src/Shared/EIDReader/smartcards/commandAPDU.ts +2 -1
  182. package/src/Shared/EIDReader/tlv/tlv.helpers.ts +1 -1
  183. package/src/Shared/EIDReader/tlv/tlv.utils.ts +8 -5
  184. package/src/Shared/EIDReader/utils/aesCrypto.utils.ts +217 -0
  185. package/src/Shared/Libs/SignalingClient.ts +189 -0
  186. package/src/Shared/Libs/analytics.utils.ts +8 -0
  187. package/src/Shared/Libs/contains.ts +0 -53
  188. package/src/Shared/Libs/country-display.utils.ts +55 -0
  189. package/src/Shared/Libs/crypto.utils.ts +2 -2
  190. package/src/Shared/Libs/deeplink.utils.ts +12 -1
  191. package/src/Shared/Libs/demo.utils.ts +10 -0
  192. package/src/Shared/Libs/http-client.ts +19 -1
  193. package/src/Shared/Libs/mrz.utils.ts +3 -2
  194. package/src/Shared/Libs/promise.utils.ts +16 -2
  195. package/src/Shared/Libs/status-bar.utils.ts +21 -0
  196. package/src/Shared/Services/DataUploadService.ts +395 -0
  197. package/src/Shared/Services/VideoSessionService.ts +190 -0
  198. package/src/Shared/Services/WebRTCService.ts +636 -0
  199. package/src/Shared/Types/analytics.types.ts +4 -0
  200. package/src/Shared/Types/identificationInfo.ts +16 -1
  201. package/src/Translation/Resources/en.ts +88 -3
  202. package/src/Translation/Resources/tr.ts +89 -3
  203. package/src/Trustchex.tsx +65 -19
  204. package/src/version.ts +1 -1
@@ -10,10 +10,14 @@ import i18n from "../../Translation/index.js";
10
10
  import StyledButton from "./StyledButton.js";
11
11
  import { analyticsService } from "../Services/AnalyticsService.js";
12
12
 
13
- // Simple global navigation lock
13
+ // Navigation lock - use a ref-like pattern so unmounting resets state.
14
+ // The module-level object is shared across all instances but the
15
+ // cleanup effect in the component resets it on unmount.
14
16
  import { jsx as _jsx } from "react/jsx-runtime";
15
- let isNavigating = false;
16
- let lastNavigationTime = 0;
17
+ const navigationLock = {
18
+ isNavigating: false,
19
+ lastNavigationTime: 0
20
+ };
17
21
  const NavigationManager = /*#__PURE__*/forwardRef(({
18
22
  canSkipStep
19
23
  }, ref) => {
@@ -29,18 +33,20 @@ const NavigationManager = /*#__PURE__*/forwardRef(({
29
33
  CONTRACT_ACCEPTANCE: 'ContractAcceptanceScreen',
30
34
  IDENTITY_DOCUMENT_SCAN: 'IdentityDocumentScanningScreen',
31
35
  IDENTITY_DOCUMENT_EID_SCAN: 'IdentityDocumentEIDScanningScreen',
32
- LIVENESS_CHECK: 'LivenessDetectionScreen'
36
+ LIVENESS_CHECK: 'LivenessDetectionScreen',
37
+ VERBAL_CONSENT: 'VerbalConsentScreen',
38
+ VIDEO_CALL: 'VideoCallScreen'
33
39
  },
34
40
  RESULT: 'ResultScreen'
35
41
  };
36
42
  React.useEffect(() => {
37
43
  const handleFocus = () => {
38
- isNavigating = false;
44
+ navigationLock.isNavigating = false;
39
45
  };
40
46
  navigation.addListener('focus', handleFocus);
41
47
  return () => {
42
48
  navigation.removeListener('focus', handleFocus);
43
- isNavigating = false;
49
+ navigationLock.isNavigating = false;
44
50
  };
45
51
  }, [navigation]);
46
52
  const getNextRoute = useCallback((workflowSteps, currentWorkFlowStep) => {
@@ -69,16 +75,22 @@ const NavigationManager = /*#__PURE__*/forwardRef(({
69
75
  if (nextStep.type === 'LIVENESS_CHECK') {
70
76
  return routes.DYNAMIC_ROUTES.LIVENESS_CHECK;
71
77
  }
78
+ if (nextStep.type === 'VIDEO_CALL') {
79
+ return routes.DYNAMIC_ROUTES.VIDEO_CALL;
80
+ }
81
+ if (nextStep.type === 'VERBAL_CONSENT') {
82
+ return routes.DYNAMIC_ROUTES.VERBAL_CONSENT;
83
+ }
72
84
  return routes.VERIFICATION_SESSION_CHECK;
73
- }, [appContext, routes.VERIFICATION_SESSION_CHECK, routes.DYNAMIC_ROUTES.CONTRACT_ACCEPTANCE, routes.DYNAMIC_ROUTES.IDENTITY_DOCUMENT_EID_SCAN, routes.DYNAMIC_ROUTES.IDENTITY_DOCUMENT_SCAN, routes.DYNAMIC_ROUTES.LIVENESS_CHECK, routes.RESULT]);
85
+ }, [appContext, routes.VERIFICATION_SESSION_CHECK, routes.RESULT, routes.DYNAMIC_ROUTES.CONTRACT_ACCEPTANCE, routes.DYNAMIC_ROUTES.IDENTITY_DOCUMENT_SCAN, routes.DYNAMIC_ROUTES.IDENTITY_DOCUMENT_EID_SCAN, routes.DYNAMIC_ROUTES.LIVENESS_CHECK, routes.DYNAMIC_ROUTES.VIDEO_CALL, routes.DYNAMIC_ROUTES.VERBAL_CONSENT]);
74
86
  const goToNextRoute = useCallback(() => {
75
87
  const currentTime = Date.now();
76
88
  const minTimeBetweenNavigation = 1000;
77
- if (isNavigating || currentTime - lastNavigationTime < minTimeBetweenNavigation) {
89
+ if (navigationLock.isNavigating || currentTime - navigationLock.lastNavigationTime < minTimeBetweenNavigation) {
78
90
  return;
79
91
  }
80
- isNavigating = true;
81
- lastNavigationTime = currentTime;
92
+ navigationLock.isNavigating = true;
93
+ navigationLock.lastNavigationTime = currentTime;
82
94
  try {
83
95
  const nextRoute = getNextRoute(appContext.workflowSteps, appContext.currentWorkflowStep);
84
96
  navigation.dispatch(CommonActions.navigate({
@@ -88,15 +100,15 @@ const NavigationManager = /*#__PURE__*/forwardRef(({
88
100
  }
89
101
  }));
90
102
  } catch (error) {
91
- isNavigating = false;
103
+ navigationLock.isNavigating = false;
92
104
  throw error;
93
105
  }
94
106
  setTimeout(() => {
95
- isNavigating = false;
107
+ navigationLock.isNavigating = false;
96
108
  }, 1000);
97
109
  }, [getNextRoute, appContext.workflowSteps, appContext.currentWorkflowStep, navigation]);
98
110
  const goToNextRouteWithAlert = useCallback(() => {
99
- if (isNavigating) {
111
+ if (navigationLock.isNavigating) {
100
112
  return;
101
113
  }
102
114
  Alert.alert(t('general.warning'), t('navigationManager.skipStepWarning'), [{
@@ -108,10 +120,10 @@ const NavigationManager = /*#__PURE__*/forwardRef(({
108
120
  }]);
109
121
  }, [goToNextRoute, t]);
110
122
  const reset = useCallback(() => {
111
- if (isNavigating) {
123
+ if (navigationLock.isNavigating) {
112
124
  return;
113
125
  }
114
- isNavigating = true;
126
+ navigationLock.isNavigating = true;
115
127
  try {
116
128
  // Preserve demo session state when resetting
117
129
  const wasDemoSession = appContext.isDemoSession;
@@ -124,8 +136,15 @@ const NavigationManager = /*#__PURE__*/forwardRef(({
124
136
  contractIds: [],
125
137
  deviceInfo: ''
126
138
  },
127
- locale: appContext.locale || i18n.language
139
+ locale: appContext.locale || i18n.language,
140
+ // Explicitly reset collected data fields
141
+ scannedDocument: undefined,
142
+ livenessDetection: undefined,
143
+ authToken: undefined,
144
+ videoSessionId: undefined
128
145
  };
146
+
147
+ // Reset branding to defaults while preserving any custom values
129
148
  appContext.branding = {
130
149
  logoUrl: appContext.branding?.logoUrl || '',
131
150
  primaryColor: appContext.branding?.primaryColor || '#000000',
@@ -138,6 +157,7 @@ const NavigationManager = /*#__PURE__*/forwardRef(({
138
157
  appContext.setIsDemoSession?.(false);
139
158
  analyticsService.setDemoSession(false);
140
159
  }
160
+ appContext.isTestVideoSession = false;
141
161
  navigation.dispatch(CommonActions.reset({
142
162
  index: 0,
143
163
  routes: [{
@@ -145,17 +165,17 @@ const NavigationManager = /*#__PURE__*/forwardRef(({
145
165
  }]
146
166
  }));
147
167
  } catch (error) {
148
- isNavigating = false;
168
+ navigationLock.isNavigating = false;
149
169
  throw error;
150
170
  }
151
171
  setTimeout(() => {
152
- isNavigating = false;
172
+ navigationLock.isNavigating = false;
153
173
  }, 1000);
154
174
  }, [appContext, navigation, routes.VERIFICATION_SESSION_CHECK]);
155
175
  usePreventRemove(true, ({
156
176
  data
157
177
  }) => {
158
- if (data.action.type === 'RESET') {
178
+ if (data?.action?.type === 'RESET') {
159
179
  navigation.dispatch(data.action);
160
180
  }
161
181
  });
@@ -23,6 +23,7 @@ export default /*#__PURE__*/createContext({
23
23
  workflowSteps: [],
24
24
  currentWorkflowStep: undefined,
25
25
  isDebugNavigated: false,
26
+ isTestVideoSession: false,
26
27
  onCompleted: undefined,
27
28
  onError: undefined,
28
29
  setSessionId: undefined,
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+
3
+ import { SecureMessagingWrapper } from "./secureMessagingWrapper.js";
4
+ import { Buffer } from 'buffer';
5
+ import { aesEncryptCBC, aesDecryptCBC, aesEncryptECB, aesCMAC } from "./utils/aesCrypto.utils.js";
6
+
7
+ /**
8
+ * AES-based secure messaging wrapper for PACE.
9
+ *
10
+ * Uses AES-CBC for encryption and AES-CMAC for MAC computation,
11
+ * as specified in ICAO Doc 9303 Part 11.
12
+ */
13
+ export class AESSecureMessagingWrapper extends SecureMessagingWrapper {
14
+ constructor(ksEnc, ksMac, maxTransceiveLength, shouldCheckMAC, ssc) {
15
+ super(ksEnc, ksMac, maxTransceiveLength, shouldCheckMAC, ssc);
16
+ }
17
+ getType() {
18
+ return 'AES';
19
+ }
20
+ getPadLength() {
21
+ return 16;
22
+ }
23
+ getIV() {
24
+ // AES IV is E(ksEnc, SSC)
25
+ const sscHex = Buffer.from(this.getEncodedSendSequenceCounter()).toString('hex');
26
+ const ivHex = aesEncryptECB(sscHex, this.getEncryptionKey());
27
+ return Array.from(Buffer.from(ivHex, 'hex'));
28
+ }
29
+ encrypt(dataHex, keyHex) {
30
+ const ivHex = Buffer.from(this.getIV()).toString('hex');
31
+ return aesEncryptCBC(dataHex, keyHex, ivHex);
32
+ }
33
+ decrypt(dataHex, keyHex) {
34
+ const ivHex = Buffer.from(this.getIV()).toString('hex');
35
+ return aesDecryptCBC(dataHex, keyHex, ivHex);
36
+ }
37
+ computeMAC(dataHex, keyHex) {
38
+ return aesCMAC(dataHex, keyHex);
39
+ }
40
+ getEncodedSendSequenceCounter() {
41
+ // AES uses 16-byte SSC (128-bit counter)
42
+ const ssc = this.getSendSequenceCounter();
43
+ const bytes = new Uint8Array(16);
44
+ let remaining = ssc;
45
+ for (let i = 15; i >= 0; i--) {
46
+ bytes[i] = Number(remaining & 0xffn);
47
+ remaining >>= 8n;
48
+ }
49
+ return bytes;
50
+ }
51
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+
3
+ export {};
@@ -69,13 +69,27 @@ export class BACKey {
69
69
  */
70
70
  getKey() {
71
71
  try {
72
- const mrz = this.documentNumber + MRZInfo.checkDigit(this.documentNumber, false) + this.dateOfBirth + MRZInfo.checkDigit(this.dateOfBirth, false) + this.dateOfExpiry + MRZInfo.checkDigit(this.dateOfExpiry, false);
73
- return cryptoUtils.computeKeySeed(mrz, 'SHA-1', true);
72
+ return cryptoUtils.computeKeySeed(this.getMRZString(), 'SHA-1', true);
74
73
  } catch (gse) {
75
74
  throw new Error('Unexpected exception');
76
75
  }
77
76
  }
78
77
 
78
+ /**
79
+ * Returns the key seed for PACE, which is the full (non-truncated) SHA-1 hash
80
+ * of the MRZ info. PACE uses the full 20-byte hash unlike BAC which truncates to 16.
81
+ */
82
+ getKeySeedForPACE() {
83
+ try {
84
+ return cryptoUtils.computeKeySeed(this.getMRZString(), 'SHA-1', false);
85
+ } catch (gse) {
86
+ throw new Error('Unexpected exception');
87
+ }
88
+ }
89
+ getMRZString() {
90
+ return this.documentNumber + MRZInfo.checkDigit(this.documentNumber, false) + this.dateOfBirth + MRZInfo.checkDigit(this.dateOfBirth, false) + this.dateOfExpiry + MRZInfo.checkDigit(this.dateOfExpiry, false);
91
+ }
92
+
79
93
  /**
80
94
  * Sets the document number.
81
95
  *
@@ -1,35 +1,374 @@
1
1
  "use strict";
2
2
 
3
3
  import { BACKey } from "./bacKey.js";
4
+ import { PACEKeySpec } from "./paceKeySpec.js";
4
5
  import { NFCManagerCardService } from "./nfcManagerCardService.js";
5
6
  import { EIDService } from "./eidService.js";
6
7
  import { DG1File } from "./lds/icao/dg1File.js";
7
8
  import { DG2File } from "./lds/icao/dg2File.js";
8
9
  import { Buffer } from 'buffer';
9
10
  import { InputStream } from "./java/inputStream.js";
11
+
12
+ // --- Minimal PNG encoder (pure JS, no native deps) ---
13
+ // Uses deflate stored (uncompressed) blocks for O(n) encoding speed.
14
+ // A naive DCT-based JPEG encoder would be O(n²) and freeze the JS thread
15
+ // on real passport photos (400×500+ pixels).
16
+
17
+ /** Standard CRC32 lookup table */
18
+ const CRC_TABLE = new Uint32Array(256);
19
+ for (let n = 0; n < 256; n++) {
20
+ let c = n;
21
+ for (let k = 0; k < 8; k++) {
22
+ c = c & 1 ? 0xedb88320 ^ c >>> 1 : c >>> 1;
23
+ }
24
+ CRC_TABLE[n] = c;
25
+ }
26
+ function crc32(data) {
27
+ let crc = 0xffffffff;
28
+ for (let i = 0; i < data.length; i++) {
29
+ crc = CRC_TABLE[(crc ^ data[i]) & 0xff] ^ crc >>> 8;
30
+ }
31
+ return (crc ^ 0xffffffff) >>> 0;
32
+ }
33
+ function adler32(data) {
34
+ let a = 1;
35
+ let b = 0;
36
+ for (let i = 0; i < data.length; i++) {
37
+ a = (a + data[i]) % 65521;
38
+ b = (b + a) % 65521;
39
+ }
40
+ return (b << 16 | a) >>> 0;
41
+ }
42
+ function writeU32BE(arr, offset, val) {
43
+ arr[offset] = val >>> 24 & 0xff;
44
+ arr[offset + 1] = val >>> 16 & 0xff;
45
+ arr[offset + 2] = val >>> 8 & 0xff;
46
+ arr[offset + 3] = val & 0xff;
47
+ }
48
+
49
+ /**
50
+ * Encode raw RGB/RGBA pixels as an uncompressed PNG.
51
+ * Uses deflate stored blocks (no compression) for maximum compatibility.
52
+ */
53
+ function pixelsToPng(pixels, width, height, channels) {
54
+ // Build raw scanlines: filter byte (0) + RGB data per row
55
+ const rawRowLen = 1 + width * 3; // filter byte + RGB
56
+ const rawData = new Uint8Array(rawRowLen * height);
57
+ for (let y = 0; y < height; y++) {
58
+ const rowOff = y * rawRowLen;
59
+ rawData[rowOff] = 0; // filter: None
60
+ for (let x = 0; x < width; x++) {
61
+ const srcIdx = (y * width + x) * channels;
62
+ const dstIdx = rowOff + 1 + x * 3;
63
+ rawData[dstIdx] = pixels[srcIdx]; // R
64
+ rawData[dstIdx + 1] = pixels[srcIdx + 1]; // G
65
+ rawData[dstIdx + 2] = pixels[srcIdx + 2]; // B
66
+ }
67
+ }
68
+
69
+ // Wrap raw data in zlib stored (uncompressed) format
70
+ const MAX_BLOCK = 65535;
71
+ const numBlocks = Math.ceil(rawData.length / MAX_BLOCK) || 1;
72
+ const zlibSize = 2 + rawData.length + numBlocks * 5 + 4;
73
+ const zlib = new Uint8Array(zlibSize);
74
+ let zOff = 0;
75
+ // zlib header: CMF=0x78 (deflate, 32K window), FLG=0x01 (check bits, no dict, level 0)
76
+ zlib[zOff++] = 0x78;
77
+ zlib[zOff++] = 0x01;
78
+ let remaining = rawData.length;
79
+ let srcOff = 0;
80
+ while (remaining > 0) {
81
+ const blockLen = Math.min(remaining, MAX_BLOCK);
82
+ const isFinal = remaining <= MAX_BLOCK ? 1 : 0;
83
+ zlib[zOff++] = isFinal; // BFINAL + BTYPE=00 (stored)
84
+ zlib[zOff++] = blockLen & 0xff;
85
+ zlib[zOff++] = blockLen >>> 8 & 0xff;
86
+ zlib[zOff++] = ~blockLen & 0xff;
87
+ zlib[zOff++] = ~blockLen >>> 8 & 0xff;
88
+ zlib.set(rawData.subarray(srcOff, srcOff + blockLen), zOff);
89
+ zOff += blockLen;
90
+ srcOff += blockLen;
91
+ remaining -= blockLen;
92
+ }
93
+ // Adler32 of uncompressed data
94
+ const adl = adler32(rawData);
95
+ writeU32BE(zlib, zOff, adl);
96
+ zOff += 4;
97
+ const zlibData = zlib.subarray(0, zOff);
98
+
99
+ // Build PNG chunks
100
+ const PNG_SIG = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10]);
101
+ function makeChunk(type, data) {
102
+ const chunk = new Uint8Array(4 + 4 + data.length + 4);
103
+ writeU32BE(chunk, 0, data.length);
104
+ chunk[4] = type.charCodeAt(0);
105
+ chunk[5] = type.charCodeAt(1);
106
+ chunk[6] = type.charCodeAt(2);
107
+ chunk[7] = type.charCodeAt(3);
108
+ chunk.set(data, 8);
109
+ const crcInput = chunk.subarray(4, 8 + data.length);
110
+ writeU32BE(chunk, 8 + data.length, crc32(crcInput));
111
+ return chunk;
112
+ }
113
+
114
+ // IHDR: width, height, bit depth 8, color type 2 (RGB)
115
+ const ihdrData = new Uint8Array(13);
116
+ writeU32BE(ihdrData, 0, width);
117
+ writeU32BE(ihdrData, 4, height);
118
+ ihdrData[8] = 8; // bit depth
119
+ ihdrData[9] = 2; // color type: RGB
120
+ ihdrData[10] = 0; // compression
121
+ ihdrData[11] = 0; // filter
122
+ ihdrData[12] = 0; // interlace
123
+
124
+ const ihdrChunk = makeChunk('IHDR', ihdrData);
125
+ const idatChunk = makeChunk('IDAT', zlibData);
126
+ const iendChunk = makeChunk('IEND', new Uint8Array(0));
127
+ const png = new Uint8Array(PNG_SIG.length + ihdrChunk.length + idatChunk.length + iendChunk.length);
128
+ let off = 0;
129
+ png.set(PNG_SIG, off);
130
+ off += PNG_SIG.length;
131
+ png.set(ihdrChunk, off);
132
+ off += ihdrChunk.length;
133
+ png.set(idatChunk, off);
134
+ off += idatChunk.length;
135
+ png.set(iendChunk, off);
136
+ return png;
137
+ }
138
+
139
+ /**
140
+ * If the image is JPEG 2000, decode and convert to PNG so React Native can display it.
141
+ * Returns { base64, mimeType } with the converted image, or the original if not JP2.
142
+ */
143
+ function convertJP2IfNeeded(imageBuffer, mimeType) {
144
+ if (mimeType !== 'image/jp2') {
145
+ return {
146
+ base64: imageBuffer.toString('base64'),
147
+ mimeType
148
+ };
149
+ }
150
+ console.debug('[EID] Face image is JP2, attempting conversion…');
151
+ try {
152
+ // Lazy-load jpeg2000 to avoid crashing if it fails to import
153
+ const {
154
+ JpxImage
155
+ } = require('jpeg2000');
156
+ const jpx = new JpxImage();
157
+ jpx.parse(imageBuffer);
158
+ const tile = jpx.tiles[0];
159
+ console.debug(`[EID] JP2 decoded: ${tile.width}x${tile.height}, ${jpx.componentsCount} channels, ${tile.items.length} bytes`);
160
+ const pngBytes = pixelsToPng(tile.items, tile.width, tile.height, jpx.componentsCount);
161
+ const pngBase64 = Buffer.from(pngBytes).toString('base64');
162
+ console.debug(`[EID] Converted JP2 → PNG (${pngBytes.length} bytes, ${pngBase64.length} base64 chars)`);
163
+ return {
164
+ base64: pngBase64,
165
+ mimeType: 'image/png'
166
+ };
167
+ } catch (err) {
168
+ console.debug('[EID] JP2 conversion failed:', err);
169
+ return {
170
+ base64: imageBuffer.toString('base64'),
171
+ mimeType
172
+ };
173
+ }
174
+ }
175
+
176
+ /**
177
+ * Try to parse PACE OID and parameter ID from EF.CardAccess data.
178
+ * Returns null if no PACE info is found.
179
+ */
180
+ function parsePACEInfoFromCardAccess(data) {
181
+ try {
182
+ // EF.CardAccess is a SET of SecurityInfo sequences (ASN.1 DER)
183
+ // Each SecurityInfo: SEQUENCE { OID, INTEGER (version), optional INTEGER (parameterId) }
184
+ let offset = 0;
185
+ if (data[offset] === 0x31) {
186
+ // SET tag
187
+ offset++;
188
+ const setLen = readASN1Length(data, offset);
189
+ offset = setLen.nextOffset;
190
+ }
191
+ while (offset < data.length) {
192
+ if (data[offset] !== 0x30) break; // SEQUENCE tag
193
+ offset++;
194
+ const seqLen = readASN1Length(data, offset);
195
+ const seqEnd = seqLen.nextOffset + seqLen.length;
196
+ offset = seqLen.nextOffset;
197
+
198
+ // Read OID
199
+ if (data[offset] !== 0x06) {
200
+ offset = seqEnd;
201
+ continue;
202
+ }
203
+ offset++;
204
+ const oidLen = readASN1Length(data, offset);
205
+ offset = oidLen.nextOffset;
206
+ const oidBytes = data.subarray(offset, offset + oidLen.length);
207
+ const oid = decodeOID(oidBytes);
208
+ offset += oidLen.length;
209
+
210
+ // Check if this is a PACE OID
211
+ if (oid.startsWith('0.4.0.127.0.7.2.2.4.')) {
212
+ // Read version (INTEGER)
213
+ let parameterId = 13; // default brainpoolP256r1
214
+ if (offset < seqEnd && data[offset] === 0x02) {
215
+ offset++;
216
+ const intLen = readASN1Length(data, offset);
217
+ offset = intLen.nextOffset + intLen.length;
218
+ }
219
+ // Read optional parameterId (INTEGER)
220
+ if (offset < seqEnd && data[offset] === 0x02) {
221
+ offset++;
222
+ const intLen = readASN1Length(data, offset);
223
+ offset = intLen.nextOffset;
224
+ parameterId = 0;
225
+ for (let i = 0; i < intLen.length; i++) {
226
+ parameterId = parameterId << 8 | data[offset + i];
227
+ }
228
+ }
229
+ return {
230
+ oid,
231
+ parameterId
232
+ };
233
+ }
234
+ offset = seqEnd;
235
+ }
236
+ } catch {
237
+ // Failed to parse, PACE not available
238
+ }
239
+ return null;
240
+ }
241
+ function readASN1Length(data, offset) {
242
+ const firstByte = data[offset];
243
+ if ((firstByte & 0x80) === 0) {
244
+ return {
245
+ length: firstByte,
246
+ nextOffset: offset + 1
247
+ };
248
+ }
249
+ const numBytes = firstByte & 0x7f;
250
+ let length = 0;
251
+ for (let i = 0; i < numBytes; i++) {
252
+ length = length << 8 | data[offset + 1 + i];
253
+ }
254
+ return {
255
+ length,
256
+ nextOffset: offset + 1 + numBytes
257
+ };
258
+ }
259
+ function decodeOID(bytes) {
260
+ const components = [];
261
+ components.push(Math.floor(bytes[0] / 40));
262
+ components.push(bytes[0] % 40);
263
+ let value = 0;
264
+ for (let i = 1; i < bytes.length; i++) {
265
+ value = value << 7 | bytes[i] & 0x7f;
266
+ if ((bytes[i] & 0x80) === 0) {
267
+ components.push(value);
268
+ value = 0;
269
+ }
270
+ }
271
+ return components.join('.');
272
+ }
10
273
  const eidReader = async (documentNumber, dateOfBirth, dateOfExpiry, progressCallback) => {
11
274
  let progress = 0;
12
275
  const nfcManagerCardService = new NFCManagerCardService();
13
276
  const passportService = new EIDService(nfcManagerCardService, EIDService.NORMAL_MAX_TRANSCEIVE_LENGTH, EIDService.DEFAULT_MAX_BLOCKSIZE, false, true);
14
277
  try {
15
278
  await passportService.open();
279
+ progress = 1;
280
+ if (progressCallback) {
281
+ progressCallback(progress);
282
+ }
16
283
 
17
- // Select Applet for MRTD
18
- await passportService.sendSelectApplet(false);
19
- progress = 10;
284
+ // Try to read EF.CardAccess before selecting applet to check for PACE.
285
+ // Explicitly SELECT MF first so the SFI=0x1C context is correct,
286
+ // regardless of which application the chip activates on power-on.
287
+ let hasPACESucceeded = false;
288
+ try {
289
+ await passportService.sendSelectMF();
290
+ console.debug('[EID] SELECT MF OK');
291
+ } catch (mfErr) {
292
+ const mfMsg = mfErr instanceof Error ? mfErr.message : String(mfErr);
293
+ console.debug(`[EID] SELECT MF failed (${mfMsg}), continuing with SFI read anyway`);
294
+ }
295
+ progress = 2;
20
296
  if (progressCallback) {
21
297
  progressCallback(progress);
22
298
  }
23
299
 
24
- // Check if EF_COM is available
300
+ // Read EF.CardAccess kept in a separate try/catch so we can distinguish
301
+ // "file read failure" from "PACE protocol failure" in the logs.
302
+ let cardAccessBuf = null;
25
303
  try {
26
- const efComStream = passportService.getInputStream(EIDService.EF_COM);
27
- await efComStream.init();
28
- await efComStream.read();
29
- } catch (error) {
30
- // EF_COM not available -> try to do BAC
31
- const bacKey = new BACKey(documentNumber, dateOfBirth, dateOfExpiry);
32
- await passportService.doBAC(bacKey);
304
+ const cardAccessStream = passportService.getInputStream(EIDService.EF_CARD_ACCESS);
305
+ await cardAccessStream.init();
306
+ const cardAccessLen = cardAccessStream.getLength();
307
+ const buf = Buffer.alloc(cardAccessLen);
308
+ for (let i = 0; i < cardAccessLen; i++) {
309
+ buf[i] = await cardAccessStream.read();
310
+ }
311
+ cardAccessBuf = buf;
312
+ console.debug(`[EID] EF.CardAccess read OK: ${cardAccessLen} bytes = ${cardAccessBuf.toString('hex')}`);
313
+ } catch (readErr) {
314
+ const readMsg = readErr instanceof Error ? readErr.message : String(readErr);
315
+ console.debug(`[EID] EF.CardAccess read FAILED: ${readMsg} — falling back to BAC`);
316
+ }
317
+ progress = 3;
318
+ if (progressCallback) {
319
+ progressCallback(progress);
320
+ }
321
+
322
+ // If EF.CardAccess was read successfully, attempt PACE.
323
+ if (cardAccessBuf !== null) {
324
+ try {
325
+ const paceInfo = parsePACEInfoFromCardAccess(cardAccessBuf);
326
+ if (paceInfo) {
327
+ console.debug(`[EID] PACE available: oid=${paceInfo.oid} parameterId=${paceInfo.parameterId}`);
328
+ // PACE is available - derive key from MRZ and perform PACE
329
+ const bacKey = new BACKey(documentNumber, dateOfBirth, dateOfExpiry);
330
+ const paceKey = PACEKeySpec.createMRZKey(bacKey.getKeySeedForPACE());
331
+ progress = 4;
332
+ if (progressCallback) {
333
+ progressCallback(progress);
334
+ }
335
+ await passportService.doPACE(paceKey, paceInfo.oid, paceInfo.parameterId, progressCallback);
336
+ hasPACESucceeded = true;
337
+ console.debug('[EID] PACE succeeded');
338
+ } else {
339
+ console.debug('[EID] EF.CardAccess read OK but no PACE SecurityInfo found — document does not support PACE, falling back to BAC');
340
+ }
341
+ } catch (paceError) {
342
+ // PACE protocol exchange failed — fall back to BAC
343
+ const msg = paceError instanceof Error ? paceError.message : String(paceError);
344
+ console.debug(`[EID] PACE protocol failed (${msg}), falling back to BAC`);
345
+ if (paceError instanceof Error && paceError.stack) {
346
+ console.debug('[EID] PACE error stack:', paceError.stack);
347
+ }
348
+ }
349
+ }
350
+ progress = 9;
351
+ if (progressCallback) {
352
+ progressCallback(progress);
353
+ }
354
+
355
+ // Select Applet for MRTD
356
+ await passportService.sendSelectApplet(hasPACESucceeded);
357
+ progress = 10;
358
+ if (progressCallback) {
359
+ progressCallback(progress);
360
+ }
361
+ if (!hasPACESucceeded) {
362
+ // Check if EF_COM is available
363
+ try {
364
+ const efComStream = passportService.getInputStream(EIDService.EF_COM);
365
+ await efComStream.init();
366
+ await efComStream.read();
367
+ } catch (error) {
368
+ // EF_COM not available -> try to do BAC
369
+ const bacKey = new BACKey(documentNumber, dateOfBirth, dateOfExpiry);
370
+ await passportService.doBAC(bacKey);
371
+ }
33
372
  }
34
373
  progress = 20;
35
374
  if (progressCallback) {
@@ -69,8 +408,10 @@ const eidReader = async (documentNumber, dateOfBirth, dateOfExpiry, progressCall
69
408
  const imageInputStream = await faceImageInfo.getImageInputStream();
70
409
  const buffer = Buffer.alloc(imageLength);
71
410
  await imageInputStream.readBytesWithOffset(buffer, 0, imageLength);
72
- imageAsBase64 = buffer.toString('base64');
73
- mimeType = faceImageInfo.getMimeType();
411
+ const rawMimeType = faceImageInfo.getMimeType();
412
+ const converted = convertJP2IfNeeded(buffer, rawMimeType);
413
+ imageAsBase64 = converted.base64;
414
+ mimeType = converted.mimeType;
74
415
  }
75
416
  progress = 100;
76
417
  if (progressCallback) {
@@ -6,6 +6,8 @@ import { DefaultFileSystem } from "./defaultFileSystem.js";
6
6
  import { BACAPDUSender } from "./protocol/bacAPDUSender.js";
7
7
  import { ReadBinaryAPDUSender } from "./protocol/readBinaryAPDUSender.js";
8
8
  import { BACProtocol } from "./protocol/bacProtocol.js";
9
+ import { PACEAPDUSender } from "./protocol/paceAPDUSender.js";
10
+ import { PACEProtocol } from "./protocol/paceProtocol.js";
9
11
  import { EID_CONSTANTS } from "./constants/eidConstants.js";
10
12
  export class EIDService extends AbstractMRTDCardService {
11
13
  /** Shared secret type for non-PACE key. */
@@ -175,7 +177,13 @@ export class EIDService extends AbstractMRTDCardService {
175
177
  this.shouldCheckMAC = shouldCheckMAC;
176
178
  this.isAppletSelected = false;
177
179
  this.isOpen = false;
178
- this.rootFileSystem = new DefaultFileSystem(this.readBinarySender, false);
180
+ // EF.CardAccess and EF.CardSecurity live in the MF (Master File), outside
181
+ // the ICAO applet. They must be read via SFI-based READ BINARY (no SELECT
182
+ // needed). Without SFI, the implementation falls back to SELECT-by-FID
183
+ // which many passport chips reject at MF level before applet selection,
184
+ // causing PACE to be falsely reported as "not available".
185
+ const rootFidToSFI = new Map([[EID_CONSTANTS.EF_CARD_ACCESS, EID_CONSTANTS.SFI_CARD_ACCESS], [EID_CONSTANTS.EF_CARD_SECURITY, EID_CONSTANTS.SFI_CARD_SECURITY]]);
186
+ this.rootFileSystem = new DefaultFileSystem(this.readBinarySender, true, rootFidToSFI);
179
187
  this.appletFileSystem = new DefaultFileSystem(this.readBinarySender, isSFIEnabled);
180
188
  }
181
189
  async open() {
@@ -209,6 +217,22 @@ export class EIDService extends AbstractMRTDCardService {
209
217
  this.appletFileSystem.setWrapper(this.wrapper);
210
218
  return bacResult;
211
219
  }
220
+
221
+ /**
222
+ * Performs the PACE protocol.
223
+ *
224
+ * @param accessKey the access key (MRZ or CAN based)
225
+ * @param oid the PACE OID string from EF.CardAccess
226
+ * @param parameterId the standard domain parameter ID (e.g. 13 for brainpoolP256r1)
227
+ */
228
+ async doPACE(accessKey, oid, parameterId, progressCallback) {
229
+ const paceSender = new PACEAPDUSender(this.service);
230
+ const paceProtocol = new PACEProtocol(paceSender, this.wrapper, EIDService.NORMAL_MAX_TRANSCEIVE_LENGTH, this.maxTransceiveLengthForSecureMessaging, this.shouldCheckMAC);
231
+ const paceResult = await paceProtocol.doPACE(accessKey, oid, parameterId, progressCallback);
232
+ this.wrapper = paceResult.getWrapper();
233
+ this.appletFileSystem.setWrapper(this.wrapper);
234
+ return paceResult;
235
+ }
212
236
  async close() {
213
237
  try {
214
238
  await this.service.close();