mirror of
https://github.com/selfxyz/self.git
synced 2026-01-10 07:08:10 -05:00
Merge pull request #310 from zk-passport/fix/change-lower-dependency
Fix/change lower dependency
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
pragma circom 2.1.9;
|
||||
|
||||
include "../../../utils/crypto/signature/rsa/verifyRsa65537Pkcs1v1_5.circom";
|
||||
|
||||
template VerifyRsaPkcs1v1_5Tester() {
|
||||
signal input signature[35];
|
||||
signal input modulus[35];
|
||||
signal input message[35];
|
||||
|
||||
VerifyRsa65537Pkcs1v1_5(120,35,224)(signature, modulus, message);
|
||||
}
|
||||
|
||||
component main = VerifyRsaPkcs1v1_5Tester();
|
||||
@@ -3,10 +3,12 @@ pragma circom 2.1.9;
|
||||
include "circomlib/circuits/bitify.circom";
|
||||
|
||||
// PKCS1v1.5 Padding Scheme
|
||||
// Reference: https://datatracker.ietf.org/doc/html/rfc8017#section-9.2
|
||||
// 0x00 || 0x01 || PS || 0x00 || OID || Hash
|
||||
// PS is a sequence of 0xFF bytes that is padded so that the data to be signed matches the length of the key.
|
||||
// OID is the object identifier for the hash function used.
|
||||
// For SHA1, the OID is 0x3021300906052b0e03021a05000414
|
||||
// For SHA224, the OID is 0x302d300d06096086480165030402040500041c
|
||||
// For SHA256, the OID is 0x3031300d060960864801650304020105000420
|
||||
// For SHA384, the OID is 0x3041300d060960864801650304020205000430
|
||||
// For SHA512, the OID is 0x3051300d060960864801650304020305000440
|
||||
@@ -105,6 +107,9 @@ function getOID(HASH_SIZE) {
|
||||
if (HASH_SIZE == 160) {
|
||||
return 0x3021300906052b0e03021a05000414;
|
||||
}
|
||||
if (HASH_SIZE == 224) {
|
||||
return 0x302d300d06096086480165030402040500041c;
|
||||
}
|
||||
if (HASH_SIZE == 256) {
|
||||
return 0x3031300d060960864801650304020105000420;
|
||||
}
|
||||
@@ -125,6 +130,9 @@ function getOIDSize(HASH_SIZE) {
|
||||
if (HASH_SIZE == 160) {
|
||||
return 120;
|
||||
}
|
||||
if (HASH_SIZE == 224) {
|
||||
return 152;
|
||||
}
|
||||
if (HASH_SIZE == 256) {
|
||||
return 152;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
pragma circom 2.1.9;
|
||||
|
||||
include "@zk-email/circuits/lib/fp.circom";
|
||||
include "@zk-email/circuits/lib/bigint.circom";
|
||||
include "./pkcs1v1_5Padding.circom";
|
||||
include "../FpPowMod.circom";
|
||||
|
||||
|
||||
@@ -33,6 +33,11 @@ export const generateMockRsaPkcs1v1_5Inputs = (signatureAlgorithm: SignatureAlgo
|
||||
signAlgorithm = signatureAlgorithm.includes('sha256') ? 'sha256' : 'sha512';
|
||||
publicExponent = 65537;
|
||||
break;
|
||||
case 'rsa_sha224_65537_2048':
|
||||
modulusLength = 2048;
|
||||
signAlgorithm = 'sha224';
|
||||
publicExponent = 65537;
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unsupported signature algorithm: ${signatureAlgorithm}`);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { describe, it } from 'mocha';
|
||||
import path from 'path';
|
||||
import { generateMockRsaPkcs1v1_5Inputs } from './generateMockInputsInCircuits';
|
||||
import { SignatureAlgorithm } from '../../../common/src/utils/types';
|
||||
|
||||
import { expect } from 'chai';
|
||||
describe('VerifyRsaPkcs1v1_5 Circuit Test', function () {
|
||||
this.timeout(0);
|
||||
/** Some tests are disabled to avoid overloading the CI/CD pipeline - the commented rsa verifications will however be tested in prove.test.ts and dsc.test.ts **/
|
||||
@@ -14,6 +14,7 @@ describe('VerifyRsaPkcs1v1_5 Circuit Test', function () {
|
||||
'rsa_sha256_65537_3072',
|
||||
'rsa_sha256_65537_4096',
|
||||
'rsa_sha512_65537_4096',
|
||||
'rsa_sha224_65537_2048',
|
||||
];
|
||||
|
||||
rsaAlgorithms.forEach((algorithm) => {
|
||||
@@ -41,5 +42,49 @@ describe('VerifyRsaPkcs1v1_5 Circuit Test', function () {
|
||||
// Check constraints
|
||||
await circuit.checkConstraints(witness);
|
||||
});
|
||||
|
||||
it('Should fail to verify RSA signature with invalid signature', async function () {
|
||||
const { signature, modulus, message } = generateMockRsaPkcs1v1_5Inputs(algorithm);
|
||||
|
||||
const invalidSignature = signature.map((byte: string) => String((parseInt(byte) + 1) % 256));
|
||||
const circuit = await wasmTester(
|
||||
path.join(__dirname, `../../circuits/tests/utils/rsa/test_${algorithm}.circom`),
|
||||
{
|
||||
include: ['node_modules', './node_modules/@zk-kit/binary-merkle-root.circom/src'],
|
||||
}
|
||||
);
|
||||
|
||||
try {
|
||||
await circuit.calculateWitness({
|
||||
signature: invalidSignature,
|
||||
modulus,
|
||||
message,
|
||||
});
|
||||
} catch (error) {
|
||||
expect(error.message).to.include('Assert Failed');
|
||||
}
|
||||
});
|
||||
|
||||
it('Should fail to verify RSA signature with invalid message', async function () {
|
||||
const { signature, modulus, message } = generateMockRsaPkcs1v1_5Inputs(algorithm);
|
||||
|
||||
const invalidMessage = message.map((byte: string) => String((parseInt(byte) + 1) % 256));
|
||||
const circuit = await wasmTester(
|
||||
path.join(__dirname, `../../circuits/tests/utils/rsa/test_${algorithm}.circom`),
|
||||
{
|
||||
include: ['node_modules', './node_modules/@zk-kit/binary-merkle-root.circom/src'],
|
||||
}
|
||||
);
|
||||
|
||||
try {
|
||||
await circuit.calculateWitness({
|
||||
signature,
|
||||
modulus,
|
||||
message: invalidMessage,
|
||||
});
|
||||
} catch (error) {
|
||||
expect(error.message).to.include('Assert Failed');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -223,8 +223,8 @@ export function generateCircuitInputsProve(
|
||||
|
||||
const dg1PaddingFunction =
|
||||
passportMetadata.dg1HashFunction === 'sha1' ||
|
||||
passportMetadata.dg1HashFunction === 'sha224' ||
|
||||
passportMetadata.dg1HashFunction === 'sha256'
|
||||
passportMetadata.dg1HashFunction === 'sha224' ||
|
||||
passportMetadata.dg1HashFunction === 'sha256'
|
||||
? shaPad
|
||||
: sha384_512Pad;
|
||||
|
||||
@@ -235,8 +235,8 @@ export function generateCircuitInputsProve(
|
||||
|
||||
const eContentPaddingFunction =
|
||||
passportMetadata.eContentHashFunction === 'sha1' ||
|
||||
passportMetadata.eContentHashFunction === 'sha224' ||
|
||||
passportMetadata.eContentHashFunction === 'sha256'
|
||||
passportMetadata.eContentHashFunction === 'sha224' ||
|
||||
passportMetadata.eContentHashFunction === 'sha256'
|
||||
? shaPad
|
||||
: sha384_512Pad;
|
||||
const [signedAttrPadded, signedAttrPaddedLen] = eContentPaddingFunction(
|
||||
|
||||
@@ -2,153 +2,147 @@ import { PassportData } from '../../../common/src/utils/types';
|
||||
import { findSubarrayIndex, formatMrz, getHashLen, hash } from './utils';
|
||||
import { parseCertificateSimple } from './certificate_parsing/parseCertificateSimple';
|
||||
import {
|
||||
CertificateData,
|
||||
PublicKeyDetailsECDSA,
|
||||
PublicKeyDetailsRSA,
|
||||
PublicKeyDetailsRSAPSS,
|
||||
CertificateData,
|
||||
PublicKeyDetailsECDSA,
|
||||
PublicKeyDetailsRSA,
|
||||
PublicKeyDetailsRSAPSS,
|
||||
} from './certificate_parsing/dataStructure';
|
||||
import { hashAlgos } from '../constants/constants';
|
||||
import { brutforceSignatureAlgorithm } from './brutForcePassportSignature';
|
||||
import { DscCertificateMetaData, parseDscCertificateData } from './parseDscCertificateData';
|
||||
|
||||
export interface PassportMetadata {
|
||||
dataGroups: string;
|
||||
dg1HashFunction: string;
|
||||
dg1HashOffset: number;
|
||||
dgPaddingBytes: number;
|
||||
eContentSize: number;
|
||||
eContentHashFunction: string;
|
||||
eContentHashOffset: number;
|
||||
signedAttrSize: number;
|
||||
signedAttrHashFunction: string;
|
||||
signatureAlgorithm: string;
|
||||
saltLength: number;
|
||||
curveOrExponent: string;
|
||||
signatureAlgorithmBits: number;
|
||||
countryCode: string;
|
||||
cscaFound: boolean;
|
||||
cscaHashFunction: string;
|
||||
cscaSignature: string;
|
||||
cscaSaltLength: number;
|
||||
cscaCurveOrExponent: string;
|
||||
cscaSignatureAlgorithmBits: number;
|
||||
dsc: string;
|
||||
dataGroups: string;
|
||||
dg1HashFunction: string;
|
||||
dg1HashOffset: number;
|
||||
dgPaddingBytes: number;
|
||||
eContentSize: number;
|
||||
eContentHashFunction: string;
|
||||
eContentHashOffset: number;
|
||||
signedAttrSize: number;
|
||||
signedAttrHashFunction: string;
|
||||
signatureAlgorithm: string;
|
||||
saltLength: number;
|
||||
curveOrExponent: string;
|
||||
signatureAlgorithmBits: number;
|
||||
countryCode: string;
|
||||
cscaFound: boolean;
|
||||
cscaHashFunction: string;
|
||||
cscaSignature: string;
|
||||
cscaSaltLength: number;
|
||||
cscaCurveOrExponent: string;
|
||||
cscaSignatureAlgorithmBits: number;
|
||||
dsc: string;
|
||||
}
|
||||
|
||||
function findHashSizeOfEContent(eContent: number[], signedAttr: number[]) {
|
||||
for (const hashFunction of hashAlgos) {
|
||||
const hashValue = hash(hashFunction, eContent);
|
||||
const hashOffset = findSubarrayIndex(signedAttr, hashValue as number[]);
|
||||
if (hashOffset !== -1) {
|
||||
return { hashFunction, offset: hashOffset };
|
||||
}
|
||||
for (const hashFunction of hashAlgos) {
|
||||
const hashValue = hash(hashFunction, eContent);
|
||||
const hashOffset = findSubarrayIndex(signedAttr, hashValue as number[]);
|
||||
if (hashOffset !== -1) {
|
||||
return { hashFunction, offset: hashOffset };
|
||||
}
|
||||
return { hashFunction: 'unknown', offset: -1 };
|
||||
}
|
||||
return { hashFunction: 'unknown', offset: -1 };
|
||||
}
|
||||
|
||||
function findDG1HashInEContent(
|
||||
mrz: string,
|
||||
eContent: number[]
|
||||
mrz: string,
|
||||
eContent: number[]
|
||||
): { hash: number[]; hashFunction: string; offset: number } | null {
|
||||
const formattedMrz = formatMrz(mrz);
|
||||
const formattedMrz = formatMrz(mrz);
|
||||
|
||||
for (const hashFunction of hashAlgos) {
|
||||
const hashValue = hash(hashFunction, formattedMrz);
|
||||
const normalizedHash = (hashValue as number[]).map((byte) => (byte > 127 ? byte - 256 : byte));
|
||||
const hashOffset = findSubarrayIndex(eContent, normalizedHash);
|
||||
for (const hashFunction of hashAlgos) {
|
||||
const hashValue = hash(hashFunction, formattedMrz);
|
||||
const normalizedHash = (hashValue as number[]).map((byte) => (byte > 127 ? byte - 256 : byte));
|
||||
const hashOffset = findSubarrayIndex(eContent, normalizedHash);
|
||||
|
||||
if (hashOffset !== -1) {
|
||||
return { hash: hashValue as number[], hashFunction, offset: hashOffset };
|
||||
}
|
||||
if (hashOffset !== -1) {
|
||||
return { hash: hashValue as number[], hashFunction, offset: hashOffset };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function getDgPaddingBytes(passportData: PassportData, dg1HashFunction: string): number {
|
||||
const formattedMrz = formatMrz(passportData.mrz);
|
||||
const hashValue = hash(dg1HashFunction, formattedMrz);
|
||||
const normalizedHash = (hashValue as number[]).map((byte) => (byte > 127 ? byte - 256 : byte));
|
||||
const dg1HashOffset = findSubarrayIndex(passportData.eContent, normalizedHash);
|
||||
const dg2Hash = passportData.dg2Hash;
|
||||
const normalizedDg2Hash = (dg2Hash as number[]).map((byte) => (byte > 127 ? byte - 256 : byte));
|
||||
const dg2HashOffset = findSubarrayIndex(passportData.eContent, normalizedDg2Hash);
|
||||
return dg2HashOffset - dg1HashOffset - getHashLen(dg1HashFunction);
|
||||
const formattedMrz = formatMrz(passportData.mrz);
|
||||
const hashValue = hash(dg1HashFunction, formattedMrz);
|
||||
const normalizedHash = (hashValue as number[]).map((byte) => (byte > 127 ? byte - 256 : byte));
|
||||
const dg1HashOffset = findSubarrayIndex(passportData.eContent, normalizedHash);
|
||||
const dg2Hash = passportData.dg2Hash;
|
||||
const normalizedDg2Hash = (dg2Hash as number[]).map((byte) => (byte > 127 ? byte - 256 : byte));
|
||||
const dg2HashOffset = findSubarrayIndex(passportData.eContent, normalizedDg2Hash);
|
||||
return dg2HashOffset - dg1HashOffset - getHashLen(dg1HashFunction);
|
||||
}
|
||||
|
||||
export function getCountryCodeFromMrz(mrz: string): string {
|
||||
return mrz.substring(2, 5);
|
||||
return mrz.substring(2, 5);
|
||||
}
|
||||
|
||||
export function getCurveOrExponent(certData: CertificateData): string {
|
||||
if (certData.signatureAlgorithm === 'rsapss' || certData.signatureAlgorithm === 'rsa') {
|
||||
return (certData.publicKeyDetails as PublicKeyDetailsRSA).exponent;
|
||||
}
|
||||
return (certData.publicKeyDetails as PublicKeyDetailsECDSA).curve;
|
||||
if (certData.signatureAlgorithm === 'rsapss' || certData.signatureAlgorithm === 'rsa') {
|
||||
return (certData.publicKeyDetails as PublicKeyDetailsRSA).exponent;
|
||||
}
|
||||
return (certData.publicKeyDetails as PublicKeyDetailsECDSA).curve;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
export function parsePassportData(passportData: PassportData): PassportMetadata {
|
||||
const dg1HashInfo = passportData.mrz
|
||||
? findDG1HashInEContent(passportData.mrz, passportData.eContent)
|
||||
: null;
|
||||
const dg1HashInfo = passportData.mrz
|
||||
? findDG1HashInEContent(passportData.mrz, passportData.eContent)
|
||||
: null;
|
||||
|
||||
const dg1HashFunction = dg1HashInfo?.hashFunction || 'unknown';
|
||||
const dg1HashOffset = dg1HashInfo?.offset || 0;
|
||||
let dgPaddingBytes = -1;
|
||||
try {
|
||||
dgPaddingBytes = getDgPaddingBytes(passportData, dg1HashFunction);
|
||||
} catch (error) {
|
||||
console.error('Error getting DG padding bytes:', error);
|
||||
}
|
||||
const { hashFunction: eContentHashFunction, offset: eContentHashOffset } = findHashSizeOfEContent(
|
||||
passportData.eContent,
|
||||
passportData.signedAttr
|
||||
);
|
||||
const dg1HashFunction = dg1HashInfo?.hashFunction || 'unknown';
|
||||
const dg1HashOffset = dg1HashInfo?.offset || 0;
|
||||
let dgPaddingBytes = -1;
|
||||
try {
|
||||
dgPaddingBytes = getDgPaddingBytes(passportData, dg1HashFunction);
|
||||
} catch (error) {
|
||||
console.error('Error getting DG padding bytes:', error);
|
||||
}
|
||||
const { hashFunction: eContentHashFunction, offset: eContentHashOffset } = findHashSizeOfEContent(
|
||||
passportData.eContent,
|
||||
passportData.signedAttr
|
||||
);
|
||||
|
||||
const brutForcedPublicKeyDetails = brutforceSignatureAlgorithm(passportData);
|
||||
const brutForcedPublicKeyDetails = brutforceSignatureAlgorithm(passportData);
|
||||
|
||||
let parsedDsc = null;
|
||||
let dscSignatureAlgorithmBits = 0;
|
||||
let parsedDsc = null;
|
||||
let dscSignatureAlgorithmBits = 0;
|
||||
|
||||
let brutForcedPublicKeyDetailsDsc: DscCertificateMetaData;
|
||||
let brutForcedPublicKeyDetailsDsc: DscCertificateMetaData;
|
||||
|
||||
if (passportData.dsc) {
|
||||
parsedDsc = parseCertificateSimple(passportData.dsc);
|
||||
dscSignatureAlgorithmBits = parseInt(parsedDsc.publicKeyDetails?.bits || '0');
|
||||
if (passportData.dsc) {
|
||||
parsedDsc = parseCertificateSimple(passportData.dsc);
|
||||
dscSignatureAlgorithmBits = parseInt(parsedDsc.publicKeyDetails?.bits || '0');
|
||||
|
||||
brutForcedPublicKeyDetailsDsc = parseDscCertificateData(parsedDsc)
|
||||
brutForcedPublicKeyDetailsDsc = parseDscCertificateData(parsedDsc);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
return {
|
||||
dataGroups:
|
||||
passportData.dgPresents
|
||||
?.toString()
|
||||
.split(',')
|
||||
.map((item) => item.replace('DG', ''))
|
||||
.join(',') || 'None',
|
||||
dg1HashFunction,
|
||||
dg1HashOffset,
|
||||
dgPaddingBytes,
|
||||
eContentSize: passportData.eContent?.length || 0,
|
||||
eContentHashFunction,
|
||||
eContentHashOffset,
|
||||
signedAttrSize: passportData.signedAttr?.length || 0,
|
||||
signedAttrHashFunction: brutForcedPublicKeyDetails.hashAlgorithm,
|
||||
signatureAlgorithm: brutForcedPublicKeyDetails.signatureAlgorithm,
|
||||
saltLength: brutForcedPublicKeyDetails.saltLength,
|
||||
curveOrExponent: parsedDsc ? getCurveOrExponent(parsedDsc) : 'unknown',
|
||||
signatureAlgorithmBits: dscSignatureAlgorithmBits,
|
||||
countryCode: passportData.mrz ? getCountryCodeFromMrz(passportData.mrz) : 'unknown',
|
||||
cscaFound: brutForcedPublicKeyDetailsDsc.cscaFound,
|
||||
cscaHashFunction: brutForcedPublicKeyDetailsDsc.cscaHashAlgorithm,
|
||||
cscaSignature: brutForcedPublicKeyDetailsDsc.cscaSignatureAlgorithm,
|
||||
cscaSaltLength: brutForcedPublicKeyDetailsDsc.cscaSaltLength,
|
||||
cscaCurveOrExponent: brutForcedPublicKeyDetailsDsc.cscaCurveOrExponent,
|
||||
cscaSignatureAlgorithmBits: brutForcedPublicKeyDetailsDsc.cscaSignatureAlgorithmBits,
|
||||
dsc: passportData.dsc,
|
||||
};
|
||||
}
|
||||
return {
|
||||
dataGroups:
|
||||
passportData.dgPresents
|
||||
?.toString()
|
||||
.split(',')
|
||||
.map((item) => item.replace('DG', ''))
|
||||
.join(',') || 'None',
|
||||
dg1HashFunction,
|
||||
dg1HashOffset,
|
||||
dgPaddingBytes,
|
||||
eContentSize: passportData.eContent?.length || 0,
|
||||
eContentHashFunction,
|
||||
eContentHashOffset,
|
||||
signedAttrSize: passportData.signedAttr?.length || 0,
|
||||
signedAttrHashFunction: brutForcedPublicKeyDetails.hashAlgorithm,
|
||||
signatureAlgorithm: brutForcedPublicKeyDetails.signatureAlgorithm,
|
||||
saltLength: brutForcedPublicKeyDetails.saltLength,
|
||||
curveOrExponent: parsedDsc ? getCurveOrExponent(parsedDsc) : 'unknown',
|
||||
signatureAlgorithmBits: dscSignatureAlgorithmBits,
|
||||
countryCode: passportData.mrz ? getCountryCodeFromMrz(passportData.mrz) : 'unknown',
|
||||
cscaFound: brutForcedPublicKeyDetailsDsc.cscaFound,
|
||||
cscaHashFunction: brutForcedPublicKeyDetailsDsc.cscaHashAlgorithm,
|
||||
cscaSignature: brutForcedPublicKeyDetailsDsc.cscaSignatureAlgorithm,
|
||||
cscaSaltLength: brutForcedPublicKeyDetailsDsc.cscaSaltLength,
|
||||
cscaCurveOrExponent: brutForcedPublicKeyDetailsDsc.cscaCurveOrExponent,
|
||||
cscaSignatureAlgorithmBits: brutForcedPublicKeyDetailsDsc.cscaSignatureAlgorithmBits,
|
||||
dsc: passportData.dsc,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ export type SignatureAlgorithm =
|
||||
| 'rsa_sha256_65537_3072'
|
||||
| 'rsa_sha256_65537_4096'
|
||||
| 'rsa_sha512_65537_4096'
|
||||
| 'rsa_sha224_65537_2048'
|
||||
| 'rsapss_sha256_65537_3072'
|
||||
| 'rsapss_sha256_65537_4096'
|
||||
| 'rsapss_sha256_3_2048'
|
||||
@@ -46,7 +47,7 @@ export type SignatureAlgorithm =
|
||||
| 'ecdsa_sha1_brainpoolP224r1_224'
|
||||
| 'ecdsa_sha224_brainpoolP224r1_224'
|
||||
| 'ecdsa_sha256_brainpoolP224r1_224'
|
||||
| 'ecdsa_sha512_brainpoolP512r1_512'
|
||||
| 'ecdsa_sha512_brainpoolP512r1_512';
|
||||
|
||||
export type Proof = {
|
||||
proof: {
|
||||
|
||||
Reference in New Issue
Block a user