mirror of
https://github.com/selfxyz/self.git
synced 2026-04-05 03:00:53 -04:00
refactor circuits repo, clean circuits tests, implement new certificates scripts, fix all tests from circuits repo
This commit is contained in:
9
common/scripts/passportData/README.md
Normal file
9
common/scripts/passportData/README.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# How to generate mock passport data based on your real data?
|
||||
|
||||
- Build the app and scan your passport to log your passport data.
|
||||
- Copy one of the files of this folder and paste your passport data.
|
||||
- Adapt the `verify` function to verify it. Once this is done, adapt the `genMockPassportData` to generate a mock one.
|
||||
- Once the mock passport data generated is verified correctly by the same `verify` function that verifies yours, you're all set!
|
||||
- Run the script to generate a mock passport data and add it to `common/src/utils/mockPassportData.ts`
|
||||
- Do a PR
|
||||
- DM us to collect your bounty!
|
||||
629
common/scripts/passportData/passportData.json
Normal file
629
common/scripts/passportData/passportData.json
Normal file
@@ -0,0 +1,629 @@
|
||||
{
|
||||
"mrz": "P<FRADUPONT<<ALPHONSE<HUGUES<ALBERT<<<<<<<<<24HB818324FRA0402111M3111115<<<<<<<<<<<<<<02",
|
||||
"signatureAlgorithm": "sha256WithRSASSAPSS",
|
||||
"pubKey": {
|
||||
"modulus": "24462187253413274681146293990014601117483150253485750502784042435672184694412963307122026240846907391312882376801424642119473345751861224453041335405750030091821974208795494089279845074559882616814677854700627123408815125641207116387150180075958682953326415376187334908428885389819481874887447587379894859906385655519567588675165038354987379327125622417796020195813417774532495071662150990707566936780047952622227454986438290561518433120591444215515025611804148686981931860883842745085825036432179109865379231910853752492302597965268640127145219080386320748404798990484447108886155687702517815916586904444799519356177",
|
||||
"exponent": "65537"
|
||||
},
|
||||
"dataGroupHashes": [
|
||||
-114,
|
||||
-118,
|
||||
-62,
|
||||
50,
|
||||
66,
|
||||
9,
|
||||
-21,
|
||||
-98,
|
||||
-47,
|
||||
-62,
|
||||
-12,
|
||||
-83,
|
||||
77,
|
||||
111,
|
||||
103,
|
||||
117,
|
||||
-85,
|
||||
-35,
|
||||
-96,
|
||||
21,
|
||||
-11,
|
||||
-19,
|
||||
-27,
|
||||
-99,
|
||||
-119,
|
||||
62,
|
||||
-62,
|
||||
-36,
|
||||
-103,
|
||||
117,
|
||||
-80,
|
||||
-33,
|
||||
31,
|
||||
-123,
|
||||
108,
|
||||
84,
|
||||
-98,
|
||||
102,
|
||||
70,
|
||||
11,
|
||||
-91,
|
||||
-81,
|
||||
-60,
|
||||
12,
|
||||
-55,
|
||||
-126,
|
||||
25,
|
||||
-125,
|
||||
46,
|
||||
125,
|
||||
-100,
|
||||
-62,
|
||||
28,
|
||||
23,
|
||||
55,
|
||||
-123,
|
||||
-99,
|
||||
-92,
|
||||
-121,
|
||||
-120,
|
||||
-36,
|
||||
78,
|
||||
-66,
|
||||
82,
|
||||
-76,
|
||||
-21,
|
||||
-34,
|
||||
33,
|
||||
79,
|
||||
50,
|
||||
-104,
|
||||
-120,
|
||||
-114,
|
||||
35,
|
||||
116,
|
||||
-32,
|
||||
6,
|
||||
-14,
|
||||
-100,
|
||||
-115,
|
||||
-128,
|
||||
-8,
|
||||
10,
|
||||
61,
|
||||
98,
|
||||
86,
|
||||
-8,
|
||||
45,
|
||||
-49,
|
||||
-46,
|
||||
90,
|
||||
-24,
|
||||
-81,
|
||||
38,
|
||||
0,
|
||||
-62,
|
||||
104,
|
||||
108,
|
||||
-19,
|
||||
-10,
|
||||
97,
|
||||
-26,
|
||||
116,
|
||||
-58,
|
||||
69,
|
||||
110,
|
||||
26,
|
||||
87,
|
||||
17,
|
||||
89,
|
||||
110,
|
||||
-57,
|
||||
108,
|
||||
-6,
|
||||
36,
|
||||
21,
|
||||
39,
|
||||
87,
|
||||
110,
|
||||
102,
|
||||
-6,
|
||||
-43,
|
||||
-82,
|
||||
-125,
|
||||
-85,
|
||||
-82,
|
||||
-120,
|
||||
-101,
|
||||
87,
|
||||
-112,
|
||||
111,
|
||||
15,
|
||||
-104,
|
||||
127,
|
||||
85,
|
||||
25,
|
||||
-102,
|
||||
81,
|
||||
20,
|
||||
58,
|
||||
51,
|
||||
75,
|
||||
-63,
|
||||
116,
|
||||
-22,
|
||||
0,
|
||||
60,
|
||||
30,
|
||||
29,
|
||||
30,
|
||||
-73,
|
||||
-115,
|
||||
72,
|
||||
-9,
|
||||
-1,
|
||||
-53,
|
||||
100,
|
||||
124,
|
||||
41,
|
||||
-22,
|
||||
106,
|
||||
78,
|
||||
31,
|
||||
11,
|
||||
114,
|
||||
-119,
|
||||
-19,
|
||||
17,
|
||||
92,
|
||||
71,
|
||||
-122,
|
||||
47,
|
||||
62,
|
||||
78,
|
||||
-67,
|
||||
-23,
|
||||
-55,
|
||||
-42,
|
||||
53,
|
||||
4,
|
||||
47,
|
||||
-67,
|
||||
-55,
|
||||
-123,
|
||||
6,
|
||||
121,
|
||||
34,
|
||||
-125,
|
||||
64,
|
||||
-114,
|
||||
91,
|
||||
-34,
|
||||
-46,
|
||||
-63,
|
||||
62,
|
||||
-34,
|
||||
104,
|
||||
82,
|
||||
36,
|
||||
41,
|
||||
-118,
|
||||
-3,
|
||||
70,
|
||||
15,
|
||||
-108,
|
||||
-48,
|
||||
-100,
|
||||
45,
|
||||
105,
|
||||
-85,
|
||||
-15,
|
||||
-61,
|
||||
-71,
|
||||
43,
|
||||
-39,
|
||||
-94,
|
||||
-110,
|
||||
-55,
|
||||
-34,
|
||||
89,
|
||||
-18,
|
||||
38,
|
||||
76,
|
||||
123,
|
||||
-40,
|
||||
13,
|
||||
51,
|
||||
-29,
|
||||
72,
|
||||
-11,
|
||||
59,
|
||||
-63,
|
||||
-18,
|
||||
-90,
|
||||
103,
|
||||
49,
|
||||
23,
|
||||
-92,
|
||||
-85,
|
||||
-68,
|
||||
-62,
|
||||
-59,
|
||||
-100,
|
||||
-69,
|
||||
-7,
|
||||
28,
|
||||
-58,
|
||||
95,
|
||||
69,
|
||||
15,
|
||||
-74,
|
||||
56,
|
||||
54,
|
||||
38
|
||||
],
|
||||
"eContent": [
|
||||
49,
|
||||
102,
|
||||
48,
|
||||
21,
|
||||
6,
|
||||
9,
|
||||
42,
|
||||
-122,
|
||||
72,
|
||||
-122,
|
||||
-9,
|
||||
13,
|
||||
1,
|
||||
9,
|
||||
3,
|
||||
49,
|
||||
8,
|
||||
6,
|
||||
6,
|
||||
103,
|
||||
-127,
|
||||
8,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
48,
|
||||
28,
|
||||
6,
|
||||
9,
|
||||
42,
|
||||
-122,
|
||||
72,
|
||||
-122,
|
||||
-9,
|
||||
13,
|
||||
1,
|
||||
9,
|
||||
5,
|
||||
49,
|
||||
15,
|
||||
23,
|
||||
13,
|
||||
49,
|
||||
57,
|
||||
49,
|
||||
50,
|
||||
49,
|
||||
54,
|
||||
49,
|
||||
55,
|
||||
50,
|
||||
50,
|
||||
51,
|
||||
56,
|
||||
90,
|
||||
48,
|
||||
47,
|
||||
6,
|
||||
9,
|
||||
42,
|
||||
-122,
|
||||
72,
|
||||
-122,
|
||||
-9,
|
||||
13,
|
||||
1,
|
||||
9,
|
||||
4,
|
||||
49,
|
||||
34,
|
||||
4,
|
||||
32,
|
||||
-122,
|
||||
-17,
|
||||
-90,
|
||||
-50,
|
||||
-85,
|
||||
-36,
|
||||
1,
|
||||
-60,
|
||||
98,
|
||||
23,
|
||||
122,
|
||||
117,
|
||||
-121,
|
||||
-30,
|
||||
85,
|
||||
120,
|
||||
53,
|
||||
83,
|
||||
-125,
|
||||
-57,
|
||||
-15,
|
||||
70,
|
||||
39,
|
||||
-114,
|
||||
64,
|
||||
123,
|
||||
39,
|
||||
-78,
|
||||
-76,
|
||||
-122,
|
||||
-50,
|
||||
-17
|
||||
],
|
||||
"encryptedDigest": [
|
||||
87,
|
||||
20,
|
||||
96,
|
||||
-119,
|
||||
-78,
|
||||
21,
|
||||
117,
|
||||
-35,
|
||||
91,
|
||||
41,
|
||||
86,
|
||||
-58,
|
||||
-21,
|
||||
72,
|
||||
-36,
|
||||
58,
|
||||
-79,
|
||||
-29,
|
||||
74,
|
||||
-103,
|
||||
-98,
|
||||
-46,
|
||||
-58,
|
||||
-84,
|
||||
110,
|
||||
29,
|
||||
-89,
|
||||
-15,
|
||||
127,
|
||||
121,
|
||||
92,
|
||||
125,
|
||||
106,
|
||||
81,
|
||||
-16,
|
||||
-96,
|
||||
-9,
|
||||
112,
|
||||
-33,
|
||||
-36,
|
||||
-52,
|
||||
-114,
|
||||
-14,
|
||||
-80,
|
||||
-61,
|
||||
-59,
|
||||
33,
|
||||
-104,
|
||||
120,
|
||||
-8,
|
||||
8,
|
||||
-29,
|
||||
-56,
|
||||
14,
|
||||
-79,
|
||||
-122,
|
||||
60,
|
||||
-23,
|
||||
-100,
|
||||
72,
|
||||
51,
|
||||
-31,
|
||||
-68,
|
||||
-70,
|
||||
37,
|
||||
18,
|
||||
-91,
|
||||
47,
|
||||
-19,
|
||||
-40,
|
||||
-4,
|
||||
-59,
|
||||
-66,
|
||||
88,
|
||||
103,
|
||||
32,
|
||||
-108,
|
||||
-77,
|
||||
-15,
|
||||
-44,
|
||||
-100,
|
||||
13,
|
||||
46,
|
||||
-45,
|
||||
-41,
|
||||
115,
|
||||
-18,
|
||||
110,
|
||||
-3,
|
||||
12,
|
||||
-17,
|
||||
85,
|
||||
111,
|
||||
-51,
|
||||
12,
|
||||
6,
|
||||
114,
|
||||
0,
|
||||
92,
|
||||
58,
|
||||
-26,
|
||||
-13,
|
||||
-40,
|
||||
34,
|
||||
43,
|
||||
11,
|
||||
87,
|
||||
-72,
|
||||
-1,
|
||||
-119,
|
||||
-86,
|
||||
13,
|
||||
79,
|
||||
-1,
|
||||
-72,
|
||||
16,
|
||||
-89,
|
||||
109,
|
||||
79,
|
||||
-99,
|
||||
-26,
|
||||
-110,
|
||||
-17,
|
||||
79,
|
||||
-2,
|
||||
-37,
|
||||
-59,
|
||||
-61,
|
||||
-3,
|
||||
-50,
|
||||
57,
|
||||
121,
|
||||
-10,
|
||||
-19,
|
||||
106,
|
||||
-76,
|
||||
-43,
|
||||
-33,
|
||||
-68,
|
||||
-1,
|
||||
5,
|
||||
22,
|
||||
-5,
|
||||
-39,
|
||||
-13,
|
||||
-10,
|
||||
73,
|
||||
33,
|
||||
28,
|
||||
-48,
|
||||
-24,
|
||||
-119,
|
||||
-49,
|
||||
127,
|
||||
-115,
|
||||
-99,
|
||||
59,
|
||||
10,
|
||||
-62,
|
||||
-47,
|
||||
-33,
|
||||
99,
|
||||
54,
|
||||
112,
|
||||
116,
|
||||
62,
|
||||
99,
|
||||
68,
|
||||
-87,
|
||||
-78,
|
||||
104,
|
||||
-84,
|
||||
-105,
|
||||
61,
|
||||
104,
|
||||
-56,
|
||||
-11,
|
||||
-56,
|
||||
-102,
|
||||
-39,
|
||||
-70,
|
||||
81,
|
||||
-58,
|
||||
-7,
|
||||
98,
|
||||
30,
|
||||
29,
|
||||
-116,
|
||||
6,
|
||||
-127,
|
||||
-103,
|
||||
-5,
|
||||
98,
|
||||
-64,
|
||||
-83,
|
||||
84,
|
||||
-4,
|
||||
100,
|
||||
70,
|
||||
112,
|
||||
29,
|
||||
114,
|
||||
109,
|
||||
-3,
|
||||
-72,
|
||||
-13,
|
||||
-4,
|
||||
-10,
|
||||
-14,
|
||||
-82,
|
||||
14,
|
||||
-38,
|
||||
112,
|
||||
-112,
|
||||
15,
|
||||
-10,
|
||||
-101,
|
||||
87,
|
||||
23,
|
||||
77,
|
||||
-31,
|
||||
101,
|
||||
121,
|
||||
-6,
|
||||
-125,
|
||||
117,
|
||||
126,
|
||||
103,
|
||||
100,
|
||||
10,
|
||||
95,
|
||||
-90,
|
||||
-103,
|
||||
-92,
|
||||
-118,
|
||||
32,
|
||||
124,
|
||||
-115,
|
||||
-116,
|
||||
4,
|
||||
73,
|
||||
-15,
|
||||
60,
|
||||
-16,
|
||||
-23,
|
||||
110,
|
||||
30,
|
||||
-122,
|
||||
42,
|
||||
-28,
|
||||
57,
|
||||
-59,
|
||||
-60,
|
||||
118,
|
||||
53,
|
||||
122
|
||||
],
|
||||
"photoBase64": "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABjElEQVR42mL8//8/AyUYiBQYmIw3..."
|
||||
}
|
||||
94
common/scripts/passportData/sha1_rsa_65537.ts
Normal file
94
common/scripts/passportData/sha1_rsa_65537.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
import { readFileSync, writeFileSync } from "fs";
|
||||
import { PassportData } from "../../src/utils/types";
|
||||
import { hash, assembleEContent, formatAndConcatenateDataHashes, formatMrz, hexToDecimal, arraysAreEqual, findSubarrayIndex } from "../../src/utils/utils";
|
||||
import * as forge from 'node-forge';
|
||||
import { assert } from "console";
|
||||
import { mock_dsc_key_sha1_rsa_4096 } from "../../src/constants/mockCertificates";
|
||||
|
||||
const sampleMRZ = "P<FRADUPONT<<ALPHONSE<HUGUES<ALBERT<<<<<<<<<24HB818324FRA0402111M3111115<<<<<<<<<<<<<<02"
|
||||
const sampleDataHashes = [
|
||||
[
|
||||
2,
|
||||
[-66, 82, -76, -21, -34, 33, 79, 50, -104, -120, -114, 35, 116, -32, 6, -14, -100, -115, -128, -8]
|
||||
],
|
||||
[
|
||||
3,
|
||||
[0, -62, 104, 108, -19, -10, 97, -26, 116, -58, 69, 110, 26, 87, 17, 89, 110, -57, 108, -6]
|
||||
],
|
||||
[
|
||||
14,
|
||||
[76, 123, -40, 13, 51, -29, 72, -11, 59, -63, -18, -90, 103, 49, 23, -92, -85, -68, -62, -59]
|
||||
]
|
||||
] as [number, number[]][]
|
||||
const signatureAlgorithm = 'sha1WithRSAEncryption'
|
||||
const hashLen = 20
|
||||
|
||||
export function genMockPassportData_sha1WithRSAEncryption_65537(): PassportData {
|
||||
const mrzHash = hash(signatureAlgorithm, formatMrz(sampleMRZ));
|
||||
const concatenatedDataHashes = formatAndConcatenateDataHashes(
|
||||
[[1, mrzHash], ...sampleDataHashes],
|
||||
hashLen,
|
||||
31 // could have been different
|
||||
);
|
||||
|
||||
const eContent = assembleEContent(hash(signatureAlgorithm, concatenatedDataHashes));
|
||||
|
||||
const privKey = forge.pki.privateKeyFromPem(mock_dsc_key_sha1_rsa_4096);
|
||||
const modulus = privKey.n.toString(16);
|
||||
|
||||
const md = forge.md.sha1.create();
|
||||
md.update(forge.util.binary.raw.encode(new Uint8Array(eContent)));
|
||||
|
||||
const signature = privKey.sign(md)
|
||||
const signatureBytes = Array.from(signature, (c: string) => c.charCodeAt(0));
|
||||
|
||||
return {
|
||||
mrz: sampleMRZ,
|
||||
signatureAlgorithm: signatureAlgorithm,
|
||||
pubKey: {
|
||||
modulus: hexToDecimal(modulus),
|
||||
exponent: '65537',
|
||||
},
|
||||
dataGroupHashes: concatenatedDataHashes,
|
||||
eContent: eContent,
|
||||
encryptedDigest: signatureBytes,
|
||||
photoBase64: "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABjElEQVR42mL8//8/AyUYiBQYmIw3..."
|
||||
}
|
||||
}
|
||||
|
||||
function verify(passportData: PassportData): boolean {
|
||||
const { mrz, signatureAlgorithm, pubKey, dataGroupHashes, eContent, encryptedDigest } = passportData;
|
||||
const formattedMrz = formatMrz(mrz);
|
||||
const mrzHash = hash(signatureAlgorithm, formattedMrz);
|
||||
const dg1HashOffset = findSubarrayIndex(dataGroupHashes, mrzHash)
|
||||
console.log('dg1HashOffset', dg1HashOffset);
|
||||
assert(dg1HashOffset !== -1, 'MRZ hash index not found in dataGroupHashes');
|
||||
|
||||
const concatHash = hash(signatureAlgorithm, dataGroupHashes)
|
||||
assert(
|
||||
arraysAreEqual(
|
||||
concatHash,
|
||||
eContent.slice(eContent.length - concatHash.length)
|
||||
),
|
||||
'concatHash is not at the right place in eContent'
|
||||
);
|
||||
|
||||
const modulus = new forge.jsbn.BigInteger(pubKey.modulus, 10);
|
||||
const exponent = new forge.jsbn.BigInteger(pubKey.exponent, 10);
|
||||
const rsaPublicKey = forge.pki.rsa.setPublicKey(modulus, exponent);
|
||||
|
||||
const md = forge.md.sha1.create();
|
||||
md.update(forge.util.binary.raw.encode(new Uint8Array(eContent)));
|
||||
|
||||
const signature = Buffer.from(encryptedDigest).toString(
|
||||
'binary',
|
||||
);
|
||||
|
||||
return rsaPublicKey.verify(md.digest().bytes(), signature);
|
||||
}
|
||||
|
||||
const mockPassportData = genMockPassportData_sha1WithRSAEncryption_65537();
|
||||
console.log("Passport Data:", JSON.stringify(mockPassportData, null, 2));
|
||||
console.log("Signature valid:", verify(mockPassportData));
|
||||
|
||||
writeFileSync(__dirname + '/passportData.json', JSON.stringify(mockPassportData, null, 2));
|
||||
107
common/scripts/passportData/sha256_rsa_65537.ts
Normal file
107
common/scripts/passportData/sha256_rsa_65537.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
import assert from "assert";
|
||||
import { PassportData } from "../../src/utils/types";
|
||||
import { hash, assembleEContent, formatAndConcatenateDataHashes, formatMrz, hexToDecimal, arraysAreEqual, findSubarrayIndex } from "../../src/utils/utils";
|
||||
import * as forge from 'node-forge';
|
||||
import { writeFileSync, readFileSync } from "fs";
|
||||
import { mock_dsc_key_sha256_rsa_4096 } from "../../src/constants/mockCertificates";
|
||||
|
||||
|
||||
const sampleMRZ = "P<FRADUPONT<<ALPHONSE<HUGUES<ALBERT<<<<<<<<<24HB818324FRA0402111M3111115<<<<<<<<<<<<<<02"
|
||||
const sampleDataHashes = [
|
||||
[
|
||||
2,
|
||||
[-66, 82, -76, -21, -34, 33, 79, 50, -104, -120, -114, 35, 116, -32, 6, -14, -100, -115, -128, -8, 10, 61, 98, 86, -8, 45, -49, -46, 90, -24, -81, 38]
|
||||
],
|
||||
[
|
||||
3,
|
||||
[0, -62, 104, 108, -19, -10, 97, -26, 116, -58, 69, 110, 26, 87, 17, 89, 110, -57, 108, -6, 36, 21, 39, 87, 110, 102, -6, -43, -82, -125, -85, -82]
|
||||
],
|
||||
[
|
||||
11,
|
||||
[-120, -101, 87, -112, 111, 15, -104, 127, 85, 25, -102, 81, 20, 58, 51, 75, -63, 116, -22, 0, 60, 30, 29, 30, -73, -115, 72, -9, -1, -53, 100, 124]
|
||||
],
|
||||
[
|
||||
12,
|
||||
[41, -22, 106, 78, 31, 11, 114, -119, -19, 17, 92, 71, -122, 47, 62, 78, -67, -23, -55, -42, 53, 4, 47, -67, -55, -123, 6, 121, 34, -125, 64, -114]
|
||||
],
|
||||
[
|
||||
13,
|
||||
[91, -34, -46, -63, 62, -34, 104, 82, 36, 41, -118, -3, 70, 15, -108, -48, -100, 45, 105, -85, -15, -61, -71, 43, -39, -94, -110, -55, -34, 89, -18, 38]
|
||||
],
|
||||
[
|
||||
14,
|
||||
[76, 123, -40, 13, 51, -29, 72, -11, 59, -63, -18, -90, 103, 49, 23, -92, -85, -68, -62, -59, -100, -69, -7, 28, -58, 95, 69, 15, -74, 56, 54, 38]
|
||||
]
|
||||
] as [number, number[]][]
|
||||
const signatureAlgorithm = 'sha256WithRSAEncryption'
|
||||
const hashLen = 32
|
||||
|
||||
export function genMockPassportData_sha256WithRSAEncryption_65537(): PassportData {
|
||||
const mrzHash = hash(signatureAlgorithm, formatMrz(sampleMRZ));
|
||||
const concatenatedDataHashes = formatAndConcatenateDataHashes(
|
||||
[[1, mrzHash], ...sampleDataHashes],
|
||||
hashLen,
|
||||
31
|
||||
);
|
||||
|
||||
const eContent = assembleEContent(hash(signatureAlgorithm, concatenatedDataHashes));
|
||||
|
||||
const privKey = forge.pki.privateKeyFromPem(mock_dsc_key_sha256_rsa_4096);
|
||||
const modulus = privKey.n.toString(16);
|
||||
|
||||
const md = forge.md.sha256.create();
|
||||
md.update(forge.util.binary.raw.encode(new Uint8Array(eContent)));
|
||||
|
||||
const signature = privKey.sign(md)
|
||||
const signatureBytes = Array.from(signature, (c: string) => c.charCodeAt(0));
|
||||
|
||||
return {
|
||||
mrz: sampleMRZ,
|
||||
signatureAlgorithm: signatureAlgorithm,
|
||||
pubKey: {
|
||||
modulus: hexToDecimal(modulus),
|
||||
exponent: '65537',
|
||||
},
|
||||
dataGroupHashes: concatenatedDataHashes,
|
||||
eContent: eContent,
|
||||
encryptedDigest: signatureBytes,
|
||||
photoBase64: "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABjElEQVR42mL8//8/AyUYiBQYmIw3..."
|
||||
}
|
||||
}
|
||||
|
||||
function verify(passportData: PassportData): boolean {
|
||||
const { mrz, signatureAlgorithm, pubKey, dataGroupHashes, eContent, encryptedDigest } = passportData;
|
||||
const formattedMrz = formatMrz(mrz);
|
||||
const mrzHash = hash(signatureAlgorithm, formattedMrz);
|
||||
const dg1HashOffset = findSubarrayIndex(dataGroupHashes, mrzHash)
|
||||
console.log('dg1HashOffset', dg1HashOffset);
|
||||
assert(dg1HashOffset !== -1, 'MRZ hash index not found in dataGroupHashes');
|
||||
|
||||
const concatHash = hash(signatureAlgorithm, dataGroupHashes)
|
||||
assert(
|
||||
arraysAreEqual(
|
||||
concatHash,
|
||||
eContent.slice(eContent.length - hashLen)
|
||||
),
|
||||
'concatHash is not at the right place in eContent'
|
||||
);
|
||||
|
||||
const modulus = new forge.jsbn.BigInteger(pubKey.modulus, 10);
|
||||
const exponent = new forge.jsbn.BigInteger(pubKey.exponent, 10);
|
||||
const rsaPublicKey = forge.pki.rsa.setPublicKey(modulus, exponent);
|
||||
|
||||
const md = forge.md.sha256.create();
|
||||
md.update(forge.util.binary.raw.encode(new Uint8Array(eContent)));
|
||||
|
||||
const signature = Buffer.from(encryptedDigest).toString(
|
||||
'binary',
|
||||
);
|
||||
|
||||
return rsaPublicKey.verify(md.digest().bytes(), signature);
|
||||
}
|
||||
|
||||
const mockPassportData = genMockPassportData_sha256WithRSAEncryption_65537();
|
||||
console.log("Passport Data:", JSON.stringify(mockPassportData, null, 2));
|
||||
console.log("Signature valid:", verify(mockPassportData));
|
||||
|
||||
writeFileSync(__dirname + '/passportData.json', JSON.stringify(mockPassportData, null, 2));
|
||||
127
common/scripts/passportData/sha256_rsapss_65537.ts
Normal file
127
common/scripts/passportData/sha256_rsapss_65537.ts
Normal file
@@ -0,0 +1,127 @@
|
||||
import assert from "assert";
|
||||
import { PassportData } from "../../src/utils/types";
|
||||
import { hash, assembleEContent, formatAndConcatenateDataHashes, formatMrz, arraysAreEqual, findSubarrayIndex } from "../../src/utils/utils";
|
||||
import * as forge from 'node-forge';
|
||||
import crypto from 'crypto';
|
||||
import { readFileSync, writeFileSync } from "fs";
|
||||
import { mock_dsc_key_sha256_rsapss_2048 } from "../../src/constants/mockCertificates";
|
||||
import { mock_dsc_sha256_rsapss_2048 } from "../../src/constants/mockCertificates";
|
||||
const sampleMRZ = "P<FRADUPONT<<ALPHONSE<HUGUES<ALBERT<<<<<<<<<24HB818324FRA0402111M3111115<<<<<<<<<<<<<<02"
|
||||
const sampleDataHashes = [
|
||||
[
|
||||
2,
|
||||
[-66, 82, -76, -21, -34, 33, 79, 50, -104, -120, -114, 35, 116, -32, 6, -14, -100, -115, -128, -8, 10, 61, 98, 86, -8, 45, -49, -46, 90, -24, -81, 38]
|
||||
],
|
||||
[
|
||||
3,
|
||||
[0, -62, 104, 108, -19, -10, 97, -26, 116, -58, 69, 110, 26, 87, 17, 89, 110, -57, 108, -6, 36, 21, 39, 87, 110, 102, -6, -43, -82, -125, -85, -82]
|
||||
],
|
||||
[
|
||||
11,
|
||||
[-120, -101, 87, -112, 111, 15, -104, 127, 85, 25, -102, 81, 20, 58, 51, 75, -63, 116, -22, 0, 60, 30, 29, 30, -73, -115, 72, -9, -1, -53, 100, 124]
|
||||
],
|
||||
[
|
||||
12,
|
||||
[41, -22, 106, 78, 31, 11, 114, -119, -19, 17, 92, 71, -122, 47, 62, 78, -67, -23, -55, -42, 53, 4, 47, -67, -55, -123, 6, 121, 34, -125, 64, -114]
|
||||
],
|
||||
[
|
||||
13,
|
||||
[91, -34, -46, -63, 62, -34, 104, 82, 36, 41, -118, -3, 70, 15, -108, -48, -100, 45, 105, -85, -15, -61, -71, 43, -39, -94, -110, -55, -34, 89, -18, 38]
|
||||
],
|
||||
[
|
||||
14,
|
||||
[76, 123, -40, 13, 51, -29, 72, -11, 59, -63, -18, -90, 103, 49, 23, -92, -85, -68, -62, -59, -100, -69, -7, 28, -58, 95, 69, 15, -74, 56, 54, 38]
|
||||
]
|
||||
] as [number, number[]][]
|
||||
const signatureAlgorithm = 'sha256WithRSASSAPSS'
|
||||
const hashLen = 32
|
||||
|
||||
export function genMockPassportData_sha256WithRSASSAPSS_65537(): PassportData {
|
||||
const privateKeyPem = forge.pki.privateKeyFromPem(mock_dsc_key_sha256_rsapss_2048);
|
||||
const privateKeyPemString = forge.pki.privateKeyToPem(privateKeyPem);
|
||||
const certificate = forge.pki.certificateFromPem(mock_dsc_sha256_rsapss_2048);
|
||||
|
||||
const publicKey = certificate.publicKey as forge.pki.rsa.PublicKey;
|
||||
|
||||
const modulus = (publicKey as any).n.toString(10);
|
||||
const exponent = (publicKey as any).e.toString(10);
|
||||
const salt = Buffer.from('dee959c7e06411361420ff80185ed57f3e6776afdee959c7e064113614201420', 'hex');
|
||||
|
||||
const mrzHash = hash(signatureAlgorithm, formatMrz(sampleMRZ));
|
||||
const concatenatedDataHashes = formatAndConcatenateDataHashes(
|
||||
[[1, mrzHash], ...sampleDataHashes],
|
||||
hashLen,
|
||||
30
|
||||
);
|
||||
|
||||
const eContent = assembleEContent(hash(signatureAlgorithm, concatenatedDataHashes));
|
||||
|
||||
const my_message = Buffer.from(eContent);
|
||||
const hash_algorithm = 'sha256';
|
||||
|
||||
const private_key = {
|
||||
key: privateKeyPemString,
|
||||
padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
|
||||
saltLength: salt.length,
|
||||
};
|
||||
|
||||
const signature = crypto.sign(hash_algorithm, my_message, private_key);
|
||||
const signatureArray = Array.from(signature, byte => byte < 128 ? byte : byte - 256);
|
||||
|
||||
return {
|
||||
mrz: sampleMRZ,
|
||||
signatureAlgorithm: signatureAlgorithm,
|
||||
pubKey: {
|
||||
modulus: modulus,
|
||||
exponent: exponent,
|
||||
},
|
||||
dataGroupHashes: concatenatedDataHashes,
|
||||
eContent: eContent,
|
||||
encryptedDigest: signatureArray,
|
||||
photoBase64: "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABjElEQVR42mL8//8/AyUYiBQYmIw3..."
|
||||
}
|
||||
}
|
||||
|
||||
function verify(passportData: PassportData): boolean {
|
||||
const { mrz, signatureAlgorithm, pubKey, dataGroupHashes, eContent, encryptedDigest } = passportData;
|
||||
const formattedMrz = formatMrz(mrz);
|
||||
const mrzHash = hash(signatureAlgorithm, formattedMrz);
|
||||
const dg1HashOffset = findSubarrayIndex(dataGroupHashes, mrzHash)
|
||||
console.log('dg1HashOffset', dg1HashOffset);
|
||||
assert(dg1HashOffset !== -1, 'MRZ hash index not found in dataGroupHashes');
|
||||
|
||||
const concatHash = hash(signatureAlgorithm, dataGroupHashes)
|
||||
assert(
|
||||
arraysAreEqual(
|
||||
concatHash,
|
||||
eContent.slice(eContent.length - concatHash.length)
|
||||
),
|
||||
'concatHash is not at the right place in eContent'
|
||||
);
|
||||
|
||||
const modulus = new forge.jsbn.BigInteger(pubKey.modulus, 10);
|
||||
const exponent = new forge.jsbn.BigInteger(pubKey.exponent, 10);
|
||||
const publicKey = forge.pki.setRsaPublicKey(modulus, exponent);
|
||||
const pem = forge.pki.publicKeyToPem(publicKey);
|
||||
const rsa_public = Buffer.from(pem);
|
||||
|
||||
const message = Buffer.from(eContent);
|
||||
const signature = Buffer.from(encryptedDigest);
|
||||
const hash_algorithm = "sha256";
|
||||
|
||||
const public_key = {
|
||||
key: rsa_public,
|
||||
padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
|
||||
saltLength: 32,
|
||||
};
|
||||
|
||||
const isVerified = crypto.verify(hash_algorithm, message, public_key, signature);
|
||||
|
||||
return isVerified;
|
||||
}
|
||||
|
||||
const mockPassportData = genMockPassportData_sha256WithRSASSAPSS_65537();
|
||||
console.log("Passport Data:", JSON.stringify(mockPassportData, null, 2));
|
||||
console.log("Signature valid:", verify(mockPassportData));
|
||||
|
||||
writeFileSync(__dirname + '/passportData.json', JSON.stringify(mockPassportData, null, 2));
|
||||
221
common/scripts/passportData/sha256_rsapss_65537_full_form.ts
Normal file
221
common/scripts/passportData/sha256_rsapss_65537_full_form.ts
Normal file
@@ -0,0 +1,221 @@
|
||||
// this file shows the fully explicit form of RSASSA-PSS, based on https://github.com/shigeki/ohtsu_rsa_pss_js/.
|
||||
// It can be useful to implement the circuit
|
||||
// It is currently broken so the line that errors is commented
|
||||
|
||||
import assert from "assert";
|
||||
import { PassportData } from "../../src/utils/types";
|
||||
import { hash, assembleEContent, formatAndConcatenateDataHashes, formatMrz, arraysAreEqual, findSubarrayIndex } from "../../src/utils/utils";
|
||||
import * as forge from 'node-forge';
|
||||
import crypto from 'crypto';
|
||||
import { writeFileSync } from "fs";
|
||||
|
||||
const sampleMRZ = "P<FRADUPONT<<ALPHONSE<HUGUES<ALBERT<<<<<<<<<24HB818324FRA0402111M3111115<<<<<<<<<<<<<<02"
|
||||
const sampleDataHashes = [
|
||||
[
|
||||
2,
|
||||
[-66, 82, -76, -21, -34, 33, 79, 50, -104, -120, -114, 35, 116, -32, 6, -14, -100, -115, -128, -8, 10, 61, 98, 86, -8, 45, -49, -46, 90, -24, -81, 38]
|
||||
],
|
||||
[
|
||||
3,
|
||||
[0, -62, 104, 108, -19, -10, 97, -26, 116, -58, 69, 110, 26, 87, 17, 89, 110, -57, 108, -6, 36, 21, 39, 87, 110, 102, -6, -43, -82, -125, -85, -82]
|
||||
],
|
||||
[
|
||||
11,
|
||||
[-120, -101, 87, -112, 111, 15, -104, 127, 85, 25, -102, 81, 20, 58, 51, 75, -63, 116, -22, 0, 60, 30, 29, 30, -73, -115, 72, -9, -1, -53, 100, 124]
|
||||
],
|
||||
[
|
||||
12,
|
||||
[41, -22, 106, 78, 31, 11, 114, -119, -19, 17, 92, 71, -122, 47, 62, 78, -67, -23, -55, -42, 53, 4, 47, -67, -55, -123, 6, 121, 34, -125, 64, -114]
|
||||
],
|
||||
[
|
||||
13,
|
||||
[91, -34, -46, -63, 62, -34, 104, 82, 36, 41, -118, -3, 70, 15, -108, -48, -100, 45, 105, -85, -15, -61, -71, 43, -39, -94, -110, -55, -34, 89, -18, 38]
|
||||
],
|
||||
[
|
||||
14,
|
||||
[76, 123, -40, 13, 51, -29, 72, -11, 59, -63, -18, -90, 103, 49, 23, -92, -85, -68, -62, -59, -100, -69, -7, 28, -58, 95, 69, 15, -74, 56, 54, 38]
|
||||
]
|
||||
] as [number, number[]][]
|
||||
const signatureAlgorithm = 'sha256WithRSASSAPSS'
|
||||
const hashLen = 32
|
||||
|
||||
export function genMockPassportData_sha256WithRSASSAPSS_65537(): PassportData {
|
||||
const keypair = forge.pki.rsa.generateKeyPair(2048);
|
||||
const privateKeyPem = forge.pki.privateKeyToPem(keypair.privateKey);
|
||||
|
||||
const publicKey = keypair.publicKey;
|
||||
const modulus = publicKey.n.toString(10);
|
||||
const exponent = publicKey.e.toString(10);
|
||||
const salt = Buffer.from('dee959c7e06411361420ff80185ed57f3e6776afdee959c7e064113614201420', 'hex');
|
||||
|
||||
const mrzHash = hash(signatureAlgorithm, formatMrz(sampleMRZ));
|
||||
const concatenatedDataHashes = formatAndConcatenateDataHashes(
|
||||
[[1, mrzHash], ...sampleDataHashes],
|
||||
hashLen,
|
||||
30
|
||||
);
|
||||
|
||||
const eContent = assembleEContent(hash(signatureAlgorithm, concatenatedDataHashes));
|
||||
|
||||
const my_message = Buffer.from(eContent);
|
||||
const sLen = 32;
|
||||
const keylen = 2048;
|
||||
const hash_algorithm = 'sha256';
|
||||
const emBits = keylen - 1;
|
||||
const emLen = Math.ceil(emBits / 8);
|
||||
const hLen = 32;
|
||||
|
||||
console.log('my_message:', my_message);
|
||||
|
||||
const private_key = {
|
||||
key: Buffer.from(privateKeyPem),
|
||||
padding: crypto.constants.RSA_NO_PADDING
|
||||
};
|
||||
|
||||
const padding1 = Buffer.alloc(8);
|
||||
const hash1 = crypto.createHash(hash_algorithm);
|
||||
hash1.update(my_message);
|
||||
const mHash = hash1.digest();
|
||||
|
||||
if (emLen < hLen + sLen + 2) {
|
||||
throw new Error('encoding error');
|
||||
}
|
||||
|
||||
var padding2 = Buffer.alloc(emLen - sLen - hLen - 2);
|
||||
const DB = Buffer.concat([padding2, Buffer.from('01', 'hex'), salt]);
|
||||
|
||||
const hash2 = crypto.createHash(hash_algorithm);
|
||||
hash2.update(Buffer.concat([padding1, mHash, salt]));
|
||||
|
||||
const H = hash2.digest();
|
||||
const dbMask = MGF1(H, emLen - hLen - 1, hLen, hash_algorithm);
|
||||
var maskedDB = BufferXOR(DB, dbMask);
|
||||
var b = Buffer.concat([maskedDB, H, Buffer.from('bc', 'hex')]);
|
||||
var signature = crypto.privateEncrypt(private_key, b);
|
||||
const signatureArray = Array.from(signature, byte => byte < 128 ? byte : byte - 256);
|
||||
|
||||
// const signatureBytes = Array.from(signature, (c: string) => c.charCodeAt(0));
|
||||
|
||||
return {
|
||||
mrz: sampleMRZ,
|
||||
signatureAlgorithm: signatureAlgorithm,
|
||||
pubKey: {
|
||||
modulus: modulus,
|
||||
exponent: exponent,
|
||||
},
|
||||
dataGroupHashes: concatenatedDataHashes,
|
||||
eContent: eContent,
|
||||
encryptedDigest: signatureArray,
|
||||
photoBase64: "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABjElEQVR42mL8//8/AyUYiBQYmIw3..."
|
||||
}
|
||||
}
|
||||
|
||||
function verify(passportData: PassportData): boolean {
|
||||
const { mrz, signatureAlgorithm, pubKey, dataGroupHashes, eContent, encryptedDigest } = passportData;
|
||||
const formattedMrz = formatMrz(mrz);
|
||||
const mrzHash = hash(signatureAlgorithm, formattedMrz);
|
||||
const dg1HashOffset = findSubarrayIndex(dataGroupHashes, mrzHash)
|
||||
assert(dg1HashOffset !== -1, 'MRZ hash index not found in dataGroupHashes');
|
||||
|
||||
const concatHash = hash(signatureAlgorithm, dataGroupHashes)
|
||||
assert(
|
||||
arraysAreEqual(
|
||||
concatHash,
|
||||
eContent.slice(eContent.length - concatHash.length)
|
||||
),
|
||||
'concatHash is not at the right place in eContent'
|
||||
);
|
||||
|
||||
const modulus = new forge.jsbn.BigInteger(pubKey.modulus, 10);
|
||||
const exponent = new forge.jsbn.BigInteger(pubKey.exponent, 10);
|
||||
const publicKey = forge.pki.setRsaPublicKey(modulus, exponent);
|
||||
const pem = forge.pki.publicKeyToPem(publicKey);
|
||||
const rsa_public = Buffer.from(pem);
|
||||
|
||||
const message = Buffer.from(eContent);
|
||||
const sLen = 32;
|
||||
const keylen = 2048;
|
||||
const signature = Buffer.from(encryptedDigest);
|
||||
const hash_algorithm = "sha256";
|
||||
const hLen = 32;
|
||||
|
||||
assert(Buffer.isBuffer(rsa_public));
|
||||
assert.strictEqual(typeof keylen, "number");
|
||||
assert.strictEqual(typeof hash_algorithm, "string");
|
||||
assert(Buffer.isBuffer(message));
|
||||
assert.strictEqual(typeof sLen, "number");
|
||||
assert(Buffer.isBuffer(signature));
|
||||
|
||||
const public_key = {
|
||||
key: rsa_public,
|
||||
padding: crypto.constants.RSA_NO_PADDING,
|
||||
};
|
||||
|
||||
var m = crypto.publicDecrypt(public_key, signature);
|
||||
|
||||
const emBits = keylen - 1;
|
||||
assert(hLen);
|
||||
const emLen = Math.ceil(emBits / 8);
|
||||
|
||||
const hash1 = crypto.createHash(hash_algorithm);
|
||||
hash1.update(message);
|
||||
const mHash = hash1.digest();
|
||||
|
||||
console.log("emLen", emLen);
|
||||
console.log("hLen", hLen);
|
||||
console.log("sLen", sLen);
|
||||
if (emLen < hLen + sLen + 2) throw new Error("inconsistent");
|
||||
|
||||
if (m[m.length - 1] !== 0xbc) throw new Error("inconsistent");
|
||||
|
||||
const maskedDB = m.slice(0, emLen - hLen - 1);
|
||||
const H = m.slice(emLen - hLen - 1, emLen - 1);
|
||||
|
||||
// if ((maskedDB[0] & 0x80) !== 0x00) throw new Error("inconsistent");
|
||||
|
||||
const dbMask = MGF1(H, emLen - hLen - 1, hLen, hash_algorithm);
|
||||
const DB = BufferXOR(maskedDB, dbMask);
|
||||
DB[0] = DB[0] & 0x7f;
|
||||
for (var i = 0; i < emLen - hLen - sLen - 2; i++) {
|
||||
assert.strictEqual(DB[i], 0x00);
|
||||
}
|
||||
assert.strictEqual(DB[emLen - hLen - sLen - 2], 0x01);
|
||||
const salt = DB.slice(-sLen);
|
||||
const MDash = Buffer.concat([Buffer.alloc(8), mHash, salt]);
|
||||
const hash2 = crypto.createHash(hash_algorithm);
|
||||
hash2.update(MDash);
|
||||
const HDash = hash2.digest();
|
||||
return HDash.equals(H);
|
||||
}
|
||||
|
||||
function MGF1(mgfSeed: Buffer, maskLen: number, hLen: number, hash_algorithm: string) {
|
||||
if (maskLen > 0xffffffff * hLen) {
|
||||
throw new Error("mask too long");
|
||||
}
|
||||
var T = [];
|
||||
for (var i = 0; i <= Math.ceil(maskLen / hLen) - 1; i++) {
|
||||
var C = Buffer.alloc(4);
|
||||
C.writeUInt32BE(i);
|
||||
const hash3 = crypto.createHash(hash_algorithm);
|
||||
hash3.update(Buffer.concat([mgfSeed, C]));
|
||||
T.push(hash3.digest());
|
||||
}
|
||||
return Buffer.concat(T).slice(0, maskLen);
|
||||
}
|
||||
|
||||
function BufferXOR(a: Buffer, b: Buffer) {
|
||||
assert(a.length === b.length, "Buffers must have the same length");
|
||||
var c = Buffer.alloc(a.length);
|
||||
for (var i = 0; i < a.length; i++) {
|
||||
c[i] = a[i] ^ b[i];
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
const mockPassportData = genMockPassportData_sha256WithRSASSAPSS_65537();
|
||||
console.log("Passport Data:", JSON.stringify(mockPassportData, null, 2));
|
||||
console.log("Signature valid:", verify(mockPassportData));
|
||||
|
||||
writeFileSync(__dirname + '/passportData.json', JSON.stringify(mockPassportData, null, 2));
|
||||
|
||||
113
common/scripts/passportData/sha384_ecdsa.ts
Normal file
113
common/scripts/passportData/sha384_ecdsa.ts
Normal file
@@ -0,0 +1,113 @@
|
||||
import assert from "assert";
|
||||
import { PassportData } from "../../src/utils/types";
|
||||
import { hash, assembleEContent, formatAndConcatenateDataHashes, formatMrz, arraysAreEqual, findSubarrayIndex } from "../../src/utils/utils";
|
||||
import * as forge from 'node-forge';
|
||||
import { writeFileSync } from "fs";
|
||||
import elliptic from 'elliptic';
|
||||
import * as crypto from 'crypto';
|
||||
|
||||
const sampleMRZ = "P<FRADUPONT<<ALPHONSE<HUGUES<ALBERT<<<<<<<<<24HB818324FRA0402111M3111115<<<<<<<<<<<<<<02"
|
||||
const sampleDataHashes = [
|
||||
[
|
||||
2,
|
||||
[-66, 82, -76, -21, -34, 33, 79, 50, -104, -120, -114, 35, 116, -32, 6, -14, -100, -115, -128, -8, 10, 61, 98, 86, -8, 45, -49, -46, 90, -24, -81, 38]
|
||||
],
|
||||
[
|
||||
3,
|
||||
[0, -62, 104, 108, -19, -10, 97, -26, 116, -58, 69, 110, 26, 87, 17, 89, 110, -57, 108, -6, 36, 21, 39, 87, 110, 102, -6, -43, -82, -125, -85, -82]
|
||||
],
|
||||
[
|
||||
11,
|
||||
[-120, -101, 87, -112, 111, 15, -104, 127, 85, 25, -102, 81, 20, 58, 51, 75, -63, 116, -22, 0, 60, 30, 29, 30, -73, -115, 72, -9, -1, -53, 100, 124]
|
||||
],
|
||||
[
|
||||
12,
|
||||
[41, -22, 106, 78, 31, 11, 114, -119, -19, 17, 92, 71, -122, 47, 62, 78, -67, -23, -55, -42, 53, 4, 47, -67, -55, -123, 6, 121, 34, -125, 64, -114]
|
||||
],
|
||||
[
|
||||
13,
|
||||
[91, -34, -46, -63, 62, -34, 104, 82, 36, 41, -118, -3, 70, 15, -108, -48, -100, 45, 105, -85, -15, -61, -71, 43, -39, -94, -110, -55, -34, 89, -18, 38]
|
||||
],
|
||||
[
|
||||
14,
|
||||
[76, 123, -40, 13, 51, -29, 72, -11, 59, -63, -18, -90, 103, 49, 23, -92, -85, -68, -62, -59, -100, -69, -7, 28, -58, 95, 69, 15, -74, 56, 54, 38]
|
||||
]
|
||||
] as [number, number[]][]
|
||||
const signatureAlgorithm = 'SHA384withECDSA'
|
||||
const hashLen = 46
|
||||
|
||||
export function genMockPassportData_SHA384withECDSA(): PassportData {
|
||||
const mrzHash = hash(signatureAlgorithm, formatMrz(sampleMRZ));
|
||||
sampleDataHashes.unshift([1, mrzHash]);
|
||||
const concatenatedDataHashes = formatAndConcatenateDataHashes(
|
||||
[[1, mrzHash], ...sampleDataHashes],
|
||||
hashLen,
|
||||
33
|
||||
);
|
||||
const eContent = assembleEContent(hash(signatureAlgorithm, concatenatedDataHashes));
|
||||
|
||||
const ec = new elliptic.ec('p384');
|
||||
const keyPair = ec.genKeyPair();
|
||||
const pubKey = keyPair.getPublic();
|
||||
|
||||
const md = forge.md.sha384.create();
|
||||
md.update(forge.util.binary.raw.encode(new Uint8Array(eContent)));
|
||||
const signature = keyPair.sign(md.digest().toHex(), 'hex');
|
||||
const signatureBytes = Array.from(Buffer.from(signature.toDER(), 'hex'));
|
||||
|
||||
const Qx = pubKey.getX().toString(16);
|
||||
const Qy = pubKey.getY().toString(16);
|
||||
|
||||
return {
|
||||
mrz: sampleMRZ,
|
||||
signatureAlgorithm: signatureAlgorithm,
|
||||
pubKey: {
|
||||
publicKeyQ: `(${Qx},${Qy},1,fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000fffffffc)`
|
||||
},
|
||||
dataGroupHashes: concatenatedDataHashes,
|
||||
eContent: eContent,
|
||||
encryptedDigest: signatureBytes,
|
||||
photoBase64: "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABjElEQVR42mL8//8/AyUYiBQYmIw3..."
|
||||
}
|
||||
}
|
||||
|
||||
function verify(passportData: PassportData): boolean {
|
||||
const { mrz, signatureAlgorithm, pubKey, dataGroupHashes, eContent, encryptedDigest } = passportData;
|
||||
const formattedMrz = formatMrz(mrz);
|
||||
const mrzHash = hash(signatureAlgorithm, formattedMrz);
|
||||
const dg1HashOffset = findSubarrayIndex(dataGroupHashes, mrzHash)
|
||||
console.log('dg1HashOffset', dg1HashOffset);
|
||||
assert(dg1HashOffset !== -1, 'MRZ hash index not found in dataGroupHashes');
|
||||
|
||||
const concatHash = hash(signatureAlgorithm, dataGroupHashes)
|
||||
assert(
|
||||
arraysAreEqual(
|
||||
concatHash,
|
||||
eContent.slice(eContent.length - concatHash.length)
|
||||
),
|
||||
'concatHash is not at the right place in eContent'
|
||||
);
|
||||
|
||||
const cleanPublicKeyQ = pubKey.publicKeyQ.replace(/[()]/g, '').split(',');
|
||||
const Qx = cleanPublicKeyQ[0];
|
||||
const Qy = cleanPublicKeyQ[1];
|
||||
|
||||
const ec = new elliptic.ec('p384');
|
||||
const key = ec.keyFromPublic({ x: Qx, y: Qy }, 'hex');
|
||||
|
||||
const messageBuffer = Buffer.from(eContent);
|
||||
const msgHash = crypto.createHash('sha384').update(messageBuffer).digest();
|
||||
|
||||
const signature = Buffer.from(encryptedDigest).toString('hex');
|
||||
|
||||
const isValid = key.verify(msgHash, signature);
|
||||
|
||||
return isValid;
|
||||
}
|
||||
|
||||
|
||||
const mockPassportData = genMockPassportData_SHA384withECDSA();
|
||||
console.log("Passport Data:", JSON.stringify(mockPassportData, null, 2));
|
||||
console.log("Signature valid:", verify(mockPassportData));
|
||||
|
||||
writeFileSync(__dirname + '/passportData.json', JSON.stringify(mockPassportData, null, 2));
|
||||
Reference in New Issue
Block a user