diff --git a/app/src/screens/UserInfo.tsx b/app/src/screens/UserInfo.tsx index 0d7ec86e..1ef646ae 100644 --- a/app/src/screens/UserInfo.tsx +++ b/app/src/screens/UserInfo.tsx @@ -2,15 +2,11 @@ import React from 'react'; import { YStack, Text, XStack, Separator } from 'tamagui'; import useUserStore from '../stores/userStore'; import { textBlack, separatorColor } from '../utils/colors'; -import { findSubarrayIndex } from '../../../common/src/utils/utils'; -import { PassportData } from '../../../common/src/utils/types'; -import { hash } from '../../../common/src/utils/utils'; -import { parseCertificate } from '../../../common/src/utils/certificates/handleCertificate'; +import { parsePassportData } from '../utils/parsePassportData'; const UserInfo: React.FC = () => { const { passportData } = useUserStore(); - const { eContent, signedAttr, dg1Hash, dgPresents } = passportData as PassportData; - const dg1HashOffset = dg1Hash ? findSubarrayIndex(eContent, dg1Hash.map(byte => byte > 127 ? byte - 256 : byte)) : undefined; + const passportMetaData = passportData ? parsePassportData(passportData) : null; const InfoRow = ({ label, value }: { label: string; value: string | number }) => ( @@ -19,69 +15,33 @@ const UserInfo: React.FC = () => { ); - function findHashSizeOfEContent(eContent: number[], signedAttr: number[]) { - const allHashes = ['sha512', 'sha384', 'sha256', 'sha1']; - for (const hashFunction of allHashes) { - const hashValue = hash(hashFunction, eContent); - const hashOffset = findSubarrayIndex(signedAttr, hashValue); - if (hashOffset !== -1) { - return { hashFunction, offset: hashOffset }; - } - } - } - - const { hashFunction: eContentHashFunction, offset: eContentHashOffset } = findHashSizeOfEContent(eContent, signedAttr) || { hashFunction: '', offset: 0 }; - const dscHashFunction = parseCertificate(passportData?.dsc || '').hashFunction; - - - return ( Passport Data Info - item.replace('DG', '')).join(',') || 'None'} - /> + - + - + + - + - + + - + + - + - + + ); }; diff --git a/app/src/utils/parsePassportData.ts b/app/src/utils/parsePassportData.ts new file mode 100644 index 00000000..49e444f5 --- /dev/null +++ b/app/src/utils/parsePassportData.ts @@ -0,0 +1,83 @@ +import { PassportData } from '../../../common/src/utils/types'; +import { findSubarrayIndex, formatMrz, hash } from '../../../common/src/utils/utils'; +import { parseCertificate } from '../../../common/src/utils/certificates/handleCertificate'; + +export interface PassportMetadata { + dataGroups: string; + dg1HashFunction: string; + dg1HashOffset: number; + eContentSize: number; + eContentHashFunction: string; + eContentHashOffset: number; + signedAttrSize: number; + signedAttrHashFunction: string; + countryCode?: string; +} + +export function findHashSizeOfEContent(eContent: number[], signedAttr: number[]) { + const allHashes = ['sha512', 'sha384', 'sha256', 'sha1']; + for (const hashFunction of allHashes) { + const hashValue = hash(hashFunction, eContent); + const hashOffset = findSubarrayIndex(signedAttr, hashValue); + if (hashOffset !== -1) { + return { hashFunction, offset: hashOffset }; + } + } + return { hashFunction: 'unknown', offset: -1 }; +} + +export function findDG1HashInEContent(mrz: string, eContent: number[]): { hash: number[], hashFunction: string } | null { + const hashFunctions = ['sha512', 'sha384', 'sha256', 'sha1']; + const formattedMrz = formatMrz(mrz); + + for (const hashFunction of hashFunctions) { + const hashValue = hash(hashFunction, formattedMrz); + const hashOffset = findSubarrayIndex(eContent, hashValue); + + if (hashOffset !== -1) { + return { hash: hashValue, hashFunction }; + } + } + return null; +} + +export function getCountryCodeFromMrz(mrz: string): string { + return mrz.substring(2, 5); +} + +export function parsePassportData(passportData: PassportData): PassportMetadata { + // Extract DG1 hash info + const dg1HashInfo = passportData.mrz ? + findDG1HashInEContent(passportData.mrz, passportData.eContent) : + null; + + // Use extracted DG1 hash if found, otherwise use provided dg1Hash + const dg1Hash = dg1HashInfo?.hash || passportData.dg1Hash; + const dg1HashFunction = dg1HashInfo?.hashFunction || 'unknown'; + + const dg1HashOffset = dg1Hash + ? findSubarrayIndex( + passportData.eContent, + dg1Hash.map(byte => byte > 127 ? byte - 256 : byte) + ) + : 0; + + const { hashFunction: eContentHashFunction, offset: eContentHashOffset } = + findHashSizeOfEContent(passportData.eContent, passportData.signedAttr); + + const dscHashFunction = passportData.dsc ? + parseCertificate(passportData.dsc).hashFunction : + 'unknown'; + + return { + dataGroups: passportData.dgPresents?.toString().split(',').map(item => item.replace('DG', '')).join(',') || 'None', + dg1HashFunction, + dg1HashOffset, + eContentSize: passportData.eContent?.length || 0, + eContentHashFunction, + eContentHashOffset, + signedAttrSize: passportData.signedAttr?.length || 0, + signedAttrHashFunction: dscHashFunction, + countryCode: passportData.mrz ? getCountryCodeFromMrz(passportData.mrz) : undefined + }; +} diff --git a/common/src/utils/utils.ts b/common/src/utils/utils.ts index fdb92983..96582f7e 100644 --- a/common/src/utils/utils.ts +++ b/common/src/utils/utils.ts @@ -409,8 +409,23 @@ export function generateMerkleProof(imt: LeanIMT, _index: number, maxDepth: numb return { merkleProofSiblings, merkleProofIndices, depthForThisOne }; } -export function findSubarrayIndex(arr: any[], subarray: any[]): number { - return arr.findIndex((_, index) => subarray.every((element, i) => element === arr[index + i])); +export function findSubarrayIndex(arr: number[], subArr: number[]): number { + if (!arr || !Array.isArray(arr) || !subArr || !Array.isArray(subArr)) { + console.warn('Invalid input to findSubarrayIndex:', { arr, subArr }); + return -1; + } + + if (subArr.length === 0) { + return -1; + } + + if (subArr.length > arr.length) { + return -1; + } + + return arr.findIndex((_, i) => + subArr.every((val, j) => arr[i + j] === val) + ); } export function extractRSFromSignature(signatureBytes: number[]): { r: string; s: string } { @@ -479,9 +494,9 @@ function checkStringLength(str: string) { function stringToBigInt(str: string): bigint { return BigInt( '1' + - Array.from(str) - .map((char) => char.charCodeAt(0).toString().padStart(3, '0')) - .join('') + Array.from(str) + .map((char) => char.charCodeAt(0).toString().padStart(3, '0')) + .join('') ); } diff --git a/registry/.gitignore b/registry/.gitignore index 31bdb75a..89c473c8 100644 --- a/registry/.gitignore +++ b/registry/.gitignore @@ -1,3 +1,4 @@ .env .env.local -outputs/ \ No newline at end of file +outputs/ +src/passport_data/passport_data/ \ No newline at end of file diff --git a/registry/src/passport_data/parse_passport_data.ts b/registry/src/passport_data/parse_passport_data.ts new file mode 100644 index 00000000..795be030 --- /dev/null +++ b/registry/src/passport_data/parse_passport_data.ts @@ -0,0 +1,55 @@ +import fs from 'fs'; +import path from 'path'; +import { PassportData } from '../../../common/src/utils/types'; +import { parsePassportData } from '../../../app/src/utils/parsePassportData'; + +function parsePassportFile(filePath: string) { + try { + const fileContent = fs.readFileSync(filePath, 'utf8'); + const passportData = JSON.parse(fileContent) as PassportData; + + const info = parsePassportData(passportData); + + // Print the results + console.log(`\nProcessing file: ${path.basename(filePath)}`); + console.log('----------------------------------------'); + if (info.countryCode) console.log(`Country Code: ${info.countryCode}`); + console.log(`Data Groups: ${info.dataGroups}`); + console.log(`DG1 Hash Function: ${info.dg1HashFunction}`); + console.log(`DG1 Hash Offset: ${info.dg1HashOffset}`); + console.log(`eContent Size: ${info.eContentSize}`); + console.log(`eContent Hash Function: ${info.eContentHashFunction}`); + console.log(`eContent Hash Offset: ${info.eContentHashOffset}`); + console.log(`Signed Attributes Size: ${info.signedAttrSize}`); + console.log(`Signed Attributes Hash Function: ${info.signedAttrHashFunction}`); + + } catch (error) { + console.error(`Error processing file ${filePath}:`, error); + } +} + +function main() { + const directoryPath = path.join(__dirname, 'passport_data'); + console.log(directoryPath); + + try { + const files = fs.readdirSync(directoryPath); + const jsonFiles = files.filter(file => file.endsWith('.json')); + + if (jsonFiles.length === 0) { + console.log('No JSON files found in the passport_data directory'); + return; + } + + jsonFiles.forEach(file => { + const filePath = path.join(directoryPath, file); + parsePassportFile(filePath); + }); + + } catch (error) { + console.error('Error reading directory:', error); + } +} + +// Execute the script +main();