Files
self/app/scripts/mrzToSig.ts
2023-07-27 17:19:20 +02:00

139 lines
4.1 KiB
TypeScript

import * as crypto from 'crypto';
import {
arraysAreEqual,
dataHashesObjToArray,
formatMrz,
assembleMrz,
findTimeOfSignature,
parsePubKeyString,
} from '../utils/utils';
import * as forge from 'node-forge';
import passportData from '../server/passportData.json';
// This script tests the whole flow from MRZ to signature
// The passportData is imported from passportData.json written by the server
const mrz = assembleMrz(passportData.mrzInfo);
console.log('mrz: ', mrz);
// Transforms the dataHashes object into an array of arrays
const dataHashesAsArray = dataHashesObjToArray(passportData.dataGroupHashes);
const formmattedMrz = formatMrz(mrz);
const hash = crypto.createHash('sha256');
hash.update(Buffer.from(formmattedMrz));
const mrzHash = Array.from(hash.digest()).map(x => (x < 128 ? x : x - 256));
console.log('mrzHash:', mrzHash);
console.log('dataHashesAsArray[0][1]:', dataHashesAsArray[0][1]);
console.log(
'Are they equal ?',
arraysAreEqual(mrzHash, dataHashesAsArray[0][1]),
);
// Let's replace the first array with the MRZ hash
dataHashesAsArray.shift();
dataHashesAsArray.unshift([1, mrzHash]);
// Concaténons les dataHashes :
const concatenatedDataHashes: number[] = [].concat(
...dataHashesAsArray.map((dataHash: any) => {
dataHash[1].unshift(...[48, 37, 2, 1, dataHash[0], 4, 32]);
return dataHash[1];
}),
);
// Starting sequence. Should be the same for everybody, but not sure
concatenatedDataHashes.unshift(
...[
48, -126, 1, 37, 2, 1, 0, 48, 11, 6, 9, 96, -122, 72, 1, 101, 3, 4, 2, 1,
48, -126, 1, 17,
],
);
console.log('concatenatedDataHashes', concatenatedDataHashes);
console.log(
'passportData.contentBytes.content.string',
passportData.contentBytes.content.string,
);
console.log(
'Are they equal ?',
arraysAreEqual(
concatenatedDataHashes,
passportData.contentBytes.content.string,
),
);
// please hash concatenatedDataHashes
const concatenatedDataHashesHash = crypto.createHash('sha256');
concatenatedDataHashesHash.update(Buffer.from(concatenatedDataHashes));
const concatenatedDataHashesHashDigest = Array.from(
concatenatedDataHashesHash.digest(),
).map(x => (x < 128 ? x : x - 256));
// Now let's reconstruct the eContent
const constructedEContent = [];
// Detailed description is in private file r&d.ts for now
// First, the tag and length, assumed to be always the same
constructedEContent.push(...[49, 102]);
// 1.2.840.113549.1.9.3 is RFC_3369_CONTENT_TYPE_OID
constructedEContent.push(
...[48, 21, 6, 9, 42, -122, 72, -122, -9, 13, 1, 9, 3],
);
// 2.23.136.1.1.1 is ldsSecurityObject
constructedEContent.push(...[49, 8, 6, 6, 103, -127, 8, 1, 1, 1]);
// 1.2.840.113549.1.9.5 is signing-time
constructedEContent.push(
...[48, 28, 6, 9, 42, -122, 72, -122, -9, 13, 1, 9, 5],
);
// time of the signature
constructedEContent.push(
...findTimeOfSignature(passportData.eContentDecomposed),
);
// 1.2.840.113549.1.9.4 is RFC_3369_MESSAGE_DIGEST_OID
constructedEContent.push(
...[48, 47, 6, 9, 42, -122, 72, -122, -9, 13, 1, 9, 4],
);
// TAG and length of the message digest
constructedEContent.push(...[49, 34, 4, 32]);
constructedEContent.push(...concatenatedDataHashesHashDigest);
console.log('constructedEContent', constructedEContent);
console.log('passportData.eContent', passportData.eContent);
console.log(
'Are they equal ?',
arraysAreEqual(constructedEContent, passportData.eContent),
);
// now let's verify the signature
const {modulus, exponent} = parsePubKeyString(passportData.publicKey);
// Create the public key
const rsa = forge.pki.rsa;
const publicKey = rsa.setPublicKey(
new forge.jsbn.BigInteger(modulus, 16),
new forge.jsbn.BigInteger(exponent, 16),
);
// SHA-256 hash of the eContent
const md = forge.md.sha256.create();
md.update(forge.util.binary.raw.encode(new Uint8Array(constructedEContent)));
const hashOfEContent = md.digest().getBytes();
// Signature verification
const signatureBytes = Buffer.from(passportData.encryptedDigest).toString(
'binary',
);
const valid = publicKey.verify(hashOfEContent, signatureBytes);
if (valid) {
console.log('The signature is valid.');
} else {
console.log('The signature is not valid.');
}