Files
self/app/utils/utils.ts
2023-09-28 18:31:26 +02:00

198 lines
5.5 KiB
TypeScript

import {DataHash, MrzInfo, PassportData} from '../types/passportData';
export function toUnsigned(byte: number) {
return byte & 0xff;
}
export function arraysAreEqual(array1: number[], array2: number[]) {
return (
array1.length === array2.length &&
array1.every((value, index) => value === array2[index])
);
}
export function toSigned(byte: number) {
return byte > 127 ? byte - 256 : byte;
}
export function dataHashesObjToArray(dataHashes: {
[key: string]: number[];
}): DataHash[] {
return 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)) as DataHash[];
}
export function assembleMrz(mrzInfo: MrzInfo) {
return (
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
);
}
export function formatMrz(mrz: string) {
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);
return mrzCharcodes;
}
// Example: [49, 15, 23, 13, 49, 57, 49, 50, 49, 54, 49, 55, 50, 50, 51, 56, 90]
// Is "191216172238Z" - 16th December 2019, 17:22:38 UTC
export function findTimeOfSignature(eContentDecomposed: any) {
const timeElement: any = eContentDecomposed.elements.find(
(element: any) => element.elements[0].identifier === '1.2.840.113549.1.9.5',
);
if (!timeElement) {
throw new Error('No time element found in eContentDecomposed');
}
const timeFound = timeElement.elements[1].elements[0].time;
// Adding the 4 bytes of the ASN.1 tag and length
// 49 : SET, 15 : LGT, 23 : UTCTIME, 13 : LGT
timeFound.unshift(...[49, 15, 23, 13]);
return timeFound;
}
export function parsePubKeyString(pubKeyString: string) {
const modulusMatch = pubKeyString.match(/modulus: ([\w\d]+)\s*public/);
const publicExponentMatch = pubKeyString.match(/public exponent: (\w+)/);
const modulus = modulusMatch ? modulusMatch[1] : null;
const exponent = publicExponentMatch ? publicExponentMatch[1] : null;
// console.log('Modulus:', modulus);
// console.log('Public Exponent:', exponent);
if (!modulus || !exponent) {
throw new Error('Could not parse public key string');
}
return {
modulus,
exponent,
};
}
export function formatAndConcatenateDataHashes(
mrzHash: number[],
dataHashes: DataHash[],
) {
// Let's replace the first array with the MRZ hash
dataHashes.shift();
dataHashes.unshift([1, mrzHash]);
let concat: number[] = []
// Starting sequence. Should be the same for everybody, but not sure
concat.push(...[
48, -126, 1, 37, 2, 1, 0, 48, 11, 6, 9, 96, -122, 72, 1, 101, 3, 4, 2, 1,
48, -126, 1, 17,
])
for(const dataHash of dataHashes) {
concat.push(...[48, 37, 2, 1, dataHash[0], 4, 32, ...dataHash[1]])
}
return concat;
}
export function assembleEContent(
messageDigest: number[],
timeOfSignature: number[],
) {
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(...timeOfSignature);
// 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(...messageDigest);
return constructedEContent;
}
export const toBinaryString = (byte: any) => {
const binary = (parseInt(byte, 10) & 0xFF).toString(2).padStart(8, '0');
return binary;
};
export function splitToWords(
number: bigint,
wordsize: bigint,
numberElement: bigint
) {
let t = number
const words: string[] = []
for (let i = BigInt(0); i < numberElement; ++i) {
const baseTwo = BigInt(2)
words.push(`${t % BigInt(Math.pow(Number(baseTwo), Number(wordsize)))}`)
t = BigInt(t / BigInt(Math.pow(Number(BigInt(2)), Number(wordsize))))
}
if (!(t == BigInt(0))) {
throw `Number ${number} does not fit in ${(
wordsize * numberElement
).toString()} bits`
}
return words
}
export function bytesToBigDecimal(arr: number[]): string {
let result = BigInt(0);
for (let i = 0; i < arr.length; i++) {
result = result * BigInt(256) + BigInt(arr[i] & 0xff);
}
return result.toString();
}
export function hexToDecimal(hex: string): string {
return BigInt(`0x${hex}`).toString();
}