dosipas-ts 1.0.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/LICENSE +21 -0
- package/README.md +216 -0
- package/dist/decoder.d.ts +16 -0
- package/dist/decoder.d.ts.map +1 -0
- package/dist/decoder.js +265 -0
- package/dist/decoder.js.map +1 -0
- package/dist/encoder.d.ts +16 -0
- package/dist/encoder.d.ts.map +1 -0
- package/dist/encoder.js +171 -0
- package/dist/encoder.js.map +1 -0
- package/dist/fixtures.d.ts +14 -0
- package/dist/fixtures.d.ts.map +1 -0
- package/dist/fixtures.js +68 -0
- package/dist/fixtures.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/oids.d.ts +28 -0
- package/dist/oids.d.ts.map +1 -0
- package/dist/oids.js +43 -0
- package/dist/oids.js.map +1 -0
- package/dist/schemas.d.ts +11 -0
- package/dist/schemas.d.ts.map +1 -0
- package/dist/schemas.js +17 -0
- package/dist/schemas.js.map +1 -0
- package/dist/signature-fixtures.d.ts +110 -0
- package/dist/signature-fixtures.d.ts.map +1 -0
- package/dist/signature-fixtures.js +142 -0
- package/dist/signature-fixtures.js.map +1 -0
- package/dist/signature-utils.d.ts +29 -0
- package/dist/signature-utils.d.ts.map +1 -0
- package/dist/signature-utils.js +201 -0
- package/dist/signature-utils.js.map +1 -0
- package/dist/signed-data.d.ts +32 -0
- package/dist/signed-data.d.ts.map +1 -0
- package/dist/signed-data.js +87 -0
- package/dist/signed-data.js.map +1 -0
- package/dist/types.d.ts +273 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/dist/verifier.d.ts +67 -0
- package/dist/verifier.d.ts.map +1 -0
- package/dist/verifier.js +309 -0
- package/dist/verifier.js.map +1 -0
- package/package.json +46 -0
- package/schemas/uic-barcode/intercode6.schema.json +204 -0
- package/schemas/uic-barcode/uicBarcodeHeader_v1.schema.json +376 -0
- package/schemas/uic-barcode/uicBarcodeHeader_v2.schema.json +484 -0
- package/schemas/uic-barcode/uicRailTicketData_v1.schema.json +31507 -0
- package/schemas/uic-barcode/uicRailTicketData_v2.schema.json +31623 -0
- package/schemas/uic-barcode/uicRailTicketData_v3.schema.json +32729 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Signature fixture data extracted from real-world ticket barcodes.
|
|
3
|
+
*
|
|
4
|
+
* These fixtures contain the DER-encoded signatures and security metadata
|
|
5
|
+
* for use in signature verification unit tests.
|
|
6
|
+
*
|
|
7
|
+
* Source tickets: intercode6-ts/src/fixtures.ts
|
|
8
|
+
* - SNCF_TER_TICKET_HEX
|
|
9
|
+
* - SOLEA_TICKET_HEX
|
|
10
|
+
* - CTS_TICKET_HEX
|
|
11
|
+
*/
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
// SNCF TER ticket
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
export const SNCF_TER_SIGNATURES = {
|
|
16
|
+
/** Issuer RICS code (securityProviderNum) — used to look up level 1 key. */
|
|
17
|
+
securityProviderNum: 1187,
|
|
18
|
+
/** Key identifier — used with securityProviderNum to find the level 1 key. */
|
|
19
|
+
keyId: 1,
|
|
20
|
+
/** Level 1 key algorithm OID: DSA (no named curve). */
|
|
21
|
+
level1KeyAlg: '2.16.840.1.101.3.4.3.1',
|
|
22
|
+
/** Level 2 key algorithm OID: secp256r1 (P-256). */
|
|
23
|
+
level2KeyAlg: '1.2.840.10045.3.1.7',
|
|
24
|
+
/** Level 1 signing algorithm OID: DSA with SHA-224. */
|
|
25
|
+
level1SigningAlg: '2.16.840.1.101.3.4.3.1',
|
|
26
|
+
/** Level 2 signing algorithm OID: ECDSA with SHA-256. */
|
|
27
|
+
level2SigningAlg: '1.2.840.10045.4.3.2',
|
|
28
|
+
/**
|
|
29
|
+
* Level 1 signature — DER-encoded DSA signature (46 bytes).
|
|
30
|
+
*
|
|
31
|
+
* SEQUENCE {
|
|
32
|
+
* INTEGER r (20 bytes): 7a71a4d9abdf2204ae40d6dd2dff4adb30df5e44
|
|
33
|
+
* INTEGER s (20 bytes): 66856f3933964f825f1c825da94a5e3868ffe649
|
|
34
|
+
* }
|
|
35
|
+
*/
|
|
36
|
+
level1SignatureHex: '302c02147a71a4d9abdf2204ae40d6dd2dff4adb30df5e44' +
|
|
37
|
+
'021466856f3933964f825f1c825da94a5e3868ffe649',
|
|
38
|
+
/**
|
|
39
|
+
* Level 2 signature — DER-encoded ECDSA P-256 signature (70 bytes).
|
|
40
|
+
*
|
|
41
|
+
* SEQUENCE {
|
|
42
|
+
* INTEGER r (32 bytes): 24df3d92d8f23d0b01572732e3752ce179f65a8160128341b86f9772f6677a14
|
|
43
|
+
* INTEGER s (32 bytes): 149d2950f3925fea703f4048eb3ada17649cdd2228ab5319cbd9c0d59d5cf603
|
|
44
|
+
* }
|
|
45
|
+
*/
|
|
46
|
+
level2SignatureHex: '3044022024df3d92d8f23d0b01572732e3752ce179f65a81' +
|
|
47
|
+
'60128341b86f9772f6677a140220149d2950f3925fea703f' +
|
|
48
|
+
'4048eb3ada17649cdd2228ab5319cbd9c0d59d5cf603',
|
|
49
|
+
};
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
// Soléa ticket
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
export const SOLEA_SIGNATURES = {
|
|
54
|
+
/** Issuer RICS code (securityProviderNum) — used to look up level 1 key. */
|
|
55
|
+
securityProviderNum: 1187,
|
|
56
|
+
/** Key identifier — used with securityProviderNum to find the level 1 key. */
|
|
57
|
+
keyId: 1,
|
|
58
|
+
/** Level 1 key algorithm OID: secp256r1 (P-256). */
|
|
59
|
+
level1KeyAlg: '1.2.840.10045.3.1.7',
|
|
60
|
+
/** Level 2 key algorithm OID: secp256r1 (P-256). */
|
|
61
|
+
level2KeyAlg: '1.2.840.10045.3.1.7',
|
|
62
|
+
/** Level 1 signing algorithm OID: ECDSA with SHA-256. */
|
|
63
|
+
level1SigningAlg: '1.2.840.10045.4.3.2',
|
|
64
|
+
/** Level 2 signing algorithm OID: ECDSA with SHA-256. */
|
|
65
|
+
level2SigningAlg: '1.2.840.10045.4.3.2',
|
|
66
|
+
/**
|
|
67
|
+
* Level 1 signature — DER-encoded ECDSA P-256 signature (71 bytes).
|
|
68
|
+
*
|
|
69
|
+
* SEQUENCE {
|
|
70
|
+
* INTEGER r (33 bytes, 0x00-padded): 00c02fa08b4a288401a053dd250c1f748ae51d16b9aac26eacc09056695f0abe68
|
|
71
|
+
* INTEGER s (32 bytes): 50c1f1b13a5e8e126441f84159e5b3188d505e73354492b8de369441daa7285b
|
|
72
|
+
* }
|
|
73
|
+
*/
|
|
74
|
+
level1SignatureHex: '30450221' +
|
|
75
|
+
'00c02fa08b4a288401a053dd250c1f748ae51d16b9aac26e' +
|
|
76
|
+
'acc09056695f0abe68' +
|
|
77
|
+
'0220' +
|
|
78
|
+
'50c1f1b13a5e8e126441f84159e5b3188d505e73354492b8' +
|
|
79
|
+
'de369441daa7285b',
|
|
80
|
+
/**
|
|
81
|
+
* Level 2 signature — DER-encoded ECDSA P-256 signature (71 bytes).
|
|
82
|
+
*
|
|
83
|
+
* SEQUENCE {
|
|
84
|
+
* INTEGER r (32 bytes): 2a79f008376051f020eae108d0e9950314cac19c4c580249be4b530ec2250ccb
|
|
85
|
+
* INTEGER s (33 bytes, 0x00-padded): 00a00d805f051efcd130b17b76bf10969b626ed026423f6024d34446eae9168fa3
|
|
86
|
+
* }
|
|
87
|
+
*/
|
|
88
|
+
level2SignatureHex: '30450220' +
|
|
89
|
+
'2a79f008376051f020eae108d0e9950314cac19c4c580249' +
|
|
90
|
+
'be4b530ec2250ccb' +
|
|
91
|
+
'0221' +
|
|
92
|
+
'00a00d805f051efcd130b17b76bf10969b626ed026423f60' +
|
|
93
|
+
'24d34446eae9168fa3',
|
|
94
|
+
};
|
|
95
|
+
// ---------------------------------------------------------------------------
|
|
96
|
+
// CTS ticket
|
|
97
|
+
// ---------------------------------------------------------------------------
|
|
98
|
+
export const CTS_SIGNATURES = {
|
|
99
|
+
/** Issuer RICS code (securityProviderNum) — used to look up level 1 key. */
|
|
100
|
+
securityProviderNum: 1187,
|
|
101
|
+
/** Key identifier — used with securityProviderNum to find the level 1 key. */
|
|
102
|
+
keyId: 1,
|
|
103
|
+
/** Level 1 key algorithm OID: secp256r1 (P-256). */
|
|
104
|
+
level1KeyAlg: '1.2.840.10045.3.1.7',
|
|
105
|
+
/** Level 2 key algorithm OID: secp256r1 (P-256). */
|
|
106
|
+
level2KeyAlg: '1.2.840.10045.3.1.7',
|
|
107
|
+
/** Level 1 signing algorithm OID: ECDSA with SHA-256. */
|
|
108
|
+
level1SigningAlg: '1.2.840.10045.4.3.2',
|
|
109
|
+
/** Level 2 signing algorithm OID: ECDSA with SHA-256. */
|
|
110
|
+
level2SigningAlg: '1.2.840.10045.4.3.2',
|
|
111
|
+
/**
|
|
112
|
+
* Level 1 signature — DER-encoded ECDSA P-256 signature (72 bytes).
|
|
113
|
+
*
|
|
114
|
+
* SEQUENCE {
|
|
115
|
+
* INTEGER r (33 bytes, 0x00-padded): 008974af39d91452785b211f49ec2e36302b2b73ec3b99f5cdba5f1bf5c9e7cb72
|
|
116
|
+
* INTEGER s (33 bytes, 0x00-padded): 00f468337ab677c729a43b601c8df31f0c9c9923be5711ace720943c1f99b2a34b
|
|
117
|
+
* }
|
|
118
|
+
*/
|
|
119
|
+
level1SignatureHex: '30460221' +
|
|
120
|
+
'008974af39d91452785b211f49ec2e36302b2b73ec3b99f5' +
|
|
121
|
+
'cdba5f1bf5c9e7cb72' +
|
|
122
|
+
'0221' +
|
|
123
|
+
'00f468337ab677c729a43b601c8df31f0c9c9923be5711ac' +
|
|
124
|
+
'e720943c1f99b2a34b',
|
|
125
|
+
/**
|
|
126
|
+
* Level 2 signature — DER-encoded ECDSA P-256 signature (71 bytes).
|
|
127
|
+
*
|
|
128
|
+
* SEQUENCE {
|
|
129
|
+
* INTEGER r (32 bytes): 2a79f008376051f020eae108d0e9950314cac19c4c580249be4b530ec2250ccb
|
|
130
|
+
* INTEGER s (33 bytes, 0x00-padded): 00a00d805f051efcd130b17b76bf10969b626ed026423f6024d34446eae9168fa3
|
|
131
|
+
* }
|
|
132
|
+
*
|
|
133
|
+
* Note: identical to Soléa level2Signature (same level 2 key pair).
|
|
134
|
+
*/
|
|
135
|
+
level2SignatureHex: '30450220' +
|
|
136
|
+
'2a79f008376051f020eae108d0e9950314cac19c4c580249' +
|
|
137
|
+
'be4b530ec2250ccb' +
|
|
138
|
+
'0221' +
|
|
139
|
+
'00a00d805f051efcd130b17b76bf10969b626ed026423f60' +
|
|
140
|
+
'24d34446eae9168fa3',
|
|
141
|
+
};
|
|
142
|
+
//# sourceMappingURL=signature-fixtures.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signature-fixtures.js","sourceRoot":"","sources":["../src/signature-fixtures.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,8EAA8E;AAC9E,kBAAkB;AAClB,8EAA8E;AAE9E,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,4EAA4E;IAC5E,mBAAmB,EAAE,IAAI;IACzB,8EAA8E;IAC9E,KAAK,EAAE,CAAC;IACR,uDAAuD;IACvD,YAAY,EAAE,wBAAwB;IACtC,oDAAoD;IACpD,YAAY,EAAE,qBAAqB;IACnC,uDAAuD;IACvD,gBAAgB,EAAE,wBAAwB;IAC1C,yDAAyD;IACzD,gBAAgB,EAAE,qBAAqB;IAEvC;;;;;;;OAOG;IACH,kBAAkB,EAChB,kDAAkD;QAClD,8CAA8C;IAEhD;;;;;;;OAOG;IACH,kBAAkB,EAChB,kDAAkD;QAClD,kDAAkD;QAClD,8CAA8C;CACxC,CAAC;AAEX,8EAA8E;AAC9E,eAAe;AACf,8EAA8E;AAE9E,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,4EAA4E;IAC5E,mBAAmB,EAAE,IAAI;IACzB,8EAA8E;IAC9E,KAAK,EAAE,CAAC;IACR,oDAAoD;IACpD,YAAY,EAAE,qBAAqB;IACnC,oDAAoD;IACpD,YAAY,EAAE,qBAAqB;IACnC,yDAAyD;IACzD,gBAAgB,EAAE,qBAAqB;IACvC,yDAAyD;IACzD,gBAAgB,EAAE,qBAAqB;IAEvC;;;;;;;OAOG;IACH,kBAAkB,EAChB,UAAU;QACV,kDAAkD;QAClD,oBAAoB;QACpB,MAAM;QACN,kDAAkD;QAClD,kBAAkB;IAEpB;;;;;;;OAOG;IACH,kBAAkB,EAChB,UAAU;QACV,kDAAkD;QAClD,kBAAkB;QAClB,MAAM;QACN,kDAAkD;QAClD,oBAAoB;CACd,CAAC;AAEX,8EAA8E;AAC9E,aAAa;AACb,8EAA8E;AAE9E,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,4EAA4E;IAC5E,mBAAmB,EAAE,IAAI;IACzB,8EAA8E;IAC9E,KAAK,EAAE,CAAC;IACR,oDAAoD;IACpD,YAAY,EAAE,qBAAqB;IACnC,oDAAoD;IACpD,YAAY,EAAE,qBAAqB;IACnC,yDAAyD;IACzD,gBAAgB,EAAE,qBAAqB;IACvC,yDAAyD;IACzD,gBAAgB,EAAE,qBAAqB;IAEvC;;;;;;;OAOG;IACH,kBAAkB,EAChB,UAAU;QACV,kDAAkD;QAClD,oBAAoB;QACpB,MAAM;QACN,kDAAkD;QAClD,oBAAoB;IAEtB;;;;;;;;;OASG;IACH,kBAAkB,EAChB,UAAU;QACV,kDAAkD;QAClD,kBAAkB;QAClB,MAAM;QACN,kDAAkD;QAClD,oBAAoB;CACd,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Signature format utilities for UIC barcode verification.
|
|
3
|
+
*
|
|
4
|
+
* Handles DER-to-raw conversion for ECDSA/DSA signatures and
|
|
5
|
+
* public key format detection/extraction.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Parse a DER-encoded ECDSA/DSA signature into raw (r || s) concatenation.
|
|
9
|
+
*
|
|
10
|
+
* DER format:
|
|
11
|
+
* SEQUENCE { INTEGER r, INTEGER s }
|
|
12
|
+
*
|
|
13
|
+
* Raw format:
|
|
14
|
+
* r (componentLength bytes) || s (componentLength bytes)
|
|
15
|
+
*
|
|
16
|
+
* @param der - DER-encoded signature bytes
|
|
17
|
+
* @param componentLength - Byte length of each component (32 for P-256, 48 for P-384, etc.)
|
|
18
|
+
* @returns Raw (r || s) bytes
|
|
19
|
+
*/
|
|
20
|
+
export declare function derToRaw(der: Uint8Array, componentLength: number): Uint8Array;
|
|
21
|
+
/**
|
|
22
|
+
* Extract the raw EC public key point from a key that may be in SPKI DER format
|
|
23
|
+
* or already a raw point (compressed or uncompressed).
|
|
24
|
+
*
|
|
25
|
+
* @param keyBytes - Public key bytes (SPKI DER or raw point)
|
|
26
|
+
* @returns Raw EC point bytes (compressed or uncompressed)
|
|
27
|
+
*/
|
|
28
|
+
export declare function extractEcPublicKeyPoint(keyBytes: Uint8Array): Uint8Array;
|
|
29
|
+
//# sourceMappingURL=signature-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signature-utils.d.ts","sourceRoot":"","sources":["../src/signature-utils.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;;;;;;;GAYG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,GAAG,UAAU,CA4C7E;AAUD;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,UAAU,GAAG,UAAU,CA4BxE"}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Signature format utilities for UIC barcode verification.
|
|
3
|
+
*
|
|
4
|
+
* Handles DER-to-raw conversion for ECDSA/DSA signatures and
|
|
5
|
+
* public key format detection/extraction.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Parse a DER-encoded ECDSA/DSA signature into raw (r || s) concatenation.
|
|
9
|
+
*
|
|
10
|
+
* DER format:
|
|
11
|
+
* SEQUENCE { INTEGER r, INTEGER s }
|
|
12
|
+
*
|
|
13
|
+
* Raw format:
|
|
14
|
+
* r (componentLength bytes) || s (componentLength bytes)
|
|
15
|
+
*
|
|
16
|
+
* @param der - DER-encoded signature bytes
|
|
17
|
+
* @param componentLength - Byte length of each component (32 for P-256, 48 for P-384, etc.)
|
|
18
|
+
* @returns Raw (r || s) bytes
|
|
19
|
+
*/
|
|
20
|
+
export function derToRaw(der, componentLength) {
|
|
21
|
+
if (der[0] !== 0x30) {
|
|
22
|
+
throw new Error(`Invalid DER signature: expected SEQUENCE tag 0x30, got 0x${der[0].toString(16)}`);
|
|
23
|
+
}
|
|
24
|
+
// Parse SEQUENCE length
|
|
25
|
+
let offset = 1;
|
|
26
|
+
let seqLen = der[offset++];
|
|
27
|
+
if (seqLen & 0x80) {
|
|
28
|
+
const lenBytes = seqLen & 0x7f;
|
|
29
|
+
seqLen = 0;
|
|
30
|
+
for (let i = 0; i < lenBytes; i++) {
|
|
31
|
+
seqLen = (seqLen << 8) | der[offset++];
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
// Parse r INTEGER
|
|
35
|
+
if (der[offset++] !== 0x02) {
|
|
36
|
+
throw new Error('Invalid DER signature: expected INTEGER tag for r');
|
|
37
|
+
}
|
|
38
|
+
const rLen = der[offset++];
|
|
39
|
+
const rStart = offset;
|
|
40
|
+
offset += rLen;
|
|
41
|
+
// Parse s INTEGER
|
|
42
|
+
if (der[offset++] !== 0x02) {
|
|
43
|
+
throw new Error('Invalid DER signature: expected INTEGER tag for s');
|
|
44
|
+
}
|
|
45
|
+
const sLen = der[offset++];
|
|
46
|
+
const sStart = offset;
|
|
47
|
+
const raw = new Uint8Array(componentLength * 2);
|
|
48
|
+
// Copy r: strip leading 0x00 padding, right-align into componentLength bytes
|
|
49
|
+
const rPad = rLen > componentLength ? rLen - componentLength : 0;
|
|
50
|
+
const rDst = componentLength - (rLen - rPad);
|
|
51
|
+
raw.set(der.slice(rStart + rPad, rStart + rLen), rDst);
|
|
52
|
+
// Copy s: strip leading 0x00 padding, right-align into componentLength bytes
|
|
53
|
+
const sPad = sLen > componentLength ? sLen - componentLength : 0;
|
|
54
|
+
const sDst = componentLength + componentLength - (sLen - sPad);
|
|
55
|
+
raw.set(der.slice(sStart + sPad, sStart + sLen), sDst);
|
|
56
|
+
return raw;
|
|
57
|
+
}
|
|
58
|
+
/** SPKI header for EC P-256 (26 bytes: SEQUENCE > SEQUENCE > OID ecPublicKey > OID P-256 > BIT STRING). */
|
|
59
|
+
const SPKI_P256_HEADER = new Uint8Array([
|
|
60
|
+
0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86,
|
|
61
|
+
0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a,
|
|
62
|
+
0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03,
|
|
63
|
+
0x42, 0x00,
|
|
64
|
+
]);
|
|
65
|
+
/**
|
|
66
|
+
* Extract the raw EC public key point from a key that may be in SPKI DER format
|
|
67
|
+
* or already a raw point (compressed or uncompressed).
|
|
68
|
+
*
|
|
69
|
+
* @param keyBytes - Public key bytes (SPKI DER or raw point)
|
|
70
|
+
* @returns Raw EC point bytes (compressed or uncompressed)
|
|
71
|
+
*/
|
|
72
|
+
export function extractEcPublicKeyPoint(keyBytes) {
|
|
73
|
+
// Already a raw point (uncompressed 0x04 or compressed 0x02/0x03)
|
|
74
|
+
if (keyBytes[0] === 0x04 || keyBytes[0] === 0x02 || keyBytes[0] === 0x03) {
|
|
75
|
+
return keyBytes;
|
|
76
|
+
}
|
|
77
|
+
// DER-encoded structure (starts with SEQUENCE tag 0x30)
|
|
78
|
+
if (keyBytes[0] === 0x30 && keyBytes.length > 33) {
|
|
79
|
+
// Check for P-256 SPKI header (91 bytes: 26 header + 65 point)
|
|
80
|
+
if (keyBytes.length === 91 && startsWith(keyBytes, SPKI_P256_HEADER)) {
|
|
81
|
+
return keyBytes.slice(26);
|
|
82
|
+
}
|
|
83
|
+
// Could be an X.509 certificate or an SPKI structure.
|
|
84
|
+
// Try extracting from X.509 first (certificates are longer, typically > 100 bytes),
|
|
85
|
+
// fall back to SPKI extraction.
|
|
86
|
+
if (keyBytes.length > 100) {
|
|
87
|
+
try {
|
|
88
|
+
return extractPointFromX509Certificate(keyBytes);
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
// Not a valid certificate, try as SPKI
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return extractPointFromSpki(keyBytes);
|
|
95
|
+
}
|
|
96
|
+
throw new Error(`Unrecognized public key format: first byte 0x${keyBytes[0].toString(16)}, length ${keyBytes.length}`);
|
|
97
|
+
}
|
|
98
|
+
function startsWith(data, prefix) {
|
|
99
|
+
if (data.length < prefix.length)
|
|
100
|
+
return false;
|
|
101
|
+
for (let i = 0; i < prefix.length; i++) {
|
|
102
|
+
if (data[i] !== prefix[i])
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Extract EC point from a generic SPKI DER structure.
|
|
109
|
+
* Walks the ASN.1 structure to find the BIT STRING containing the public key.
|
|
110
|
+
*/
|
|
111
|
+
function extractPointFromSpki(spki) {
|
|
112
|
+
let offset = 0;
|
|
113
|
+
// Outer SEQUENCE
|
|
114
|
+
if (spki[offset++] !== 0x30)
|
|
115
|
+
throw new Error('Expected SEQUENCE');
|
|
116
|
+
const outerLen = parseDerLength(spki, offset);
|
|
117
|
+
offset = outerLen.offset;
|
|
118
|
+
// AlgorithmIdentifier SEQUENCE (skip it)
|
|
119
|
+
if (spki[offset++] !== 0x30)
|
|
120
|
+
throw new Error('Expected AlgorithmIdentifier SEQUENCE');
|
|
121
|
+
const algLen = parseDerLength(spki, offset);
|
|
122
|
+
offset = algLen.offset + algLen.length;
|
|
123
|
+
// BIT STRING containing the public key
|
|
124
|
+
if (spki[offset++] !== 0x03)
|
|
125
|
+
throw new Error('Expected BIT STRING');
|
|
126
|
+
const bitStringLen = parseDerLength(spki, offset);
|
|
127
|
+
offset = bitStringLen.offset;
|
|
128
|
+
// Skip unused bits count byte (should be 0)
|
|
129
|
+
offset++;
|
|
130
|
+
// The rest is the raw public key point
|
|
131
|
+
return spki.slice(offset, offset + bitStringLen.length - 1);
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Extract the EC public key point from a DER-encoded X.509 certificate.
|
|
135
|
+
*
|
|
136
|
+
* X.509 structure:
|
|
137
|
+
* SEQUENCE {
|
|
138
|
+
* SEQUENCE (tbsCertificate) {
|
|
139
|
+
* [0] EXPLICIT version, INTEGER serial, SEQUENCE algo,
|
|
140
|
+
* SEQUENCE issuer, SEQUENCE validity, SEQUENCE subject,
|
|
141
|
+
* SEQUENCE (subjectPublicKeyInfo) { ... }
|
|
142
|
+
* }
|
|
143
|
+
* SEQUENCE (signatureAlgorithm), BIT STRING (signature)
|
|
144
|
+
* }
|
|
145
|
+
*
|
|
146
|
+
* We walk through tbsCertificate fields to find the SPKI, then extract the key.
|
|
147
|
+
*/
|
|
148
|
+
function extractPointFromX509Certificate(cert) {
|
|
149
|
+
let offset = 0;
|
|
150
|
+
// Outer SEQUENCE
|
|
151
|
+
if (cert[offset++] !== 0x30)
|
|
152
|
+
throw new Error('Expected SEQUENCE (certificate)');
|
|
153
|
+
const certLen = parseDerLength(cert, offset);
|
|
154
|
+
offset = certLen.offset;
|
|
155
|
+
// tbsCertificate SEQUENCE
|
|
156
|
+
if (cert[offset++] !== 0x30)
|
|
157
|
+
throw new Error('Expected SEQUENCE (tbsCertificate)');
|
|
158
|
+
const tbsLen = parseDerLength(cert, offset);
|
|
159
|
+
offset = tbsLen.offset;
|
|
160
|
+
// [0] EXPLICIT version (optional, tag = 0xA0)
|
|
161
|
+
if (cert[offset] === 0xa0) {
|
|
162
|
+
offset++;
|
|
163
|
+
const vLen = parseDerLength(cert, offset);
|
|
164
|
+
offset = vLen.offset + vLen.length;
|
|
165
|
+
}
|
|
166
|
+
// INTEGER serialNumber
|
|
167
|
+
skipDerElement(cert, offset, (o) => { offset = o; });
|
|
168
|
+
// SEQUENCE signature (algorithm)
|
|
169
|
+
skipDerElement(cert, offset, (o) => { offset = o; });
|
|
170
|
+
// SEQUENCE issuer
|
|
171
|
+
skipDerElement(cert, offset, (o) => { offset = o; });
|
|
172
|
+
// SEQUENCE validity
|
|
173
|
+
skipDerElement(cert, offset, (o) => { offset = o; });
|
|
174
|
+
// SEQUENCE subject
|
|
175
|
+
skipDerElement(cert, offset, (o) => { offset = o; });
|
|
176
|
+
// SEQUENCE subjectPublicKeyInfo — this is the SPKI
|
|
177
|
+
if (cert[offset] !== 0x30)
|
|
178
|
+
throw new Error('Expected SEQUENCE (subjectPublicKeyInfo)');
|
|
179
|
+
const spkiStart = offset;
|
|
180
|
+
const spkiTagLen = parseDerLength(cert, offset + 1);
|
|
181
|
+
const spkiTotalLen = 1 + (spkiTagLen.offset - (offset + 1)) + spkiTagLen.length;
|
|
182
|
+
const spki = cert.slice(spkiStart, spkiStart + spkiTotalLen);
|
|
183
|
+
return extractPointFromSpki(spki);
|
|
184
|
+
}
|
|
185
|
+
function skipDerElement(data, offset, cb) {
|
|
186
|
+
offset++; // skip tag
|
|
187
|
+
const len = parseDerLength(data, offset);
|
|
188
|
+
cb(len.offset + len.length);
|
|
189
|
+
}
|
|
190
|
+
function parseDerLength(data, offset) {
|
|
191
|
+
let len = data[offset++];
|
|
192
|
+
if (len & 0x80) {
|
|
193
|
+
const numBytes = len & 0x7f;
|
|
194
|
+
len = 0;
|
|
195
|
+
for (let i = 0; i < numBytes; i++) {
|
|
196
|
+
len = (len << 8) | data[offset++];
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return { length: len, offset };
|
|
200
|
+
}
|
|
201
|
+
//# sourceMappingURL=signature-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signature-utils.js","sourceRoot":"","sources":["../src/signature-utils.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/** Extracted signed data bytes and security metadata from a UIC barcode. */
|
|
2
|
+
export interface ExtractedSignedData {
|
|
3
|
+
/** The exact bytes of `level1Data` — signed by level1Signature. */
|
|
4
|
+
level1DataBytes: Uint8Array;
|
|
5
|
+
/** The exact bytes of `level2SignedData` — signed by level2Signature. */
|
|
6
|
+
level2SignedBytes: Uint8Array;
|
|
7
|
+
/** Security metadata extracted from the decoded header. */
|
|
8
|
+
security: {
|
|
9
|
+
securityProviderNum?: number;
|
|
10
|
+
securityProviderIA5?: string;
|
|
11
|
+
keyId?: number;
|
|
12
|
+
level1KeyAlg?: string;
|
|
13
|
+
level2KeyAlg?: string;
|
|
14
|
+
level1SigningAlg?: string;
|
|
15
|
+
level2SigningAlg?: string;
|
|
16
|
+
level2PublicKey?: Uint8Array;
|
|
17
|
+
level1Signature?: Uint8Array;
|
|
18
|
+
level2Signature?: Uint8Array;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Extract the signed data bytes and security metadata from a UIC barcode.
|
|
23
|
+
*
|
|
24
|
+
* This decodes the barcode header with metadata tracking, then reads the
|
|
25
|
+
* exact original bytes from the source buffer via `rawBytes`. No re-encoding
|
|
26
|
+
* is performed.
|
|
27
|
+
*
|
|
28
|
+
* @param bytes - The raw barcode payload bytes.
|
|
29
|
+
* @returns Extracted signed bytes and security metadata.
|
|
30
|
+
*/
|
|
31
|
+
export declare function extractSignedData(bytes: Uint8Array): ExtractedSignedData;
|
|
32
|
+
//# sourceMappingURL=signed-data.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signed-data.d.ts","sourceRoot":"","sources":["../src/signed-data.ts"],"names":[],"mappings":"AAuCA,4EAA4E;AAC5E,MAAM,WAAW,mBAAmB;IAClC,mEAAmE;IACnE,eAAe,EAAE,UAAU,CAAC;IAC5B,yEAAyE;IACzE,iBAAiB,EAAE,UAAU,CAAC;IAE9B,2DAA2D;IAC3D,QAAQ,EAAE;QACR,mBAAmB,CAAC,EAAE,MAAM,CAAC;QAC7B,mBAAmB,CAAC,EAAE,MAAM,CAAC;QAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,eAAe,CAAC,EAAE,UAAU,CAAC;QAC7B,eAAe,CAAC,EAAE,UAAU,CAAC;QAC7B,eAAe,CAAC,EAAE,UAAU,CAAC;KAC9B,CAAC;CACH;AAMD;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,UAAU,GAAG,mBAAmB,CA6CxE"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Signed data extraction for UIC barcode signature verification.
|
|
3
|
+
*
|
|
4
|
+
* Uses `decodeWithMetadata` to extract the exact original bytes that were
|
|
5
|
+
* signed, avoiding any re-encoding that could introduce mismatches.
|
|
6
|
+
*/
|
|
7
|
+
import { SchemaCodec, SchemaBuilder, BitBuffer, } from 'asn1-per-ts';
|
|
8
|
+
import { HEADER_SCHEMAS } from './schemas';
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
// Codec cache (separate from decoder/encoder to avoid coupling)
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
const headerCodecCache = new Map();
|
|
13
|
+
function getHeaderCodec(version) {
|
|
14
|
+
let codec = headerCodecCache.get(version);
|
|
15
|
+
if (codec)
|
|
16
|
+
return codec;
|
|
17
|
+
const schemas = HEADER_SCHEMAS[version];
|
|
18
|
+
if (!schemas) {
|
|
19
|
+
throw new Error(`No schema for header v${version}. Supported: v1, v2`);
|
|
20
|
+
}
|
|
21
|
+
codec = new SchemaCodec(schemas.UicBarcodeHeader);
|
|
22
|
+
headerCodecCache.set(version, codec);
|
|
23
|
+
return codec;
|
|
24
|
+
}
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
// Public API
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
/**
|
|
29
|
+
* Extract the signed data bytes and security metadata from a UIC barcode.
|
|
30
|
+
*
|
|
31
|
+
* This decodes the barcode header with metadata tracking, then reads the
|
|
32
|
+
* exact original bytes from the source buffer via `rawBytes`. No re-encoding
|
|
33
|
+
* is performed.
|
|
34
|
+
*
|
|
35
|
+
* @param bytes - The raw barcode payload bytes.
|
|
36
|
+
* @returns Extracted signed bytes and security metadata.
|
|
37
|
+
*/
|
|
38
|
+
export function extractSignedData(bytes) {
|
|
39
|
+
// Peek the header version
|
|
40
|
+
const peekBuf = BitBuffer.from(bytes);
|
|
41
|
+
peekBuf.readBit(); // skip optional bitmap
|
|
42
|
+
const format = SchemaBuilder.build({ type: 'IA5String' }).decode(peekBuf);
|
|
43
|
+
const headerVersionMatch = format.match(/^U(\d+)$/);
|
|
44
|
+
if (!headerVersionMatch) {
|
|
45
|
+
throw new Error(`Unknown header format "${format}"`);
|
|
46
|
+
}
|
|
47
|
+
const headerVersion = parseInt(headerVersionMatch[1], 10);
|
|
48
|
+
// Decode with metadata to get exact byte positions
|
|
49
|
+
const codec = getHeaderCodec(headerVersion);
|
|
50
|
+
const root = codec.decodeWithMetadata(bytes);
|
|
51
|
+
// Navigate the metadata tree
|
|
52
|
+
const headerFields = root.value;
|
|
53
|
+
// level2SignedData node — its rawBytes are what level2Signature signs
|
|
54
|
+
const level2SignedDataNode = headerFields.level2SignedData;
|
|
55
|
+
const level2SignedBytes = level2SignedDataNode.meta.rawBytes;
|
|
56
|
+
// level1Data node — its rawBytes are what level1Signature signs
|
|
57
|
+
const l2Fields = level2SignedDataNode.value;
|
|
58
|
+
const level1DataNode = l2Fields.level1Data;
|
|
59
|
+
const level1DataBytes = level1DataNode.meta.rawBytes;
|
|
60
|
+
// Extract security metadata by stripping metadata from the relevant nodes
|
|
61
|
+
const l1Fields = level1DataNode.value;
|
|
62
|
+
const security = {
|
|
63
|
+
securityProviderNum: getNodeValue(l1Fields.securityProviderNum),
|
|
64
|
+
securityProviderIA5: getNodeValue(l1Fields.securityProviderIA5),
|
|
65
|
+
keyId: getNodeValue(l1Fields.keyId),
|
|
66
|
+
level1KeyAlg: getNodeValue(l1Fields.level1KeyAlg),
|
|
67
|
+
level2KeyAlg: getNodeValue(l1Fields.level2KeyAlg),
|
|
68
|
+
level1SigningAlg: getNodeValue(l1Fields.level1SigningAlg),
|
|
69
|
+
level2SigningAlg: getNodeValue(l1Fields.level2SigningAlg),
|
|
70
|
+
level2PublicKey: getNodeValue(l1Fields.level2PublicKey),
|
|
71
|
+
level1Signature: getNodeValue(l2Fields.level1Signature),
|
|
72
|
+
level2Signature: getNodeValue(headerFields.level2Signature),
|
|
73
|
+
};
|
|
74
|
+
return { level1DataBytes, level2SignedBytes, security };
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Get the plain value from a DecodedNode, handling optional/absent fields.
|
|
78
|
+
*/
|
|
79
|
+
function getNodeValue(node) {
|
|
80
|
+
if (!node)
|
|
81
|
+
return undefined;
|
|
82
|
+
if (node.meta.optional && !node.meta.present && !node.meta.isDefault) {
|
|
83
|
+
return undefined;
|
|
84
|
+
}
|
|
85
|
+
return node.value;
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=signed-data.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signed-data.js","sourceRoot":"","sources":["../src/signed-data.ts"],"names":[],"mappings":"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"}
|