Files
self/app/scripts/script.ts
2023-07-27 14:27:17 +02:00

156 lines
4.7 KiB
TypeScript

import * as crypto from 'crypto';
import {
dg1File,
dataHashes,
contentBytes,
eContent,
modulusHex,
exponentHex,
encryptedDigest,
} from './env';
import {toUnsigned, arraysAreEqual} from './utils';
import * as forge from 'node-forge';
const mrzInfo = dg1File.mrzInfo;
const mrz =
mrzInfo.documentCode +
'<' +
mrzInfo.issuingState +
mrzInfo.primaryIdentifier +
'<<' +
mrzInfo.secondaryIdentifier +
mrzInfo.documentNumber +
mrzInfo.documentNumberCheckDigit +
mrzInfo.nationality +
mrzInfo.dateOfBirth +
mrzInfo.dateOfBirthCheckDigit +
mrzInfo.gender.substring(0, 1) +
mrzInfo.dateOfExpiry +
mrzInfo.dateOfExpiryCheckDigit +
mrzInfo.optionalData1 +
mrzInfo.compositeCheckDigit;
// Transforms the dataHashes object into an array of arrays
const dataHashesAsArray = Object.keys(dataHashes)
.map(key => {
const dataHash = dataHashes[key as keyof typeof dataHashes];
return [Number(key), dataHash];
})
.sort((a, b) => (a[0] as number) - (b[0] as number));
console.log('dataHashesAsArray:', dataHashesAsArray);
console.log('mrz: ', mrz);
const mrzCharcodes = [...mrz].map(char => char.charCodeAt(0));
console.log('mrzCharcodes:', mrzCharcodes);
mrzCharcodes.unshift(88); // the length of the mrz data
mrzCharcodes.unshift(95, 31); // the MRZ_INFO_TAG
mrzCharcodes.unshift(91); // the new length of the whole array
mrzCharcodes.unshift(97); // the tag for DG1
console.log('mrzCharcodes with tags:', mrzCharcodes);
const hash = crypto.createHash('sha256');
hash.update(Buffer.from(mrzCharcodes));
const mrzHash = Array.from(hash.digest()).map(x => (x < 128 ? x : x - 256));
console.log('mrzHash:', mrzHash);
console.log('dataHashes["1"]:', dataHashes['1']);
console.log('Are they equal ?', arraysAreEqual(mrzHash, dataHashes['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('contentBytes', contentBytes);
console.log(
'Are they equal ?',
arraysAreEqual(concatenatedDataHashes, contentBytes),
);
// 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 = [];
// 191216172238Z : 16th December 2019, 17:22:38 UTC
const timeOfSignature = [
49, 15, 23, 13, 49, 57, 49, 50, 49, 54, 49, 55, 50, 50, 51, 56, 90,
];
// 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, 134, 72, 134, 247, 13, 1, 9, 3]);
// 2.23.136.1.1.1 is ldsSecurityObject
constructedEContent.push(...[49, 8, 6, 6, 103, 129, 8, 1, 1, 1]);
// 1.2.840.113549.1.9.5 is signing-time
constructedEContent.push(...[48, 28, 6, 9, 42, 134, 72, 134, 247, 13, 1, 9, 5]);
// time of the signature
constructedEContent.push(...timeOfSignature);
// 1.2.840.113549.1.9.4 is RFC_3369_MESSAGE_DIGEST_OID
constructedEContent.push(...[48, 47, 6, 9, 42, 134, 72, 134, 247, 13, 1, 9, 4]);
// TAG and length of the message digest
constructedEContent.push(...[49, 34, 4, 32]);
constructedEContent.push(
...concatenatedDataHashesHashDigest.map((byte: number) => toUnsigned(byte)),
);
console.log('constructedEContent', constructedEContent);
console.log('eContent', eContent);
console.log('Are they equal ?', arraysAreEqual(constructedEContent, eContent));
// now let's verify the signature
// Create the public key
const rsa = forge.pki.rsa;
const publicKey = rsa.setPublicKey(
new forge.jsbn.BigInteger(modulusHex, 16),
new forge.jsbn.BigInteger(exponentHex, 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 = 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.');
}