@trustchex/react-native-sdk 1.487.0 → 1.488.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 (66) hide show
  1. package/lib/module/Shared/Components/EIDScanner.js +95 -36
  2. package/lib/module/Shared/Components/IdentityDocumentCamera.constants.js +9 -1
  3. package/lib/module/Shared/EIDReader/cardFileInputStream.js +25 -2
  4. package/lib/module/Shared/EIDReader/defaultFileSystem.js +56 -6
  5. package/lib/module/Shared/EIDReader/eidReader.js +124 -14
  6. package/lib/module/Shared/EIDReader/lds/icao/dg11File.js +23 -6
  7. package/lib/module/Shared/EIDReader/nfcManagerCardService.js +79 -9
  8. package/lib/module/Shared/EIDReader/protocol/paceDH.js +101 -0
  9. package/lib/module/Shared/EIDReader/protocol/paceErrors.js +34 -0
  10. package/lib/module/Shared/EIDReader/protocol/paceProtocol.js +53 -4
  11. package/lib/module/Shared/EIDReader/protocol/readBinaryAPDUSender.js +7 -3
  12. package/lib/module/Shared/Libs/country-display.utils.js +13 -1
  13. package/lib/module/Shared/Libs/diagnosticReport.js +24 -1
  14. package/lib/module/Shared/Libs/diagnostics.js +30 -8
  15. package/lib/module/Shared/Libs/mrz.utils.js +22 -5
  16. package/lib/module/Shared/Libs/mrzTransliteration.js +14 -1
  17. package/lib/module/Translation/Resources/en.js +12 -0
  18. package/lib/module/Translation/Resources/tr.js +12 -0
  19. package/lib/module/version.js +1 -1
  20. package/lib/typescript/src/Shared/Components/EIDScanner.d.ts.map +1 -1
  21. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.constants.d.ts.map +1 -1
  22. package/lib/typescript/src/Shared/EIDReader/cardFileInputStream.d.ts +3 -0
  23. package/lib/typescript/src/Shared/EIDReader/cardFileInputStream.d.ts.map +1 -1
  24. package/lib/typescript/src/Shared/EIDReader/defaultFileSystem.d.ts +4 -0
  25. package/lib/typescript/src/Shared/EIDReader/defaultFileSystem.d.ts.map +1 -1
  26. package/lib/typescript/src/Shared/EIDReader/eidReader.d.ts.map +1 -1
  27. package/lib/typescript/src/Shared/EIDReader/lds/icao/dg11File.d.ts.map +1 -1
  28. package/lib/typescript/src/Shared/EIDReader/nfcManagerCardService.d.ts +9 -1
  29. package/lib/typescript/src/Shared/EIDReader/nfcManagerCardService.d.ts.map +1 -1
  30. package/lib/typescript/src/Shared/EIDReader/protocol/paceDH.d.ts +56 -0
  31. package/lib/typescript/src/Shared/EIDReader/protocol/paceDH.d.ts.map +1 -0
  32. package/lib/typescript/src/Shared/EIDReader/protocol/paceErrors.d.ts +26 -0
  33. package/lib/typescript/src/Shared/EIDReader/protocol/paceErrors.d.ts.map +1 -0
  34. package/lib/typescript/src/Shared/EIDReader/protocol/paceProtocol.d.ts.map +1 -1
  35. package/lib/typescript/src/Shared/EIDReader/protocol/readBinaryAPDUSender.d.ts.map +1 -1
  36. package/lib/typescript/src/Shared/Libs/country-display.utils.d.ts.map +1 -1
  37. package/lib/typescript/src/Shared/Libs/diagnosticReport.d.ts.map +1 -1
  38. package/lib/typescript/src/Shared/Libs/diagnostics.d.ts +9 -1
  39. package/lib/typescript/src/Shared/Libs/diagnostics.d.ts.map +1 -1
  40. package/lib/typescript/src/Shared/Libs/mrz.utils.d.ts.map +1 -1
  41. package/lib/typescript/src/Shared/Libs/mrzTransliteration.d.ts.map +1 -1
  42. package/lib/typescript/src/Translation/Resources/en.d.ts +12 -0
  43. package/lib/typescript/src/Translation/Resources/en.d.ts.map +1 -1
  44. package/lib/typescript/src/Translation/Resources/tr.d.ts +12 -0
  45. package/lib/typescript/src/Translation/Resources/tr.d.ts.map +1 -1
  46. package/lib/typescript/src/version.d.ts +1 -1
  47. package/package.json +1 -1
  48. package/src/Shared/Components/EIDScanner.tsx +148 -68
  49. package/src/Shared/Components/IdentityDocumentCamera.constants.ts +9 -1
  50. package/src/Shared/EIDReader/cardFileInputStream.ts +30 -6
  51. package/src/Shared/EIDReader/defaultFileSystem.ts +75 -17
  52. package/src/Shared/EIDReader/eidReader.ts +154 -21
  53. package/src/Shared/EIDReader/lds/icao/dg11File.ts +28 -13
  54. package/src/Shared/EIDReader/nfcManagerCardService.ts +91 -13
  55. package/src/Shared/EIDReader/protocol/paceDH.ts +183 -0
  56. package/src/Shared/EIDReader/protocol/paceErrors.ts +41 -0
  57. package/src/Shared/EIDReader/protocol/paceProtocol.ts +72 -6
  58. package/src/Shared/EIDReader/protocol/readBinaryAPDUSender.ts +7 -4
  59. package/src/Shared/Libs/country-display.utils.ts +12 -1
  60. package/src/Shared/Libs/diagnosticReport.ts +32 -1
  61. package/src/Shared/Libs/diagnostics.ts +38 -14
  62. package/src/Shared/Libs/mrz.utils.ts +27 -6
  63. package/src/Shared/Libs/mrzTransliteration.ts +16 -3
  64. package/src/Translation/Resources/en.ts +19 -0
  65. package/src/Translation/Resources/tr.ts +19 -0
  66. package/src/version.ts +1 -1
@@ -4,6 +4,24 @@ import NFCManager, { NfcTech } from 'react-native-nfc-manager';
4
4
  import { CardService } from "./cardService.js";
5
5
  import { ResponseAPDU } from "./smartcards/responseAPDU.js";
6
6
  import { Platform } from 'react-native';
