clean scripts and frontend

This commit is contained in:
0xturboblitz
2023-07-27 17:19:20 +02:00
parent e0be30d799
commit aceff3b39c
7 changed files with 279 additions and 2160 deletions

View File

@@ -24,23 +24,20 @@ import {
// @ts-ignore
import PassportReader from 'react-native-passport-reader';
import {checkInputs, getFirstName} from './utils/checks';
import {DEFAULT_PNUMBER, DEFAULT_DOB, DEFAULT_DOE, DEFAULT_ADDRESS} from '@env';
import {
DEFAULT_PNUMBER,
DEFAULT_DOB,
DEFAULT_DOE,
DEFAULT_ADDRESS,
LOCAL_IP,
} from '@env';
import {PassportData} from './types/passportData';
import {dataHashesObjToArray} from './utils/utils';
console.log('DEFAULT_PNUMBER', DEFAULT_PNUMBER);
const CACHE_DATA_IN_LOCAL_SERVER = true;
const SKIP_SCAN = true;
type PassportData = {
mrzInfo: any;
publicKey: any;
publicKeyPEM: any;
dataGroupHashes: any;
eContent: any;
encryptedDigest: any;
contentBytes: any;
eContentDecomposed: any;
};
const SKIP_SCAN = false;
function App(): JSX.Element {
const isDarkMode = useColorScheme() === 'dark';
@@ -67,7 +64,7 @@ function App(): JSX.Element {
if (SKIP_SCAN && passportData === null) {
console.log('skipping scan step...');
fetch('http://192.168.1.22:3000/passportData')
fetch(`${LOCAL_IP}/passportData`)
.then(response => response.json())
.then(data => {
console.log('passport data fetched');
@@ -92,7 +89,7 @@ function App(): JSX.Element {
mrzInfo: JSON.parse(mrzInfo),
publicKey: publicKey,
publicKeyPEM: publicKeyPEM,
dataGroupHashes: JSON.parse(dataGroupHashes),
dataGroupHashes: dataHashesObjToArray(JSON.parse(dataGroupHashes)),
eContent: JSON.parse(eContent),
encryptedDigest: JSON.parse(encryptedDigest),
contentBytes: JSON.parse(contentBytes),
@@ -113,7 +110,7 @@ function App(): JSX.Element {
if (CACHE_DATA_IN_LOCAL_SERVER) {
// Caches data in local server to avoid having to scan the passport each time
// For development purposes only
fetch('http://192.168.1.22:3000/post', {
fetch(`${LOCAL_IP}/post`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
@@ -123,16 +120,10 @@ function App(): JSX.Element {
.then(response => response.json())
.then(data => console.log(data.message))
.catch(error => {
console.error('Error:', error);
console.log('error caching data in local server', error);
});
}
// 1. Compute the eContent from the dg1File
// 2. Format all the data as calldata for the verifier contract
// 3. Call the verifier contract with the calldata
setStep('scanCompleted');
}
@@ -157,11 +148,27 @@ function App(): JSX.Element {
}
const handleProve = () => {
// Generate a proof of passport here
if (passportData === null) {
console.log('passport data is null');
return;
}
// const mrz = computeMrz(passportData);
// 1. Compute the eContent from the mrzInfo and the dataGroupHashes
// 2. check that it matches the eContent from the passportData
// 3. Check the signature in js
// 4. Format all the data as inputs for the circuit
// 5. Generate a proof of passport
};
const handleMint = () => {
// mint "Proof of Passport" NFT to the address logic here
// 6. Format the proof and publicInputs as calldata for the verifier contract
// 7. Call the verifier contract with the calldata
};
const handleNative = async () => {

View File

@@ -12,15 +12,13 @@
"dependencies": {
"body-parser": "^1.20.2",
"buffer": "^6.0.3",
"crypto-js": "^4.1.1",
"express": "^4.18.2",
"node-forge": "^1.3.1",
"pvutils": "^1.1.3",
"react": "18.2.0",
"react-native": "0.72.3",
"react-native-passport-reader": "^1.0.3",
"asn1js": "^3.0.5",
"expo": "^49.0.5",
"react-native-fs": "^2.20.0"
"react-native-passport-reader": "^1.0.3"
},
"devDependencies": {
"@babel/core": "^7.20.0",
@@ -29,6 +27,7 @@
"@react-native/eslint-config": "^0.72.2",
"@react-native/metro-config": "^0.72.9",
"@tsconfig/react-native": "^3.0.0",
"@types/crypto-js": "^4.1.1",
"@types/express": "^4.17.17",
"@types/node-forge": "^1.3.3",
"@types/react": "^18.0.24",

View File

@@ -1,66 +1,37 @@
import * as crypto from 'crypto';
import {
dg1File,
dataHashes,
contentBytes,
eContent,
modulusHex,
exponentHex,
encryptedDigest,
} from './env';
import {toUnsigned, arraysAreEqual} from './utils';
arraysAreEqual,
dataHashesObjToArray,
formatMrz,
assembleMrz,
findTimeOfSignature,
parsePubKeyString,
} from '../utils/utils';
import * as forge from 'node-forge';
import passportData from '../server/passportData.json';
const mrzInfo = dg1File.mrzInfo;
// This script tests the whole flow from MRZ to signature
// The passportData is imported from passportData.json written by the server
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);
const mrz = assembleMrz(passportData.mrzInfo);
console.log('mrz: ', mrz);
const mrzCharcodes = [...mrz].map(char => char.charCodeAt(0));
// Transforms the dataHashes object into an array of arrays
const dataHashesAsArray = dataHashesObjToArray(passportData.dataGroupHashes);
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 formmattedMrz = formatMrz(mrz);
const hash = crypto.createHash('sha256');
hash.update(Buffer.from(mrzCharcodes));
hash.update(Buffer.from(formmattedMrz));
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']));
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();
@@ -82,10 +53,16 @@ concatenatedDataHashes.unshift(
);
console.log('concatenatedDataHashes', concatenatedDataHashes);
console.log('contentBytes', contentBytes);
console.log(
'passportData.contentBytes.content.string',
passportData.contentBytes.content.string,
);
console.log(
'Are they equal ?',
arraysAreEqual(concatenatedDataHashes, contentBytes),
arraysAreEqual(
concatenatedDataHashes,
passportData.contentBytes.content.string,
),
);
// please hash concatenatedDataHashes
@@ -99,44 +76,48 @@ const concatenatedDataHashesHashDigest = Array.from(
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]);
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, 129, 8, 1, 1, 1]);
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, 134, 72, 134, 247, 13, 1, 9, 5]);
constructedEContent.push(
...[48, 28, 6, 9, 42, -122, 72, -122, -9, 13, 1, 9, 5],
);
// time of the signature
constructedEContent.push(...timeOfSignature);
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, 134, 72, 134, 247, 13, 1, 9, 4]);
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.map((byte: number) => toUnsigned(byte)),
);
constructedEContent.push(...concatenatedDataHashesHashDigest);
console.log('constructedEContent', constructedEContent);
console.log('eContent', eContent);
console.log('Are they equal ?', arraysAreEqual(constructedEContent, eContent));
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(modulusHex, 16),
new forge.jsbn.BigInteger(exponentHex, 16),
new forge.jsbn.BigInteger(modulus, 16),
new forge.jsbn.BigInteger(exponent, 16),
);
// SHA-256 hash of the eContent
@@ -145,7 +126,9 @@ md.update(forge.util.binary.raw.encode(new Uint8Array(constructedEContent)));
const hashOfEContent = md.digest().getBytes();
// Signature verification
const signatureBytes = encryptedDigest.toString('binary');
const signatureBytes = Buffer.from(passportData.encryptedDigest).toString(
'binary',
);
const valid = publicKey.verify(hashOfEContent, signatureBytes);
if (valid) {

View File

@@ -1,14 +0,0 @@
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;
}

30
app/types/passportData.ts Normal file
View File

@@ -0,0 +1,30 @@
export type MrzInfo = {
compositeCheckDigit: string;
dateOfBirth: string;
dateOfBirthCheckDigit: string;
dateOfExpiry: string;
dateOfExpiryCheckDigit: string;
documentCode: string;
documentNumber: string;
documentNumberCheckDigit: string;
documentType: number;
gender: string;
issuingState: string;
nationality: string;
optionalData1: string;
primaryIdentifier: string;
secondaryIdentifier: string;
};
export type DataHash = [number, number[]];
export type PassportData = {
mrzInfo: MrzInfo;
publicKey: any;
publicKeyPEM: string;
dataGroupHashes: DataHash[];
eContent: any;
encryptedDigest: any;
contentBytes: any;
eContentDecomposed: any;
};

104
app/utils/utils.ts Normal file
View File

@@ -0,0 +1,104 @@
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;
console.log('timeFound', timeFound);
// 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+)/);
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,
};
}

File diff suppressed because it is too large Load Diff