mirror of
https://github.com/selfxyz/self.git
synced 2026-02-08 13:25:59 -05:00
clean scripts and frontend
This commit is contained in:
57
app/App.tsx
57
app/App.tsx
@@ -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 () => {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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) {
|
||||
@@ -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
30
app/types/passportData.ts
Normal 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
104
app/utils/utils.ts
Normal 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,
|
||||
};
|
||||
}
|
||||
2104
app/yarn.lock
2104
app/yarn.lock
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user