7
+
8
+ // iOS CoreNFC "Tag connection lost" (NFCError 100) is a well-known issue whose
9
+ // likelihood scales with the number of APDU round-trips in a session — a large
10
+ // file like DG2 (face image) needs dozens, so the tag is regularly lost
11
+ // mid-read. The session itself can be re-established with
12
+ // restartTechnologyRequestIOS() WITHOUT losing our PACE/BAC secure-messaging
13
+ // state (keys + send-sequence-counter live in our JS wrapper), so a lost-tag
14
+ // transmit can recover by restarting the session and re-issuing the SAME APDU
15
+ // (idempotent — a failed read advances no SM state, and the caller rewinds the
16
+ // SSC). See react-native-nfc-manager#358.
17
+ const IOS_MAX_RECONNECTS = 3;
18
+ const sleep = ms => new Promise(r => setTimeout(r, ms));
19
+ const isTagLostError = e => {
20
+ const msg = (e instanceof Error ? e.message : typeof e === 'string' ? e : '').toLowerCase();
21
+ // The native bridge surfaces these for a dropped/timed-out CoreNFC session;
22
+ // an empty message is also treated as a lost tag (the observed DG2 failure).
23
+ return msg === '' || msg.includes('lost') || msg.includes('not connected') || msg.includes('tag was') || msg.includes('session') || msg.includes('invalidated') || msg.includes('timeout') || msg.includes('100');
24
+ };
7
25
  export class NFCManagerCardService extends CardService {
8
26
  isOpened = false;
9
27
  async open() {
@@ -18,21 +36,73 @@ export class NFCManagerCardService extends CardService {
18
36
  getIsOpen() {
19
37
  return this.isOpened;
20
38
  }
39
+
40
+ // Session-restart recovery is SAFE only for idempotent data-group reads, NOT
41
+ // during PACE/BAC: the authentication state machine lives on the chip, so
42
+ // restarting the CoreNFC session mid-handshake destroys it ("Not even
43
+ // registered"). The reader turns this on AFTER authentication succeeds.
44
+ sessionRestartEnabled = false;
45
+ setSessionRestartEnabled(enabled) {
46
+ this.sessionRestartEnabled = enabled;
47
+ }
21
48
  async transmit(commandAPDU) {
22
- let response;
23
- if (Platform.OS === 'ios') {
24
- const iosResponse = await NFCManager.sendCommandAPDUIOS(Array.from(commandAPDU.getBytes()));
25
- response = [...iosResponse.response, iosResponse.sw1, iosResponse.sw2];
26
- } else {
27
- response = await NFCManager.transceive(Array.from(commandAPDU.getBytes()));
49
+ const bytes = Array.from(commandAPDU.getBytes());
50
+ if (Platform.OS !== 'ios') {
51
+ return new ResponseAPDU(await NFCManager.transceive(bytes));
52
+ }
53
+
54
+ // During auth (restart disabled) just send once — never restart mid-PACE.
55
+ if (!this.sessionRestartEnabled) {
56
+ const r = await NFCManager.sendCommandAPDUIOS(bytes);
57
+ return new ResponseAPDU([...r.response, r.sw1, r.sw2]);
58
+ }
59
+
60
+ // Post-auth data reads: on a lost-tag error restart the CoreNFC session and
61
+ // retry the same command (secure-messaging state is preserved by the caller).
62
+ let lastError;
63
+ for (let attempt = 0; attempt <= IOS_MAX_RECONNECTS; attempt++) {
64
+ try {
65
+ const r = await NFCManager.sendCommandAPDUIOS(bytes);
66
+ return new ResponseAPDU([...r.response, r.sw1, r.sw2]);
67
+ } catch (e) {
68
+ lastError = e;
69
+ if (attempt >= IOS_MAX_RECONNECTS || !isTagLostError(e)) {
70
+ throw e;
71
+ }
72
+ // Re-establish the session, then pause briefly so the RF field settles
73
+ // before re-issuing (immediate re-send after a drop usually fails again).
74
+ try {
75
+ await NFCManager.restartTechnologyRequestIOS();
76
+ } catch {
77
+ // If restart itself fails the session is unrecoverable for now —
78
+ // surface the original lost-tag error to the caller.
79
+ throw e;
80
+ }
81
+ await sleep(150 * (attempt + 1));
82
+ }
28
83
  }
29
- return new ResponseAPDU(response);
84
+ throw lastError;
30
85
  }
31
86
  getATR() {
32
87
  throw new Error('Method not implemented.');
33
88
  }
34
- getIsConnectionLost(_error) {
35
- return !this.isOpened;
89
+ getIsConnectionLost(error) {
90
+ // Lost if the session was closed, or the error looks like a dropped tag.
91
+ return !this.isOpened || isTagLostError(error);
92
+ }
93
+
94
+ /** True if the error looks like a dropped/timed-out CoreNFC tag connection
95
+ * (as opposed to a real APDU/crypto failure). */
96
+ isTagLost(error) {
97
+ return isTagLostError(error);
98
+ }
99
+
100
+ /** Re-establish the CoreNFC session (iOS) so a fresh handshake can be retried
101
+ * after a tag drop. No-op on Android. Best-effort. */
102
+ async restartSession() {
103
+ if (Platform.OS === 'ios') {
104
+ await NFCManager.restartTechnologyRequestIOS();
105
+ }
36
106
  }
37
107
  async close() {
38
108
  await NFCManager.cancelTechnologyRequest();
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * DH (modular exponentiation, non-EC) primitives for PACE Generic Mapping.
5
+ *
6
+ * Modern ePassports/eIDs use ECDH almost universally; a minority of documents
7
+ * negotiate classic DH PACE. This module provides the DH math as pure,
8
+ * unit-testable functions so it can be verified against the ICAO 9303 Part 11
9
+ * worked example before it ever touches a chip. It is deliberately separate from
10
+ * the EC path in paceProtocol.ts — the working ECDH handshake is not modified.
11
+ *
12
+ * References: ICAO Doc 9303 Part 11 §4.4.3.2 (Generic Mapping), BSI TR-03110
13
+ * Part 3 §3.3.1, RFC 5114 (standardized MODP groups), TR-03110 Table A.1
14
+ * (standardized domain parameter IDs 0–7).
15
+ */
16
+
17
+ /** A DH group: safe-prime modulus p, generator g, and subgroup order q. */
18
+
19
+ const hx = s => BigInt('0x' + s.replace(/\s+/g, ''));
20
+
21
+ /**
22
+ * Standardized DH domain parameters by TR-03110 parameter ID.
23
+ * 0 → RFC 5114 §2.1 1024-bit MODP Group with 160-bit Prime Order Subgroup
24
+ * 1 → RFC 5114 §2.2 2048-bit MODP Group with 224-bit Prime Order Subgroup
25
+ * 2 → RFC 5114 §2.3 2048-bit MODP Group with 256-bit Prime Order Subgroup
26
+ * (IDs 3–7 are reserved/unused in practice; EC params start at 8.)
27
+ */
28
+ export const DH_GROUPS = {
29
+ // RFC 5114 §2.1 — 1024-bit MODP, 160-bit subgroup (the ICAO worked-example group).
30
+ 0: {
31
+ p: hx('B10B8F96 A080E01D DE92DE5E AE5D54EC 52C99FBC FB06A3C6' + '9A6A9DCA 52D23B61 6073E286 75A23D18 9838EF1E 2EE652C0' + '13ECB4AE A9061123 24975C3C D49B83BF ACCBDD7D 90C4BD70' + '98488E9C 219A7372 4EFFD6FA E5644738 FAA31A4F F55BCCC0' + 'A151AF5F 0DC8B4BD 45BF37DF 365C1A65 E68CFDA7 6D4DA708' + 'DF1FB2BC 2E4A4371'),
32
+ g: hx('A4D1CBD5 C3FD3412 6765A442 EFB99905 F8104DD2 58AC507F' + 'D6406CFF 14266D31 266FEA1E 5C41564B 777E690F 5504F213' + '160217B4 B01B886A 5E91547F 9E2749F4 D7FBD7D3 B9A92EE1' + '909D0D22 63F80A76 A6A24C08 7A091F53 1DBF0A01 69B6A28A' + 'D662A4D1 8E73AFA3 2D779D59 18D08BC8 858F4DCE F97C2A24' + '855E6EEB 22B3B2E5'),
33
+ q: hx('F518AA87 81A8DF27 8ABA4E7D 64B7CB9D 49462353')
34
+ },
35
+ // RFC 5114 §2.2 — 2048-bit MODP, 224-bit subgroup.
36
+ 1: {
37
+ p: hx('AD107E1E 9123A9D0 D660FAA7 9559C51F A20D64E5 683B9FD1' + 'B54B1597 B61D0A75 E6FA141D F95A56DB AF9A3C40 7BA1DF15' + 'EB3D688A 309C180E 1DE6B85A 1274A0A6 6D3F8152 AD6AC212' + '9037C9ED EFDA4DF8 D91E8FEF 55B7394B 7AD5B7D0 B6C12207' + 'C9F98D11 ED34DBF6 C6BA0B2C 8BBC27BE 6A00E0A0 B9C49708' + 'B3BF8A31 70918836 81286130 BC8985DB 1602E714 415D9330' + '278273C7 DE31EFDC 7310F712 1FD5A074 15987D9A DC0A486D' + 'CDF93ACC 44328387 315D75E1 98C641A4 80CD86A1 B9E587E8' + 'BE60E69C C928B2B9 C52172E4 13042E9B 23F10B0E 16E79763' + 'C9B53DCF 4BA80A29 E3FB73C1 6B8E75B9 7EF363E2 FFA31F71' + 'CF9DE538 4E71B81C 0AC4DFFE 0C10E64F'),
38
+ g: hx('AC4032EF 4F2D9AE3 9DF30B5C 8FFDAC50 6CDEBE7B 89998CAF' + '74866A08 CFE4FFE3 A6824A4E 10B9A6F0 DD921F01 A70C4AFA' + 'AB739D77 00C29F52 C57DB17C 620A8652 BE5E9001 A8D66AD7' + 'C1766910 1999024A F4D02727 5AC1348B B8A762D0 521BC98A' + 'E2471504 22EA1ED4 09939D54 DA7460CD B5F6C6B2 50717CBE' + 'F180EB34 118E98D1 19529A45 D6F83456 6E3025E3 16A330EF' + 'BB77A86F 0C1AB15B 051AE3D4 28C8F8AC B70A8137 150B8EEB' + '10E183ED D19963DD D9E263E4 770589EF 6AA21E7F 5F2FF381' + 'B539CCE3 409D13CD 566AFBB4 8D6C0191 81E1BCFE 94B30269' + 'EDFE72FE 9B6AA4BD 7B5A0F1C 71CFFF4C 19C418E1 F6EC0179' + '81BC087F 2A7065B3 84B890D3 191F2BFA'),
39
+ q: hx('801C0D34 C58D93FE 99717710 1F80535A 4738CEBC BF389A99' + 'B36371EB')
40
+ },
41
+ // RFC 5114 §2.3 — 2048-bit MODP, 256-bit subgroup.
42
+ 2: {
43
+ p: hx('87A8E61D B4B6663C FFBBD19C 65195999 8CEEF608 660DD0F2' + '5D2CEED4 435E3B00 E00DF8F1 D61957D4 FAF7DF45 61B2AA30' + '16C3D911 34096FAA 3BF4296D 830E9A7C 209E0C64 97517ABD' + '5A8A9D30 6BCF67ED 91F9E672 5B4758C0 22E0B1EF 4275BF7B' + '6C5BFC11 D45F9088 B941F54E B1E59BB8 BC39A0BF 12307F5C' + '4FDB70C5 81B23F76 B63ACAE1 CAA6B790 2D525267 35488A0E' + 'F13C6D9A 51BFA4AB 3AD83477 96524D8E F6A167B5 A41825D9' + '67E144E5 14056425 1CCACB83 E6B486F6 B3CA3F79 71506026' + 'C0B857F6 89962856 DED4010A BD0BE621 C3A3960A 54E710C3' + '75F26375 D7014103 A4B54330 C198AF12 6116D227 6E11715F' + '693877FA D7EF09CA DB094AE9 1E1A1597'),
44
+ g: hx('3FB32C9B 73134D0B 2E775066 60EDBD48 4CA7B18F 21EF2054' + '07F4793A 1A0BA125 10DBC150 77BE463F FF4FED4A AC0BB555' + 'BE3A6C1B 0C6B47B1 BC3773BF 7E8C6F62 901228F8 C28CBB18' + 'A55AE313 41000A65 0196F931 C77A57F2 DDF463E5 E9EC144B' + '777DE62A AAB8A862 8AC376D2 82D6ED38 64E67982 428EBC83' + '1D14348F 6F2F9193 B5045AF2 767164E1 DFC967C1 FB3F2E55' + 'A4BD1BFF E83B9C80 D052B985 D182EA0A DB2A3B73 13D3FE14' + 'C8484B1E 052588B9 B7D2BBD2 DF016199 ECD06E15 57CD0915' + 'B3353BBB 64E0EC37 7FD02837 0DF92B52 C7891428 CDC67EB6' + '184B523D 1DB246C3 2F630784 90F00EF8 D647D148 D4795451' + '5E2327CF EF98C582 664B4C0F 6CC41659'),
45
+ q: hx('8CF83642 A709A097 B4479976 40129DA2 99B1A47D 1EB3750B' + 'A308B0FE 64F5FBD3')
46
+ }
47
+ };
48
+
49
+ /**
50
+ * PACE DH Generic Mapping (ICAO 9303 Part 11 §4.4.3.2):
51
+ * shared secret h = PK_PICC ^ SK_PCD mod p
52
+ * mapped generator g~ = g^s · h mod p
53
+ * where s is the decrypted PICC nonce (a big-endian integer).
54
+ *
55
+ * Pure function over the group + the three inputs, so it can be checked against
56
+ * the ICAO worked example. `pkPicc` is the PICC mapping public key; `skPcd` the
57
+ * PCD mapping private key; `nonce` the decrypted nonce s.
58
+ */
59
+ export const dhGenericMapping = (group, skPcd, pkPicc, nonce) => {
60
+ const sharedH = modPow(pkPicc, skPcd, group.p);
61
+ const gPowS = modPow(group.g, nonce, group.p);
62
+ const mappedGenerator = gPowS * sharedH % group.p;
63
+ return {
64
+ sharedH,
65
+ mappedGenerator
66
+ };
67
+ };
68
+
69
+ /**
70
+ * DH key agreement with a mapped generator (PACE step 3):
71
+ * PK = g~ ^ SK mod p (public key to send)
72
+ * K = PK_PICC ^ SK mod p (shared secret)
73
+ */
74
+ export const dhPublicKey = (group, mappedGenerator, sk) => modPow(mappedGenerator, sk, group.p);
75
+ export const dhSharedSecret = (group, pkPicc, sk) => modPow(pkPicc, sk, group.p);
76
+
77
+ /** Square-and-multiply modular exponentiation (constant-shape, bigint). */
78
+ export function modPow(base, exp, mod) {
79
+ if (mod === 1n) return 0n;
80
+ let result = 1n;
81
+ base = (base % mod + mod) % mod;
82
+ let e = exp;
83
+ while (e > 0n) {
84
+ if (e & 1n) result = result * base % mod;
85
+ e >>= 1n;
86
+ base = base * base % mod;
87
+ }
88
+ return result;
89
+ }
90
+
91
+ /** Fixed-width big-endian byte encoding of a DH value (left-padded to p's size). */
92
+ export const dhEncode = (value, group) => {
93
+ const byteLen = Math.ceil(group.p.toString(16).length / 2);
94
+ let h = value.toString(16);
95
+ if (h.length % 2) h = '0' + h;
96
+ const buf = Buffer.from(h, 'hex');
97
+ if (buf.length === byteLen) return buf;
98
+ const out = Buffer.alloc(byteLen);
99
+ buf.copy(out, byteLen - buf.length);
100
+ return out;
101
+ };
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Typed PACE errors.
5
+ *
6
+ * The point is to let the caller distinguish a PACE CAPABILITY gap (this build
7
+ * cannot run the variant the chip advertises — e.g. DH agreement, CAM mapping,
8
+ * an unregistered curve) from a RUNTIME failure (wrong key, tag lost, APDU
9
+ * error). Both fall back to BAC, but the diagnostics reason must differ so we
10
+ * can tell "we don't support this document's PACE" from "the read went wrong".
11
+ */
12
+
13
+ /** Why a PACE attempt could not even be run by this implementation. */
14
+
15
+ // cipher not implemented for the chosen mapping
16
+
17
+ /**
18
+ * Thrown when the chip advertises a PACE variant this build cannot perform.
19
+ * Carries a coarse, non-PII reason that maps directly to a diagnostics fallback
20
+ * reason (`pace_unsupported_<reason>`), so support can see exactly which
21
+ * capability was missing for a given document.
22
+ */
23
+ export class PACEUnsupportedError extends Error {
24
+ paceUnsupported = true;
25
+ constructor(reason, detail) {
26
+ super(`PACE variant not supported (${reason}): ${detail}`);
27
+ this.name = 'PACEUnsupportedError';
28
+ this.reason = reason;
29
+ }
30
+ }
31
+
32
+ /** Type guard usable across module boundaries (instanceof is unreliable after
33
+ * transpilation/bundling, so we also check the marker flag). */
34
+ export const isPACEUnsupportedError = e => e instanceof PACEUnsupportedError || typeof e === 'object' && e !== null && e.paceUnsupported === true;
@@ -3,6 +3,7 @@
3
3
  import { AESSecureMessagingWrapper } from "../aesSecureMessagingWrapper.js";
4
4
  import { TripleDesSecureMessagingWrapper } from "../tripleDesSecureMessagingWrapper.js";
5
5
  import { PACEInfo } from "../paceInfo.js";
6
+ import { PACEUnsupportedError } from "./paceErrors.js";
6
7
  import { PACEResult } from "./paceResult.js";
7
8
  import { PACEKeySpec } from "../paceKeySpec.js";
8
9
  import { EID_CONSTANTS } from "../constants/eidConstants.js";
@@ -18,6 +19,51 @@ import { ec as EC } from 'elliptic';
18
19
  const _elliptic = require('elliptic');
19
20
  const _hash = require('hash.js');
20
21
  const PresetCurve = _elliptic.curves.PresetCurve;
22
+
23
+ // brainpoolP192r1 (RFC 5639 §3.1) — PACE standardized domain parameter ID 9.
24
+ if (!_elliptic.curves.brainpoolP192r1) {
25
+ _elliptic.curves.brainpoolP192r1 = new PresetCurve({
26
+ type: 'short',
27
+ prime: null,
28
+ p: 'c302f41d932a36cda7a3463093d18db78fce476de1a86297',
29
+ a: '6a91174076b1e0e19c39c031fe8685c1cae040e5c69a28ef',
30
+ b: '469a28ef7c28cca3dc721d044f4496bcca7ef4146fbf25c9',
31
+ n: 'c302f41d932a36cda7a3462f9e9e916b5be8f1029ac4acc1',
32
+ hash: _hash.sha256,
33
+ gRed: false,
34
+ g: ['c0a0647eaab6a48753b033c56cb0f0900a2f5c4853375fd6', '14b690866abd5bb88b5f4828c1490002e6773fa2fa299b8f']
35
+ });
36
+ }
37
+
38
+ // brainpoolP224r1 (RFC 5639 §3.2) — PACE standardized domain parameter ID 11.
39
+ if (!_elliptic.curves.brainpoolP224r1) {
40
+ _elliptic.curves.brainpoolP224r1 = new PresetCurve({
41
+ type: 'short',
42
+ prime: null,
43
+ p: 'd7c134aa264366862a18302575d1d787b09f075797da89f57ec8c0ff',
44
+ a: '68a5e62ca9ce6c1c299803a6c1530b514e182ad8b0042a59cad29f43',
45
+ b: '2580f63ccfe44138870713b1a92369e33e2135d266dbb372386c400b',
46
+ n: 'd7c134aa264366862a18302575d0fb98d116bc4b6ddebca3a5a7939f',
47
+ hash: _hash.sha256,
48
+ gRed: false,
49
+ g: ['0d9029ad2c7e5cf4340823b2a87dc68c9e4ce3174c1e6efdee12c07d', '58aa56f772c0726f24c6b89e4ecdac24354b9e99caa3f6d3761402cd']
50
+ });
51
+ }
52
+
53
+ // brainpoolP320r1 (RFC 5639 §3.4) — PACE standardized domain parameter ID 14.
54
+ if (!_elliptic.curves.brainpoolP320r1) {
55
+ _elliptic.curves.brainpoolP320r1 = new PresetCurve({
56
+ type: 'short',
57
+ prime: null,
58
+ p: 'd35e472036bc4fb7e13c785ed201e065f98fcfa6f6f40def4f92b9ec7893ec28fcd412b1f1b32e27',
59
+ a: '3ee30b568fbab0f883ccebd46d3f3bb8a2a73513f5eb79da66190eb085ffa9f492f375a97d860eb4',
60
+ b: '520883949dfdbc42d3ad198640688a6fe13f41349554b49acc31dccd884539816f5eb4ac8fb1f1a6',
61
+ n: 'd35e472036bc4fb7e13c785ed201e065f98fcfa5b68f12a32d482ec7ee8658e98691555b44c59311',
62
+ hash: _hash.sha384,
63
+ gRed: false,
64
+ g: ['43bd7e9afb53d8b85289bcc48ee5bfe6f20137d10a087eb6e7871e2a10a599c710af8d0d39e20611', '14fdd05545ec1cc8ab4093247f77275e0743ffed117182eaa9c77877aaac6ac7d35245d1692e8ee1']
65
+ });
66
+ }
21
67
  if (!_elliptic.curves.brainpoolP256r1) {
22
68
  _elliptic.curves.brainpoolP256r1 = new PresetCurve({
23
69
  type: 'short',
@@ -91,10 +137,13 @@ export class PACEProtocol {
91
137
  const digestAlg = PACEInfo.toDigestAlgorithm(oid);
92
138
  const keyLength = PACEInfo.toKeyLength(oid);
93
139
  if (agreementAlg !== 'ECDH') {
94
- throw new Error(`Only ECDH key agreement is supported, found: ${agreementAlg}`);
140
+ // DH-based PACE is rare in modern documents; surface it as a typed
141
+ // capability gap so the caller falls back to BAC with a precise reason.
142
+ throw new PACEUnsupportedError('agreement', `key agreement ${agreementAlg} (only ECDH implemented)`);
95
143
  }
96
144
  if (mappingType !== 'GM' && mappingType !== 'IM') {
97
- throw new Error(`Unsupported PACE mapping type: ${mappingType}`);
145
+ // CAM (Chip Authentication Mapping) is not implemented.
146
+ throw new PACEUnsupportedError('mapping', `mapping type ${mappingType} (only GM/IM implemented)`);
98
147
  }
99
148
 
100
149
  // Derive static PACE key K_pi from the access key
@@ -252,7 +301,7 @@ export class PACEProtocol {
252
301
  */
253
302
  async doPACEStep2IM(curveName, piccNonceHex, cipherAlg) {
254
303
  if (cipherAlg !== 'AES') {
255
- throw new Error(`PACE IM: Only AES cipher is supported in this implementation, found: ${cipherAlg}`);
304
+ throw new PACEUnsupportedError('cipher', `IM mapping requires AES, found ${cipherAlg}`);
256
305
  }
257
306
  const curve = new EC(curveName);
258
307
 
@@ -649,7 +698,7 @@ export class PACEProtocol {
649
698
  return 'p521';
650
699
  // NIST P-521
651
700
  default:
652
- throw new Error(`Unsupported domain parameter ID: ${parameterId}`);
701
+ throw new PACEUnsupportedError('curve', `domain parameter ID ${parameterId}`);
653
702
  }
654
703
  }
655
704
  }
@@ -59,10 +59,14 @@ export class ReadBinaryAPDUSender {
59
59
  try {
60
60
  responseAPDU = await this.secureMessagingSender.transmit(wrapper, commandAPDU);
61
61
  } catch (error) {
62
- if (this.service.getIsConnectionLost(error)) {
63
- throw error;
64
- }
62
+ // A transient transceive failure (iOS RF drop during a large DG2 read)
63
+ // used to be swallowed here, leaving responseAPDU undefined — which then
64
+ // crashed downstream as a misleading "Could not read file" that the retry
65
+ // layer couldn't recognise. Rethrow so the block-read retry/backoff in
66
+ // CardFileInputStream can re-issue the SAME block (idempotent), and the
67
+ // file system can downgrade the read size if needed.
65
68
  console.debug('Exception during READ BINARY', error);
69
+ throw error;
66
70
  }
67
71
  const responseData = ReadBinaryAPDUSender.getResponseData(responseAPDU, isTLVEncodedOffsetNeeded);
68
72
  ReadBinaryAPDUSender.checkStatusWordAfterFileOperation(commandAPDU, responseAPDU);
@@ -5,12 +5,24 @@ import { UnicodeCountry } from "../EIDReader/data/unicodeCountry.js";
5
5
  const COUNTRY_NAME_OVERRIDES = {
6
6
  TUR: 'Türkiye'
7
7
  };
8
+
9
+ // ICAO/MRZ issuing-state codes that are not ISO-3166 alpha-3 and must be mapped
10
+ // before lookup. "D" is Germany's historical 1-letter MRZ code (ICAO 9303
11
+ // Part 3 §C), surfaced filler-padded as "D<<" by the chip getters.
12
+ const MRZ_CODE_ALIASES = {
13
+ D: 'DEU'
14
+ };
8
15
  const findCountryByAlpha3 = alpha3Code => {
9
16
  return [...UnicodeCountry.VALUES, ...ISOCountry.VALUES].find(country => country.toAlpha3Code() === alpha3Code);
10
17
  };
11
18
  export const getLocalizedCountryName = (alpha3Code, _language) => {
12
19
  if (!alpha3Code) return '';
13
- const normalizedCode = alpha3Code.trim().toUpperCase();
20
+
21
+ // Strip MRZ filler (the chip pads short codes, e.g. German "D" → "D<<") and
22
+ // map non-ISO MRZ codes to their alpha-3 before lookup.
23
+ const stripped = alpha3Code.replace(/</g, '').trim().toUpperCase();
24
+ if (!stripped) return '';
25
+ const normalizedCode = MRZ_CODE_ALIASES[stripped] ?? stripped;
14
26
  const overrideName = COUNTRY_NAME_OVERRIDES[normalizedCode];
15
27
  if (overrideName) {
16
28
  return overrideName;
@@ -24,6 +24,26 @@ const icaoCheckDigit = str => {
24
24
  }
25
25
  return String(sum);
26
26
  };
27
+
28
+ /**
29
+ * Reportable NFC error code, e.g. `NFC-AUTH-6982`. MUST match the code shown to
30
+ * the user by EIDScanner (built in eidReader.buildNfcErrorCode) so a user who
31
+ * quotes "NFC-AUTH-6982" can be matched verbatim against their diagnostics.json.
32
+ * Local copy of the category→prefix map (eidReader pulls in native NFC deps).
33
+ */
34
+ const NFC_CATEGORY_CODE = {
35
+ user_cancelled: 'CANCEL',
36
+ tag_lost: 'TAGLOST',
37
+ timeout: 'TIMEOUT',
38
+ unsupported: 'UNSUPPORTED',
39
+ not_enabled: 'DISABLED',
40
+ auth_failed: 'AUTH',
41
+ reading_error: 'READ'
42
+ };
43
+ const nfcErrorCode = (category, statusWord) => {
44
+ const part = NFC_CATEGORY_CODE[category ?? ''] ?? 'READ';
45
+ return statusWord ? `NFC-${part}-${statusWord}` : `NFC-${part}`;
46
+ };
27
47
  /** Compute per-field check-digit pass/fail for a TD1 MRZ (best-effort). */
28
48
  const computeTd1CheckDigits = mrzText => {
29
49
  if (!mrzText) return undefined;
@@ -120,7 +140,10 @@ export const buildDiagnosticReport = params => {
120
140
 
121
141
  // --- compact email body (always under MAX_BODY_BYTES) ---
122
142
  const nfc = diag.nfc;
123
- const nfcLine = nfc.attempted ? `${nfc.authProtocol}${nfc.paceFellBackToBac ? '→BAC' : ''} · ${nfc.succeeded ? 'success' : `failed at ${nfc.failureStep ?? '?'} (${nfc.errorCategory ?? 'error'})`}` : 'not attempted';
143
+ // Status word of the failing step (if any), used to build the reportable code.
144
+ const failingSw = nfc.failureStep ? nfc.steps.find(s => s.step === nfc.failureStep)?.sw : undefined;
145
+ const nfcCode = nfcErrorCode(nfc.errorCategory, failingSw);
146
+ const nfcLine = nfc.attempted ? `${nfc.authProtocol}${nfc.paceFellBackToBac ? '→BAC' : ''} · ${nfc.succeeded ? 'success' : `failed at ${nfc.failureStep ?? '?'} (${nfc.errorCategory ?? 'error'}) [${nfcCode}]`}` : 'not attempted';
124
147
  const checksLine = checkDigits ? Object.entries(checkDigits).map(([k, v]) => `${k}:${v}`).join(' ') : 'n/a';
125
148
  const body = ['TRUSTCHEX SCAN REPORT', `Device: ${device.brand} ${device.model} · ${device.systemName} ${device.systemVersion}${device.apiLevel ? ` (API ${device.apiLevel})` : ''}`, `SDK: ${sdkVersion} (build ${device.buildNumber}) · ${device.manufacturer} · ${device.deviceId}`, `Document: ${scanDataObj.documentType ?? 'unknown'} · source: ${scanDataObj.dataSource ?? 'unknown'}`, `MRZ: ${diag.mrz.reachedStable ? 'stable' : 'NOT stable'} after ${diag.mrz.framesProcessed} frames · checks: ${checksLine}`, `Camera: ${diag.camera.frames} frames · brightness ${diag.camera.avgBrightness} (low:${yesNo(diag.camera.hadLowBrightness)} blur:${yesNo(diag.camera.hadBlurryFrames)})`, `NFC: ${nfcLine}`, sessionId ? `Session: ${sessionId}` : '', '', 'Describe the issue here:', '', '', '— Full diagnostics and document images are attached. Please review before sending; do not edit the attachments. —'].filter(Boolean).join('\n');
126
149
  const subject = `Trustchex scan report — ${scanDataObj.documentType ?? 'doc'} — ${device.brand} ${device.model} — v${sdkVersion}${sessionId ? ` — ${sessionId}` : ''}`;
@@ -100,7 +100,16 @@ export class DiagnosticsCollector {
100
100
 
101
101
  nfcAttemptStarted() {
102
102
  // A fresh attempt: bump retry count if a previous attempt left steps.
103
- if (this.nfc.attempted) this.nfc.retryCount += 1;
103
+ if (this.nfc.attempted) {
104
+ this.nfc.retryCount += 1;
105
+ // A retry supersedes the prior attempt: that attempt's failure is stale,
106
+ // so clear it. The failure markers then reflect only the CURRENT attempt —
107
+ // a within-attempt failure (e.g. PACE failing before a BAC fallback in the
108
+ // same read) is retained, while a failure that a later retry recovered is
109
+ // not reported as the outcome.
110
+ this.nfc.failureStep = undefined;
111
+ this.nfc.errorCategory = undefined;
112
+ }
104
113
  this.nfc.attempted = true;
105
114
  }
106
115
  nfcChipDetected() {
@@ -124,15 +133,28 @@ export class DiagnosticsCollector {
124
133
  this.nfc.failureStep = step.step;
125
134
  }
126
135
  }
136
+
137
+ /** The first NFC step that failed in the current attempt (e.g. 'paceAuth',
138
+ * 'readDG2'), or undefined. Lets the caller build a step-specific error code
139
+ * without taking a full snapshot. */
140
+ getFailureStep() {
141
+ return this.nfc.failureStep;
142
+ }
143
+
144
+ /** APDU status word recorded for the failing step, if any (e.g. '6982'). */
145
+ getFailureStatusWord() {
146
+ const fs = this.nfc.failureStep;
147
+ if (!fs) return undefined;
148
+ return this.nfc.steps.find(s => s.step === fs && !s.ok)?.sw;
149
+ }
127
150
  nfcResult(succeeded, errorCategory) {
128
151
  this.nfc.succeeded = succeeded;
129
- if (succeeded) {
130
- // The read ultimately succeeded transient per-attempt failures (e.g. a
131
- // recovered DG2 retry, or the expected EF.CardAccess 6982 on a BAC-only
132
- // card) must NOT be reported as the outcome. Clear the failure markers.
133
- this.nfc.failureStep = undefined;
134
- this.nfc.errorCategory = undefined;
135
- } else if (errorCategory) {
152
+ // Note: a failure recovered by a RETRY is already cleared in
153
+ // nfcAttemptStarted (the retry supersedes it). We deliberately do NOT clear
154
+ // here on success, so a within-attempt failure that a fallback recovered
155
+ // (e.g. PACE failing before BAC succeeds in the same read) stays visible in
156
+ // diagnostics — that is a meaningful signal, not the overall outcome.
157
+ if (!succeeded && errorCategory) {
136
158
  this.nfc.errorCategory = errorCategory;
137
159
  }
138
160
  }
@@ -30,6 +30,21 @@ const normalizeDocumentCode = code => {
30
30
  return 'I';
31
31
  };
32
32
 
33
+ /**
34
+ * Strip MRZ filler characters from a human-readable text field. In the MRZ a
35
+ * single `<` separates words and `<<` separates components; the `mrz` package
36
+ * usually trims trailing fillers but can leave embedded `<`/`<<` in name and
37
+ * optional-data fields, which then surfaced raw in the UI (e.g.
38
+ * "ANNA<<MARIA"). Convert any filler run to a single space and trim, so the
39
+ * displayed value reads "ANNA MARIA". Returns null for empty/filler-only input.
40
+ * Only for text fields — never call on dates, sex, codes, or check digits.
41
+ */
42
+ const cleanMRZTextField = value => {
43
+ if (!value) return null;
44
+ const cleaned = value.replace(/</g, ' ').replace(/\s+/g, ' ').trim();
45
+ return cleaned.length > 0 ? cleaned : null;
46
+ };
47
+
33
48
  /**
34
49
  * Pads a TD1 line to the fixed 30-char width.
35
50
  *
@@ -1264,14 +1279,16 @@ const validateMRZ = (mrzText, autocorrect = true) => {
1264
1279
  issuingState: result.fields.issuingState || null,
1265
1280
  documentNumber: result.fields.documentNumber || null,
1266
1281
  nationality: result.fields.nationality || null,
1267
- lastName: result.fields.lastName || null,
1268
- firstName: result.fields.firstName || null,
1282
+ // Text fields can carry embedded MRZ filler (`<`/`<<`) — strip it so the
1283
+ // UI shows "ANNA MARIA", not "ANNA<<MARIA".
1284
+ lastName: cleanMRZTextField(result.fields.lastName),
1285
+ firstName: cleanMRZTextField(result.fields.firstName),
1269
1286
  sex: result.fields.sex || null,
1270
1287
  birthDate: result.fields.birthDate || null,
1271
1288
  expirationDate: result.fields.expirationDate || null,
1272
- personalNumber: result.fields.personalNumber || null,
1273
- optional1: result.fields.optional1 || null,
1274
- optional2: result.fields.optional2 || null
1289
+ personalNumber: cleanMRZTextField(result.fields.personalNumber),
1290
+ optional1: cleanMRZTextField(result.fields.optional1),
1291
+ optional2: cleanMRZTextField(result.fields.optional2)
1275
1292
  };
1276
1293
  return {
1277
1294
  valid: true,
@@ -45,7 +45,20 @@ function convertName(rawName, map) {
45
45
  return rawName.split(' ').map(word => applyDigraphMap(word, map)).join(' ');
46
46
  }
47
47
  export function buildDocumentName(rawFirst, rawLast, issuingState, dg11First, dg11Last) {
48
- if (dg11First != null && dg11Last != null && (dg11First || dg11Last)) {
48
+ // Prefer DG11 (the chip's printed name in UTF-8) ONLY when it carries a proper
49
+ // two-part split — both a surname AND a given name. That is the authoritative
50
+ // correction for chips whose DG1 MRZ name lacks a `<<` separator (e.g. Turkish
51
+ // PACE passports), where the raw MRZ would otherwise put the full name into
52
+ // the surname.
53
+ //
54
+ // A one-sided DG11 result (only surname, or only given name) is NOT reliable —
55
+ // it usually means DG11 had no clear separator, so we don't know which part it
56
+ // is. Back-filling the missing side from the raw MRZ caused the given name to
57
+ // be duplicated into both fields, so we now fall through to the MRZ-based
58
+ // split instead, which is more trustworthy than mixing the two sources.
59
+ const hasFirst = (dg11First ?? '') !== '';
60
+ const hasLast = (dg11Last ?? '') !== '';
61
+ if (hasFirst && hasLast) {
49
62
  return {
50
63
  rawFirst,
51
64
  rawLast,
@@ -50,6 +50,11 @@ export default {
50
50
  'diagnosticReport.noticeBody': 'This opens a draft with your scan data, document images and technical details so support can help resolve a scanning issue. Review it before sending.',
51
51
  'diagnosticReport.continue': 'Continue',
52
52
  'diagnosticReport.cancel': 'Cancel',
53
+ 'diagnosticReport.shareFailedTitle': "Couldn't open the share sheet",
54
+ 'diagnosticReport.shareFailedBody': "We couldn't attach the report to share. Send it securely to support instead?",
55
+ 'diagnosticReport.uploadAction': 'Send to support',
56
+ 'diagnosticReport.uploadSuccess': 'Diagnostic report sent to support.',
57
+ 'diagnosticReport.uploadFailed': "Couldn't send the report. Please try again later.",
53
58
  'livenessDetectionScreen.guideHeader': 'Face Verification',
54
59
  'livenessDetectionScreen.guideText': 'Please ensure the following for best results:',
55
60
  'livenessDetectionScreen.guidePoint1': 'Remove glasses, hats, or face coverings',
@@ -72,6 +77,13 @@ export default {
72
77
  'eidScannerScreen.guideHeader': 'NFC Document Scan',
73
78
  'eidScannerScreen.guideText': 'When prompted, hold your document flat against the back of your device near the NFC antenna.',
74
79
  'eidScannerScreen.invalidMRZFields': 'Document information could not be read. Please try again.',
80
+ 'eidScannerScreen.error': 'Error',
81
+ 'eidScannerScreen.errorCode': 'Error code',
82
+ 'eidScannerScreen.nfcGenericError': 'Something went wrong while reading your document. Please try again. If the problem continues, contact support with the code below.',
83
+ 'eidScannerScreen.nfcReadFailed': 'Could not read the chip. Hold the document flat against the back of your device and keep it still, then try again.',
84
+ 'eidScannerScreen.nfcAuthFailed': 'Could not establish a secure connection to the chip. Make sure the document number, date of birth and expiry date are correct, then try again.',
85
+ 'eidScannerScreen.nfcTagLost': 'Connection to the document was lost. Keep the document still against the device and try again.',
86
+ 'eidScannerScreen.nfcTimeout': 'Reading the chip timed out. Hold the document steady against the device and try again.',
75
87
  'eidScannerScreen.faceImageNotFound': 'Photo data not found on document.',
76
88
  'eidScannerScreen.documentCode': 'Document Type',
77
89
  'eidScannerScreen.nationality': 'Nationality',
@@ -50,6 +50,11 @@ export default {
50
50
  'diagnosticReport.noticeBody': 'Bu işlem; tarama verilerinizi, belge görüntülerinizi ve teknik ayrıntıları içeren bir taslak açar; böylece destek ekibi tarama sorununu çözebilir. Göndermeden önce gözden geçirin.',
51
51
  'diagnosticReport.continue': 'Devam et',
52
52
  'diagnosticReport.cancel': 'İptal',
53
+ 'diagnosticReport.shareFailedTitle': 'Paylaşım ekranı açılamadı',
54
+ 'diagnosticReport.shareFailedBody': 'Raporu paylaşmak için ekleyemedik. Bunun yerine destek ekibine güvenli şekilde gönderelim mi?',
55
+ 'diagnosticReport.uploadAction': 'Desteğe gönder',
56
+ 'diagnosticReport.uploadSuccess': 'Tanılama raporu destek ekibine gönderildi.',
57
+ 'diagnosticReport.uploadFailed': 'Rapor gönderilemedi. Lütfen daha sonra tekrar deneyin.',
53
58
  'livenessDetectionScreen.guideHeader': 'Yüz Doğrulaması',
54
59
  'livenessDetectionScreen.guideText': 'En iyi sonuç için lütfen şunları sağlayın:',
55
60
  'livenessDetectionScreen.guidePoint1': 'Gözlük, şapka veya yüz aksesuarlarını çıkarın',
@@ -72,6 +77,13 @@ export default {
72
77
  'eidScannerScreen.guideHeader': 'NFC Belge Taraması',
73
78
  'eidScannerScreen.guideText': 'İstendiğinde, belgenizi cihazınızın arka tarafındaki NFC antenine düz bir şekilde yaklaştırın.',
74
79
  'eidScannerScreen.invalidMRZFields': 'Belge bilgileri okunamadı. Lütfen tekrar deneyin.',
80
+ 'eidScannerScreen.error': 'Hata',
81
+ 'eidScannerScreen.errorCode': 'Hata kodu',
82
+ 'eidScannerScreen.nfcGenericError': 'Belgeniz okunurken bir sorun oluştu. Lütfen tekrar deneyin. Sorun devam ederse aşağıdaki kod ile destek ekibimize başvurun.',
83
+ 'eidScannerScreen.nfcReadFailed': 'Çip okunamadı. Belgeyi cihazınızın arkasına düz şekilde yaslayıp sabit tutun ve tekrar deneyin.',
84
+ 'eidScannerScreen.nfcAuthFailed': 'Çip ile güvenli bağlantı kurulamadı. Belge numarası, doğum tarihi ve son geçerlilik tarihinin doğru olduğundan emin olup tekrar deneyin.',
85
+ 'eidScannerScreen.nfcTagLost': 'Belge ile bağlantı kesildi. Belgeyi cihaza sabit tutun ve tekrar deneyin.',
86
+ 'eidScannerScreen.nfcTimeout': 'Çip okuması zaman aşımına uğradı. Belgeyi cihaza sabit tutup tekrar deneyin.',
75
87
  'eidScannerScreen.faceImageNotFound': 'Belgede fotoğraf verisi bulunamadı.',
76
88
  'eidScannerScreen.documentCode': 'Belge Türü',
77
89
  'eidScannerScreen.nationality': 'Uyruk',
@@ -2,4 +2,4 @@
2
2
 
3
3
  // This file is auto-generated. Do not edit manually.
4
4
  // Version is synced from package.json during build.
5
- export const SDK_VERSION = '1.487.0';
5
+ export const SDK_VERSION = '1.488.0';
@@ -1 +1 @@
1
- {"version":3,"file":"EIDScanner.d.ts","sourceRoot":"","sources":["../../../../../src/Shared/Components/EIDScanner.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAgBpD,UAAU,eAAe;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,CACd,OAAO,EAAE,SAAS,EAClB,SAAS,EAAE,MAAM,EACjB,iBAAiB,EAAE,MAAM,KACtB,IAAI,CAAC;IACV,cAAc,CAAC,EAAE,CAAC,SAAS,EAAE,OAAO,KAAK,IAAI,CAAC;CAC/C;AAED,QAAA,MAAM,UAAU,GAAI,6FAOjB,eAAe,4CAsrBjB,CAAC;AA4NF,eAAe,UAAU,CAAC"}
1
+ {"version":3,"file":"EIDScanner.d.ts","sourceRoot":"","sources":["../../../../../src/Shared/Components/EIDScanner.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAiBpD,UAAU,eAAe;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,CACd,OAAO,EAAE,SAAS,EAClB,SAAS,EAAE,MAAM,EACjB,iBAAiB,EAAE,MAAM,KACtB,IAAI,CAAC;IACV,cAAc,CAAC,EAAE,CAAC,SAAS,EAAE,OAAO,KAAK,IAAI,CAAC;CAC/C;AAED,QAAA,MAAM,UAAU,GAAI,6FAOjB,eAAe,4CAqwBjB,CAAC;AA4NF,eAAe,UAAU,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"IdentityDocumentCamera.constants.d.ts","sourceRoot":"","sources":["../../../../../src/Shared/Components/IdentityDocumentCamera.constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,oBAAoB,KAAK,CAAC;AACvC,eAAO,MAAM,4BAA4B,OAAO,CAAC;AACjD,eAAO,MAAM,8BAA8B,IAAI,CAAC;AAChD,eAAO,MAAM,yBAAyB,MAAM,CAAC;AAC7C,eAAO,MAAM,gCAAgC,KAAK,CAAC;AAEnD,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAC3C,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAC5C,eAAO,MAAM,gCAAgC,KAAK,CAAC;AACnD,eAAO,MAAM,wBAAwB,OAAO,CAAC;AAC7C,eAAO,MAAM,0BAA0B,OAAO,CAAC;AAC/C,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAE9C,eAAO,MAAM,6BAA6B,IAAI,CAAC;AAC/C,eAAO,MAAM,sCAAsC,IAAI,CAAC;AAExD,eAAO,MAAM,eAAe,QAAkC,CAAC;AAC/D,eAAO,MAAM,oBAAoB,QAAyB,CAAC;AAC3D,eAAO,MAAM,iBAAiB,QAAsB,CAAC;AACrD,eAAO,MAAM,oBAAoB,QAAe,CAAC;AAIjD,eAAO,MAAM,8BAA8B,IAAI,CAAC;AAChD,eAAO,MAAM,sBAAsB;;;;;;;;;;CAUzB,CAAC;AAKX,eAAO,MAAM,0BAA0B,MAAM,CAAC;AAC9C,eAAO,MAAM,gCAAgC,MAAM,CAAC;AACpD,eAAO,MAAM,2BAA2B,OAAO,CAAC;AAChD,eAAO,MAAM,mBAAmB,MAAM,CAAC;AACvC,eAAO,MAAM,mBAAmB,MAAM,CAAC"}
1
+ {"version":3,"file":"IdentityDocumentCamera.constants.d.ts","sourceRoot":"","sources":["../../../../../src/Shared/Components/IdentityDocumentCamera.constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,oBAAoB,KAAK,CAAC;AACvC,eAAO,MAAM,4BAA4B,OAAO,CAAC;AACjD,eAAO,MAAM,8BAA8B,IAAI,CAAC;AAChD,eAAO,MAAM,yBAAyB,MAAM,CAAC;AAC7C,eAAO,MAAM,gCAAgC,KAAK,CAAC;AAEnD,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAC3C,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAC5C,eAAO,MAAM,gCAAgC,KAAK,CAAC;AACnD,eAAO,MAAM,wBAAwB,OAAO,CAAC;AAC7C,eAAO,MAAM,0BAA0B,OAAO,CAAC;AAC/C,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAE9C,eAAO,MAAM,6BAA6B,IAAI,CAAC;AAC/C,eAAO,MAAM,sCAAsC,IAAI,CAAC;AAExD,eAAO,MAAM,eAAe,QAAkC,CAAC;AAC/D,eAAO,MAAM,oBAAoB,QAAyB,CAAC;AAC3D,eAAO,MAAM,iBAAiB,QAAsB,CAAC;AASrD,eAAO,MAAM,oBAAoB,QAAyC,CAAC;AAI3E,eAAO,MAAM,8BAA8B,IAAI,CAAC;AAChD,eAAO,MAAM,sBAAsB;;;;;;;;;;CAUzB,CAAC;AAKX,eAAO,MAAM,0BAA0B,MAAM,CAAC;AAC9C,eAAO,MAAM,gCAAgC,MAAM,CAAC;AACpD,eAAO,MAAM,2BAA2B,OAAO,CAAC;AAChD,eAAO,MAAM,mBAAmB,MAAM,CAAC;AACvC,eAAO,MAAM,mBAAmB,MAAM,CAAC"}
@@ -1,6 +1,9 @@
1
1
  import { InputStream } from './java/inputStream';
2
2
  import type { FileSystemStructured } from './smartcards/fileSystemStructured';
3
3
  export declare class CardFileInputStream extends InputStream {
4
+ /** Max re-issues of a single block read before failing — mitigates transient
5
+ * iOS NFC transceive drops during large-file reads (e.g. DG2). */
6
+ private static readonly MAX_BLOCK_READ_RETRIES;
4
7
  private path;
5
8
  private buffer;
6
9
  private bufferLength;