Bringing openpassport/main (#19)

Co-authored-by: seshanthS <seshanth@protonmail.com>
Co-authored-by: turnoffthiscomputer <colin.remi07@gmail.com>
Co-authored-by: thomas-senechal <thomas.senechal@pm.me>
Co-authored-by: motemotech <i.am.nicoshark@gmail.com>
Co-authored-by: turnoffthiscomputer <98749896+remicolin@users.noreply.github.com>
Co-authored-by: ayman <aymanshaik1015@gmail.com>
This commit is contained in:
turboblitz
2025-02-05 02:13:52 -08:00
committed by GitHub
parent 3775790736
commit 629dfdad1a
484 changed files with 23040 additions and 18896 deletions

View File

@@ -62,7 +62,7 @@ jobs:
- name: Run Tests (Circuits)
working-directory: ./circuits
env:
FULL_TEST_SUITE: ${{ github.ref == 'refs/heads/main' || github.ref == 'refs/heads/openpassportv2' }}
FULL_TEST_SUITE: false
run: yarn test
- name: Run Tests (Common)

View File

@@ -1,8 +1,13 @@
import React, { useEffect } from 'react';
import 'react-native-get-random-values';
import * as amplitude from '@amplitude/analytics-react-native';
import { AMPLITUDE_KEY } from '@env';
import { SEGMENT_KEY } from '@env';
import {
EventPlugin,
PluginType,
SegmentEvent,
createClient,
} from '@segment/analytics-react-native';
import '@ethersproject/shims';
import { Buffer } from 'buffer';
import { YStack } from 'tamagui';
@@ -15,6 +20,57 @@ import { setupUniversalLinkListener } from './src/utils/qrCode';
global.Buffer = Buffer;
// Adjust the import path as needed
export class DisableTrackingPlugin extends EventPlugin {
type = PluginType.before;
execute(event: SegmentEvent): SegmentEvent {
// Ensure context exists
if (!event.context) {
event.context = {};
}
// Ensure device context exists
if (!event.context.device) {
event.context.device = {};
}
// Force tracking related fields to be disabled
event.context.device.adTrackingEnabled = false;
event.context.device.advertisingId = undefined;
event.context.device.trackingStatus = 'not-authorized';
event.context.device.id = undefined;
return event;
}
}
export const createSegmentClient = () => {
if (!SEGMENT_KEY) return null;
const client = createClient({
writeKey: SEGMENT_KEY,
trackAppLifecycleEvents: true,
debug: true,
collectDeviceId: false,
defaultSettings: {
integrations: {
'Segment.io': {
apiKey: SEGMENT_KEY,
},
},
},
});
client.add({ plugin: new DisableTrackingPlugin() });
return client;
};
// Export the client variable (will be initialized later)
export let segmentClient: ReturnType<typeof createClient> | null = null;
function App(): React.JSX.Element {
// const toast = useToastController();
// const setToast = useNavigationStore(state => state.setToast);
@@ -34,14 +90,13 @@ function App(): React.JSX.Element {
// }, [setSelectedTab]);
useEffect(() => {
if (AMPLITUDE_KEY) {
amplitude.init(AMPLITUDE_KEY);
}
const cleanup = setupUniversalLinkListener();
return cleanup;
}, []);
useEffect(() => {
const cleanup = setupUniversalLinkListener();
return cleanup;
// Initialize segment directly without any tracking checks
segmentClient = createSegmentClient();
}, []);
return (

View File

@@ -85,8 +85,8 @@ android {
applicationId "com.proofofpassportapp"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 16
versionName "1.5"
versionCode 19
versionName "1.8"
externalNativeBuild {
cmake {
cppFlags += "-fexceptions -frtti -std=c++11"

View File

@@ -362,11 +362,13 @@ class RNPassportReaderModule(private val reactContext: ReactApplicationContext)
}
}
eventMessageEmitter("Reading DG1.....")
val dg1In = service.getInputStream(PassportService.EF_DG1)
dg1File = DG1File(dg1In)
val dg2In = service.getInputStream(PassportService.EF_DG2)
dg2File = DG2File(dg2In)
dg1File = DG1File(dg1In)
// eventMessageEmitter("Reading DG2.....")
// val dg2In = service.getInputStream(PassportService.EF_DG2)
// dg2File = DG2File(dg2In)
eventMessageEmitter("Reading SOD.....")
val sodIn = service.getInputStream(PassportService.EF_SOD)
sodFile = SODFile(sodIn)
@@ -406,20 +408,20 @@ class RNPassportReaderModule(private val reactContext: ReactApplicationContext)
// sendDataToJS(PassportData(dg1File, dg2File, sodFile))
// Log.d(TAG, "============DATA SENT TO JS=============")
val allFaceImageInfo: MutableList<FaceImageInfo> = ArrayList()
dg2File.faceInfos.forEach {
allFaceImageInfo.addAll(it.faceImageInfos)
}
if (allFaceImageInfo.isNotEmpty()) {
val faceImageInfo = allFaceImageInfo.first()
val imageLength = faceImageInfo.imageLength
val dataInputStream = DataInputStream(faceImageInfo.imageInputStream)
val buffer = ByteArray(imageLength)
dataInputStream.readFully(buffer, 0, imageLength)
val inputStream: InputStream = ByteArrayInputStream(buffer, 0, imageLength)
bitmap = decodeImage(reactContext, faceImageInfo.mimeType, inputStream)
imageBase64 = Base64.encodeToString(buffer, Base64.DEFAULT)
}
// val allFaceImageInfo: MutableList<FaceImageInfo> = ArrayList()
// dg2File.faceInfos.forEach {
// allFaceImageInfo.addAll(it.faceImageInfos)
// }
// if (allFaceImageInfo.isNotEmpty()) {
// val faceImageInfo = allFaceImageInfo.first()
// val imageLength = faceImageInfo.imageLength
// val dataInputStream = DataInputStream(faceImageInfo.imageInputStream)
// val buffer = ByteArray(imageLength)
// dataInputStream.readFully(buffer, 0, imageLength)
// val inputStream: InputStream = ByteArrayInputStream(buffer, 0, imageLength)
// bitmap = decodeImage(reactContext, faceImageInfo.mimeType, inputStream)
// imageBase64 = Base64.encodeToString(buffer, Base64.DEFAULT)
// }
} catch (e: Exception) {
eventMessageEmitter(Messages.RESET)
return e
@@ -429,6 +431,7 @@ class RNPassportReaderModule(private val reactContext: ReactApplicationContext)
private fun doChipAuth(service: PassportService) {
try {
eventMessageEmitter("Reading DG14.....")
val dg14In = service.getInputStream(PassportService.EF_DG14)
dg14Encoded = IOUtils.toByteArray(dg14In)
val dg14InByte = ByteArrayInputStream(dg14Encoded)
@@ -459,11 +462,8 @@ class RNPassportReaderModule(private val reactContext: ReactApplicationContext)
val dataHashes = sodFile.dataGroupHashes
eventMessageEmitter("Reading DG14.....")
val dg14Hash = if (chipAuthSucceeded) digest.digest(dg14Encoded) else ByteArray(0)
eventMessageEmitter("Reading DG1.....")
val dg1Hash = digest.digest(dg1File.encoded)
eventMessageEmitter("Reading DG2.....")
val dg2Hash = digest.digest(dg2File.encoded)
// val gson = Gson()
@@ -486,7 +486,8 @@ class RNPassportReaderModule(private val reactContext: ReactApplicationContext)
Log.d(TAG, "Comparing data group hashes...")
eventMessageEmitter(Messages.COMPARING)
if (Arrays.equals(dg1Hash, dataHashes[1]) && Arrays.equals(dg2Hash, dataHashes[2])
// if (Arrays.equals(dg1Hash, dataHashes[1]) && Arrays.equals(dg2Hash, dataHashes[2])
if (Arrays.equals(dg1Hash, dataHashes[1])
&& (!chipAuthSucceeded || Arrays.equals(dg14Hash, dataHashes[14]))) {
Log.d(TAG, "Data group hashes match.")
@@ -660,13 +661,13 @@ class RNPassportReaderModule(private val reactContext: ReactApplicationContext)
// Log.d(TAG, "signedData.signerInfos: ${gson.toJson(signedData.signerInfos)}")
// Log.d(TAG, "signedData.certificates: ${gson.toJson(signedData.certificates)}")
var quality = 100
val base64 = bitmap?.let { toBase64(it, quality) }
val photo = Arguments.createMap()
photo.putString("base64", base64 ?: "")
photo.putInt("width", bitmap?.width ?: 0)
photo.putInt("height", bitmap?.height ?: 0)
passport.putMap("photo", photo)
// var quality = 100
// val base64 = bitmap?.let { toBase64(it, quality) }
// val photo = Arguments.createMap()
// photo.putString("base64", base64 ?: "")
// photo.putInt("width", bitmap?.width ?: 0)
// photo.putInt("height", bitmap?.height ?: 0)
// passport.putMap("photo", photo)
// passport.putString("dg2File", gson.toJson(dg2File))
eventMessageEmitter(Messages.COMPLETED)

View File

@@ -34,7 +34,6 @@
1686F0E02C500FBD00841CDE /* QRScannerBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 1686F0DF2C500FBD00841CDE /* QRScannerBridge.m */; };
16E6646E2B8D292500FDD6A0 /* QKMRZScannerViewRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 16E6646D2B8D292500FDD6A0 /* QKMRZScannerViewRepresentable.swift */; };
16E884A52C5BD764003B7125 /* passport.json in Resources */ = {isa = PBXBuildFile; fileRef = 16E884A42C5BD764003B7125 /* passport.json */; };
1B904271B8E1DB8434EF0613 /* Pods_OpenPassport.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3435ED6D988B5E2DE0DE8101 /* Pods_OpenPassport.framework */; };
4A1791A6108347E5A635DB1C /* Inter-ThinItalic.otf in Resources */ = {isa = PBXBuildFile; fileRef = EEA2A4D24A8F4B10A5ECA607 /* Inter-ThinItalic.otf */; };
5008C8140A304ED79DB817C5 /* slkscrb.ttf in Resources */ = {isa = PBXBuildFile; fileRef = B4E7218406B64A95BCE0DFE4 /* slkscrb.ttf */; };
5C66B65890614C638590DC8A /* Inter-ExtraBold.otf in Resources */ = {isa = PBXBuildFile; fileRef = 8CE3CC4CFFC24314A22F1391 /* Inter-ExtraBold.otf */; };
@@ -61,6 +60,7 @@
EBECCA4983EC6929A7722578 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = E56E082698598B41447667BB /* PrivacyInfo.xcprivacy */; };
EEF3331B93C24D6482021BA2 /* Inter-ExtraLightItalic.otf in Resources */ = {isa = PBXBuildFile; fileRef = 090474A3F07B4FFA82C9A751 /* Inter-ExtraLightItalic.otf */; };
F1961109CC004035884F79D9 /* Inter-ExtraBoldItalic.otf in Resources */ = {isa = PBXBuildFile; fileRef = 37A4DECF3A824D5BAA8863FD /* Inter-ExtraBoldItalic.otf */; };
F2B6CAF95CEE1D3F94541B0F /* Pods_OpenPassport.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF34842C3AD0E76B7A7D5C5E /* Pods_OpenPassport.framework */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
@@ -142,12 +142,12 @@
16E6646D2B8D292500FDD6A0 /* QKMRZScannerViewRepresentable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QKMRZScannerViewRepresentable.swift; sourceTree = "<group>"; };
16E884A42C5BD764003B7125 /* passport.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = passport.json; sourceTree = "<group>"; };
276287D0D37B4E8EBD7A1ABD /* Inter-SemiBold.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = undefined; includeInIndex = 0; lastKnownFileType = unknown; name = "Inter-SemiBold.otf"; path = "../node_modules/@tamagui/font-inter/otf/Inter-SemiBold.otf"; sourceTree = "<group>"; };
3435ED6D988B5E2DE0DE8101 /* Pods_OpenPassport.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_OpenPassport.framework; sourceTree = BUILT_PRODUCTS_DIR; };
37A4DECF3A824D5BAA8863FD /* Inter-ExtraBoldItalic.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = undefined; includeInIndex = 0; lastKnownFileType = unknown; name = "Inter-ExtraBoldItalic.otf"; path = "../node_modules/@tamagui/font-inter/otf/Inter-ExtraBoldItalic.otf"; sourceTree = "<group>"; };
3BF771EA645241D9A28A3AE9 /* Inter-Bold.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = undefined; includeInIndex = 0; lastKnownFileType = unknown; name = "Inter-Bold.otf"; path = "../node_modules/@tamagui/font-inter/otf/Inter-Bold.otf"; sourceTree = "<group>"; };
4B261C28061D47B7B3BB3E37 /* Inter-Black.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = undefined; includeInIndex = 0; lastKnownFileType = unknown; name = "Inter-Black.otf"; path = "../node_modules/@tamagui/font-inter/otf/Inter-Black.otf"; sourceTree = "<group>"; };
64F386964AAE4C1E8F2DF789 /* Inter-ExtraLight.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = undefined; includeInIndex = 0; lastKnownFileType = unknown; name = "Inter-ExtraLight.otf"; path = "../node_modules/@tamagui/font-inter/otf/Inter-ExtraLight.otf"; sourceTree = "<group>"; };
6CF8D6DF98634423ACD5D630 /* Inter-Regular.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = undefined; includeInIndex = 0; lastKnownFileType = unknown; name = "Inter-Regular.otf"; path = "../node_modules/@tamagui/font-inter/otf/Inter-Regular.otf"; sourceTree = "<group>"; };
73A8B182B1187BFE180CBB44 /* Pods-OpenPassport.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OpenPassport.release.xcconfig"; path = "Target Support Files/Pods-OpenPassport/Pods-OpenPassport.release.xcconfig"; sourceTree = "<group>"; };
7CCB5F83F57540E88D54B34A /* slkscr.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = undefined; includeInIndex = 0; lastKnownFileType = unknown; name = slkscr.ttf; path = "../node_modules/@tamagui/font-silkscreen/files/slkscr.ttf"; sourceTree = "<group>"; };
7E5C3CEF7EDA4871B3D0EBE1 /* Advercase-Regular.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = undefined; includeInIndex = 0; lastKnownFileType = unknown; name = "Advercase-Regular.otf"; path = "../src/assets/fonts/Advercase-Regular.otf"; sourceTree = "<group>"; };
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = OpenPassport/LaunchScreen.storyboard; sourceTree = "<group>"; };
@@ -158,10 +158,10 @@
905B70062A72774000AFA232 /* PassportReader.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PassportReader.m; sourceTree = "<group>"; };
905B70082A729CD400AFA232 /* OpenPassport.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = OpenPassport.entitlements; path = OpenPassport/OpenPassport.entitlements; sourceTree = "<group>"; };
9BF744D9A73A4BAC96EC569A /* DINOT-Medium.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = undefined; includeInIndex = 0; lastKnownFileType = unknown; name = "DINOT-Medium.otf"; path = "../src/assets/fonts/DINOT-Medium.otf"; sourceTree = "<group>"; };
A239B1BBD7EF208EB51EF7DE /* Pods-OpenPassport.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OpenPassport.release.xcconfig"; path = "Target Support Files/Pods-OpenPassport/Pods-OpenPassport.release.xcconfig"; sourceTree = "<group>"; };
AF34842C3AD0E76B7A7D5C5E /* Pods_OpenPassport.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_OpenPassport.framework; sourceTree = BUILT_PRODUCTS_DIR; };
B4E7218406B64A95BCE0DFE4 /* slkscrb.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = undefined; includeInIndex = 0; lastKnownFileType = unknown; name = slkscrb.ttf; path = "../node_modules/@tamagui/font-silkscreen/files/slkscrb.ttf"; sourceTree = "<group>"; };
BB9316819FB038104D42933E /* Pods-OpenPassport.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OpenPassport.debug.xcconfig"; path = "Target Support Files/Pods-OpenPassport/Pods-OpenPassport.debug.xcconfig"; sourceTree = "<group>"; };
C56F122245594D6DA9B7570A /* slkscr.woff */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = undefined; includeInIndex = 0; lastKnownFileType = unknown; name = slkscr.woff; path = "../node_modules/@tamagui/font-silkscreen/files/slkscr.woff"; sourceTree = "<group>"; };
CA67A75B161A05334E3E9402 /* Pods-OpenPassport.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OpenPassport.debug.xcconfig"; path = "Target Support Files/Pods-OpenPassport/Pods-OpenPassport.debug.xcconfig"; sourceTree = "<group>"; };
D58C0CED91AE4265A5A406A0 /* Inter-BlackItalic.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = undefined; includeInIndex = 0; lastKnownFileType = unknown; name = "Inter-BlackItalic.otf"; path = "../node_modules/@tamagui/font-inter/otf/Inter-BlackItalic.otf"; sourceTree = "<group>"; };
DD642F4F3A114B43A22296D7 /* Inter-Thin.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = undefined; includeInIndex = 0; lastKnownFileType = unknown; name = "Inter-Thin.otf"; path = "../node_modules/@tamagui/font-inter/otf/Inter-Thin.otf"; sourceTree = "<group>"; };
E56E082698598B41447667BB /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xml; name = PrivacyInfo.xcprivacy; path = OpenPassport/PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
@@ -186,7 +186,7 @@
05D985F62BB331AB00F58EEA /* libfr.a in Frameworks */,
167D934A2C91D2EA00530E6B /* libwitnesscalc_prove_rsa_65537_sha256.a in Frameworks */,
167D93462C91B1E100530E6B /* libwitnesscalc_register_rsa_65537_sha1.a in Frameworks */,
1B904271B8E1DB8434EF0613 /* Pods_OpenPassport.framework in Frameworks */,
F2B6CAF95CEE1D3F94541B0F /* Pods_OpenPassport.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -273,7 +273,7 @@
0569F35E2BBC98C9006670BD /* libfq.a */,
0569F35A2BBC900D006670BD /* librapidsnark.a */,
ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
3435ED6D988B5E2DE0DE8101 /* Pods_OpenPassport.framework */,
AF34842C3AD0E76B7A7D5C5E /* Pods_OpenPassport.framework */,
);
name = Frameworks;
sourceTree = "<group>";
@@ -343,8 +343,8 @@
BBD78D7AC51CEA395F1C20DB /* Pods */ = {
isa = PBXGroup;
children = (
BB9316819FB038104D42933E /* Pods-OpenPassport.debug.xcconfig */,
A239B1BBD7EF208EB51EF7DE /* Pods-OpenPassport.release.xcconfig */,
CA67A75B161A05334E3E9402 /* Pods-OpenPassport.debug.xcconfig */,
73A8B182B1187BFE180CBB44 /* Pods-OpenPassport.release.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
@@ -356,15 +356,15 @@
isa = PBXNativeTarget;
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "OpenPassport" */;
buildPhases = (
62212FE980074600640A3F2F /* [CP] Check Pods Manifest.lock */,
1C3B778186C749AEAB10A0B1 /* [CP] Check Pods Manifest.lock */,
FD10A7F022414F080027D42C /* Start Packager */,
13B07F871A680F5B00A75B9A /* Sources */,
13B07F8C1A680F5B00A75B9A /* Frameworks */,
13B07F8E1A680F5B00A75B9A /* Resources */,
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
054340D12C71B2980014B445 /* Embed App Clips */,
19B6B230BF58128B7603B834 /* [CP] Embed Pods Frameworks */,
CCF107224BF9CF7E8A4F57B7 /* [CP] Copy Pods Resources */,
0C6411FE06C61E7F495D9204 /* [CP] Embed Pods Frameworks */,
DAB2FFB78D69B620A585FF8C /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@@ -461,7 +461,7 @@
shellPath = /bin/sh;
shellScript = "set -e\n\nWITH_ENVIRONMENT=\"../node_modules/react-native/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"../node_modules/react-native/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n";
};
19B6B230BF58128B7603B834 /* [CP] Embed Pods Frameworks */ = {
0C6411FE06C61E7F495D9204 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@@ -478,7 +478,7 @@
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-OpenPassport/Pods-OpenPassport-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
62212FE980074600640A3F2F /* [CP] Check Pods Manifest.lock */ = {
1C3B778186C749AEAB10A0B1 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@@ -500,7 +500,7 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
CCF107224BF9CF7E8A4F57B7 /* [CP] Copy Pods Resources */ = {
DAB2FFB78D69B620A585FF8C /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@@ -584,7 +584,7 @@
/* Begin XCBuildConfiguration section */
13B07F941A680F5B00A75B9A /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = BB9316819FB038104D42933E /* Pods-OpenPassport.debug.xcconfig */;
baseConfigurationReference = CA67A75B161A05334E3E9402 /* Pods-OpenPassport.debug.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
@@ -592,7 +592,7 @@
CODE_SIGN_ENTITLEMENTS = OpenPassport/OpenPassportDebug.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 67;
CURRENT_PROJECT_VERSION = 72;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = 5B29R5LYHQ;
ENABLE_BITCODE = NO;
@@ -707,7 +707,7 @@
"$(PROJECT_DIR)",
"$(PROJECT_DIR)/MoproKit/Libs",
);
MARKETING_VERSION = 2.0.0;
MARKETING_VERSION = 2.0.6;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
@@ -725,13 +725,13 @@
};
13B07F951A680F5B00A75B9A /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = A239B1BBD7EF208EB51EF7DE /* Pods-OpenPassport.release.xcconfig */;
baseConfigurationReference = 73A8B182B1187BFE180CBB44 /* Pods-OpenPassport.release.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = OpenPassport/OpenPassport.entitlements;
CURRENT_PROJECT_VERSION = 67;
CURRENT_PROJECT_VERSION = 72;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = 5B29R5LYHQ;
FRAMEWORK_SEARCH_PATHS = (
@@ -845,7 +845,7 @@
"$(PROJECT_DIR)",
"$(PROJECT_DIR)/MoproKit/Libs",
);
MARKETING_VERSION = 2.0.0;
MARKETING_VERSION = 2.0.6;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",

View File

@@ -97,7 +97,7 @@ class PassportReader: NSObject{
// let masterListURL = Bundle.main.url(forResource: "masterList", withExtension: ".pem")
// passportReader.setMasterListURL( masterListURL! )
let passport = try await passportReader.readPassport( mrzKey: mrzKey, customDisplayMessage: customMessageHandler)
let passport = try await passportReader.readPassport( mrzKey: mrzKey, tags: [.COM, .DG1, .SOD], customDisplayMessage: customMessageHandler)
var ret = [String:String]()
print("documentType", passport.documentType)
@@ -118,13 +118,13 @@ class PassportReader: NSObject{
ret["phoneNumber"] = passport.phoneNumber
ret["personalNumber"] = passport.personalNumber
let passportPhotoData = passport.passportPhoto // [UInt8]
if let passportPhotoData = passport.passportPhoto {
let data = Data(passportPhotoData)
let base64String = data.base64EncodedString()
// let passportPhotoData = passport.passportPhoto // [UInt8]
// if let passportPhotoData = passport.passportPhoto {
// let data = Data(passportPhotoData)
// let base64String = data.base64EncodedString()
ret["passportPhoto"] = base64String
}
// ret["passportPhoto"] = base64String
// }
// documentSigningCertificate
// countrySigningCertificate

View File

@@ -24,7 +24,7 @@ target 'OpenPassport' do
config = use_native_modules!
use_frameworks!
pod 'NFCPassportReader', git: 'https://github.com/0xturboblitz/NFCPassportReader.git', commit: '0a8e26d56f5f85f698b67c5df5ea9ecbb53cbc45'
pod 'NFCPassportReader', git: 'https://github.com/zk-passport/NFCPassportReader', commit: '8e72f0a2d3ca3bede00304bd22ed10829535dd53'
pod 'QKMRZScanner'
pod 'RNFS', :path => '../node_modules/react-native-fs'
pod 'lottie-ios'

View File

@@ -7,9 +7,9 @@ PODS:
- fmt (9.1.0)
- glog (0.3.5)
- lottie-ios (4.5.0)
- NFCPassportReader (2.0.3):
- OpenSSL-Universal (= 1.1.1100)
- OpenSSL-Universal (1.1.1100)
- NFCPassportReader (2.1.1):
- OpenSSL-Universal (= 1.1.1900)
- OpenSSL-Universal (1.1.1900)
- QKMRZParser (2.0.0)
- QKMRZScanner (3.0.0):
- QKMRZParser (~> 2.0.0)
@@ -1227,6 +1227,10 @@ PODS:
- React-Core
- react-native-nfc-manager (3.16.1):
- React-Core
- react-native-safe-area-context (5.1.0):
- React-Core
- react-native-tracking-transparency (0.1.2):
- React
- React-nativeconfig (0.75.4)
- React-NativeModulesApple (0.75.4):
- glog
@@ -1474,6 +1478,27 @@ PODS:
- React-Core
- RNFS (2.20.0):
- React-Core
- RNGestureHandler (2.22.1):
- DoubleConversion
- glog
- RCT-Folly (= 2024.01.01.00)
- RCTRequired
- RCTTypeSafety
- React-Core
- React-debug
- React-Fabric
- React-featureflags
- React-graphics
- React-ImageManager
- React-jsi
- React-NativeModulesApple
- React-RCTFabric
- React-rendererdebug
- React-utils
- ReactCodegen
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- Yoga
- RNKeychain (8.2.0):
- React-Core
- RNSVG (13.4.0):
@@ -1485,7 +1510,12 @@ PODS:
- RNZipArchive/Core (6.1.2):
- React-Core
- SSZipArchive (~> 2.2)
- segment-analytics-react-native (2.20.3):
- React-Core
- sovran-react-native
- SocketRocket (0.7.0)
- sovran-react-native (1.1.3):
- React-Core
- SSZipArchive (2.4.3)
- SwiftQRScanner (1.1.6)
- SwiftyTesseract (3.1.3)
@@ -1499,7 +1529,7 @@ DEPENDENCIES:
- fmt (from `../node_modules/react-native/third-party-podspecs/fmt.podspec`)
- glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
- lottie-ios
- NFCPassportReader (from `https://github.com/0xturboblitz/NFCPassportReader.git`, commit `0a8e26d56f5f85f698b67c5df5ea9ecbb53cbc45`)
- NFCPassportReader (from `https://github.com/zk-passport/NFCPassportReader`, commit `8e72f0a2d3ca3bede00304bd22ed10829535dd53`)
- QKMRZScanner
- RCT-Folly (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`)
- RCT-Folly/Fabric (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`)
@@ -1536,6 +1566,8 @@ DEPENDENCIES:
- react-native-get-random-values (from `../node_modules/react-native-get-random-values`)
- "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)"
- react-native-nfc-manager (from `../node_modules/react-native-nfc-manager`)
- react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
- react-native-tracking-transparency (from `../node_modules/react-native-tracking-transparency`)
- React-nativeconfig (from `../node_modules/react-native/ReactCommon`)
- React-NativeModulesApple (from `../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios`)
- React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`)
@@ -1563,9 +1595,12 @@ DEPENDENCIES:
- ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`)
- "RNCAsyncStorage (from `../node_modules/@react-native-async-storage/async-storage`)"
- RNFS (from `../node_modules/react-native-fs`)
- RNGestureHandler (from `../node_modules/react-native-gesture-handler`)
- RNKeychain (from `../node_modules/react-native-keychain`)
- RNSVG (from `../node_modules/react-native-svg`)
- RNZipArchive (from `../node_modules/react-native-zip-archive`)
- "segment-analytics-react-native (from `../node_modules/@segment/analytics-react-native`)"
- "sovran-react-native (from `../node_modules/@segment/sovran-react-native`)"
- SwiftQRScanner (from `https://github.com/vinodiOS/SwiftQRScanner`)
- Yoga (from `../node_modules/react-native/ReactCommon/yoga`)
@@ -1593,8 +1628,8 @@ EXTERNAL SOURCES:
glog:
:podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec"
NFCPassportReader:
:commit: 0a8e26d56f5f85f698b67c5df5ea9ecbb53cbc45
:git: https://github.com/0xturboblitz/NFCPassportReader.git
:commit: 8e72f0a2d3ca3bede00304bd22ed10829535dd53
:git: https://github.com/zk-passport/NFCPassportReader
RCT-Folly:
:podspec: "../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec"
RCTDeprecation:
@@ -1661,6 +1696,10 @@ EXTERNAL SOURCES:
:path: "../node_modules/@react-native-community/netinfo"
react-native-nfc-manager:
:path: "../node_modules/react-native-nfc-manager"
react-native-safe-area-context:
:path: "../node_modules/react-native-safe-area-context"
react-native-tracking-transparency:
:path: "../node_modules/react-native-tracking-transparency"
React-nativeconfig:
:path: "../node_modules/react-native/ReactCommon"
React-NativeModulesApple:
@@ -1715,12 +1754,18 @@ EXTERNAL SOURCES:
:path: "../node_modules/@react-native-async-storage/async-storage"
RNFS:
:path: "../node_modules/react-native-fs"
RNGestureHandler:
:path: "../node_modules/react-native-gesture-handler"
RNKeychain:
:path: "../node_modules/react-native-keychain"
RNSVG:
:path: "../node_modules/react-native-svg"
RNZipArchive:
:path: "../node_modules/react-native-zip-archive"
segment-analytics-react-native:
:path: "../node_modules/@segment/analytics-react-native"
sovran-react-native:
:path: "../node_modules/@segment/sovran-react-native"
SwiftQRScanner:
:git: https://github.com/vinodiOS/SwiftQRScanner
Yoga:
@@ -1728,8 +1773,8 @@ EXTERNAL SOURCES:
CHECKOUT OPTIONS:
NFCPassportReader:
:commit: 0a8e26d56f5f85f698b67c5df5ea9ecbb53cbc45
:git: https://github.com/0xturboblitz/NFCPassportReader.git
:commit: 8e72f0a2d3ca3bede00304bd22ed10829535dd53
:git: https://github.com/zk-passport/NFCPassportReader
SwiftQRScanner:
:commit: fddcabcb431cd6110cea0394660082661dbafa7e
:git: https://github.com/vinodiOS/SwiftQRScanner
@@ -1742,8 +1787,8 @@ SPEC CHECKSUMS:
fmt: 4c2741a687cc09f0634a2e2c72a838b99f1ff120
glog: 69ef571f3de08433d766d614c73a9838a06bf7eb
lottie-ios: a881093fab623c467d3bce374367755c272bdd59
NFCPassportReader: a160b80e3df3b5325c13902f90405f5eef7520b3
OpenSSL-Universal: ebc357f1e6bc71fa463ccb2fe676756aff50e88c
NFCPassportReader: e931c61c189e08a4b4afa0ed4014af19eab2f129
OpenSSL-Universal: 84efb8a29841f2764ac5403e0c4119a28b713346
QKMRZParser: 6b419b6f07d6bff6b50429b97de10846dc902c29
QKMRZScanner: cf2348fd6ce441e758328da4adf231ef2b51d769
RCT-Folly: 4464f4d875961fce86008d45f4ecf6cef6de0740
@@ -1779,6 +1824,8 @@ SPEC CHECKSUMS:
react-native-get-random-values: 21325b2244dfa6b58878f51f9aa42821e7ba3d06
react-native-netinfo: f0a9899081c185db1de5bb2fdc1c88c202a059ac
react-native-nfc-manager: 5213321cf6c18d879c8092c0bf56806b771ec5ac
react-native-safe-area-context: 04803a01f39f31cc6605a5531280b477b48f8a88
react-native-tracking-transparency: 25ff1ff866e338c137c818bdec20526bb05ffcc1
React-nativeconfig: 31072ab0146e643594f6959c7f970a04b6c9ddd0
React-NativeModulesApple: 5df767d9a2197ac25f4d8dd2d4ae1af3624022e2
React-perflogger: 59e1a3182dca2cee7b9f1f7aab204018d46d1914
@@ -1806,15 +1853,18 @@ SPEC CHECKSUMS:
ReactCommon: 03d2d48fcd1329fe3bc4e428a78a0181b68068c2
RNCAsyncStorage: ec53e44dc3e75b44aa2a9f37618a49c3bc080a7a
RNFS: 4ac0f0ea233904cb798630b3c077808c06931688
RNGestureHandler: e705387b01bba53f4643bdff381ee08c7b9679a1
RNKeychain: bfe3d12bf4620fe488771c414530bf16e88f3678
RNSVG: 07dbd870b0dcdecc99b3a202fa37c8ca163caec2
RNZipArchive: 6d736ee4e286dbbd9d81206b7a4da355596ca04a
segment-analytics-react-native: d57ed4971cbb995706babf29215ebdbf242ecdab
SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d
sovran-react-native: eec37f82e4429f0e3661f46aaf4fcd85d1b54f60
SSZipArchive: fe6a26b2a54d5a0890f2567b5cc6de5caa600aef
SwiftQRScanner: e85a25f9b843e9231dab89a96e441472fe54a724
SwiftyTesseract: 1f3d96668ae92dc2208d9842c8a59bea9fad2cbb
Yoga: b05994d1933f507b0a28ceaa4fdb968dc18da178
PODFILE CHECKSUM: cc6778e0dcd4c510b705f4dc458411547dc1d00c
PODFILE CHECKSUM: a5761927116120f511f409f5249a18a8c626545c
COCOAPODS: 1.16.2
COCOAPODS: 1.15.2

View File

@@ -27,6 +27,8 @@
"@react-navigation/elements": "^2.2.5",
"@react-navigation/native": "^7.0.14",
"@react-navigation/stack": "^7.1.1",
"@segment/analytics-react-native": "^2.20.3",
"@segment/sovran-react-native": "^1.1.3",
"@tamagui/colors": "1.110.0",
"@tamagui/config": "1.110.0",
"@tamagui/core": "1.110.0",
@@ -57,6 +59,7 @@
"react-native-passport-reader": "^1.0.3",
"react-native-safe-area-context": "^5.1.0",
"react-native-svg": "13.4.0",
"react-native-tracking-transparency": "^0.1.2",
"react-native-zip-archive": "^6.1.0",
"socket.io-client": "^4.7.5",
"tamagui": "1.110.0",

View File

@@ -19,7 +19,8 @@ import {
} from 'tamagui';
import { countryCodes } from '../../../common/src/constants/constants';
import { genMockPassportData } from '../../../common/src/utils/genMockPassportData';
import { genMockPassportData } from '../../../common/src/utils/passports/genMockPassportData';
import { parsePassportData } from '../../../common/src/utils/passports/passport_parsing/parsePassportData';
import CustomButton from '../components/CustomButton';
import useUserStore from '../stores/userStore';
import {
@@ -77,20 +78,38 @@ const MockDataScreen: React.FC<MockDataScreenProps> = ({}) => {
.toUpperCase();
await new Promise(resolve =>
setTimeout(() => {
let mockPassportData;
const hashAlgo = selectedAlgorithm === 'rsa sha1' ? 'sha1' : 'sha256';
const mockPassportData = genMockPassportData(
hashAlgo,
hashAlgo,
signatureAlgorithmToStrictSignatureAlgorithm[
selectedAlgorithm as keyof typeof signatureAlgorithmToStrictSignatureAlgorithm
],
selectedCountry as keyof typeof countryCodes,
castDate(-age),
castDate(expiryYears),
randomPassportNumber,
...(isInOfacList ? ['HENAO MONTOYA', 'ARCANGEL DE JESUS'] : []),
);
if (isInOfacList) {
mockPassportData = genMockPassportData(
hashAlgo,
hashAlgo,
signatureAlgorithmToStrictSignatureAlgorithm[
selectedAlgorithm as keyof typeof signatureAlgorithmToStrictSignatureAlgorithm
],
selectedCountry as keyof typeof countryCodes,
castDate(-age),
castDate(expiryYears),
randomPassportNumber,
'HENAO MONTOYA', // this name is the OFAC list
'ARCANGEL DE JESUS',
);
} else {
mockPassportData = genMockPassportData(
hashAlgo,
hashAlgo,
signatureAlgorithmToStrictSignatureAlgorithm[
selectedAlgorithm as keyof typeof signatureAlgorithmToStrictSignatureAlgorithm
],
selectedCountry as keyof typeof countryCodes,
castDate(-age),
castDate(expiryYears),
randomPassportNumber,
);
}
useUserStore.getState().registerPassportData(mockPassportData);
const parsedPassportData = parsePassportData(mockPassportData);
useUserStore.getState().setPassportMetadata(parsedPassportData);
useUserStore.getState().setRegistered(true);
resolve(null);
}, 0),

View File

@@ -50,9 +50,10 @@ const NextScreen: React.FC = () => {
h={height > 750 ? 190 : 130}
borderRadius={height > 750 ? '$7' : '$6'}
source={{
uri: passportData.mockUser
? USER_PROFILE
: passportData.photoBase64 ?? USER_PROFILE,
uri:
passportData?.mockUser || !!!passportData?.photoBase64
? USER_PROFILE
: passportData?.photoBase64 ?? USER_PROFILE,
}}
/>
)}
@@ -78,16 +79,34 @@ const NextScreen: React.FC = () => {
const key_ = key;
const indexes =
attributeToPosition[key_ as keyof typeof attributeToPosition];
if (!passportData?.mrz || !indexes) {
return null;
}
const keyFormatted = key_
.replace(/_/g, ' ')
.split(' ')
.map((word: string) => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ');
if (
indexes[0] >= passportData.mrz.length ||
indexes[1] >= passportData.mrz.length
) {
console.warn(
`Invalid indexes for key ${key_}: [${indexes[0]}, ${indexes[1]}]`,
);
return null;
}
const mrzAttribute = passportData.mrz.slice(
indexes[0],
indexes[1] + 1,
);
const mrzAttributeFormatted = formatAttribute(key_, mrzAttribute);
const mrzAttributeFormatted = formatAttribute(
key_,
mrzAttribute ?? '',
);
return (
<Fieldset horizontal key={key} gap="$3" alignItems="center">

View File

@@ -1,12 +1,10 @@
import React, { useEffect, useState } from 'react';
import { useNavigation } from '@react-navigation/native';
import io, { Socket } from 'socket.io-client';
import { Text, YStack } from 'tamagui';
import {
DEVELOPMENT_MODE,
max_cert_bytes,
MAX_CERT_BYTES,
} from '../../../../common/src/constants/constants';
import {
ArgumentsProveOffChain,
@@ -16,13 +14,15 @@ import {
getCircuitNameOld,
parseCertificateSimple,
} from '../../../../common/src/utils/certificate_parsing/parseCertificateSimple';
// import {
// getCSCAFromSKI,
// sendCSCARequest,
// } from '../../../../common/src/utils/csca';
import {
generateCircuitInputsDSC,
getCSCAFromSKI,
sendCSCARequest,
} from '../../../../common/src/utils/csca';
} from '../../../../common/src/utils/circuits/generateInputs';
import { buildAttestation } from '../../../../common/src/utils/openPassportAttestation';
import { parsePassportData } from '../../../../common/src/utils/parsePassportData';
import { parsePassportData } from '../../../../common/src/utils/passports/passport_parsing/parsePassportData';
import Disclosures from '../../components/Disclosures';
import { PrimaryButton } from '../../components/buttons/PrimaryButton';
import { BodyText } from '../../components/typography/BodyText';
@@ -31,7 +31,7 @@ import { ExpandableBottomLayout } from '../../layouts/ExpandableBottomLayout';
import useNavigationStore from '../../stores/navigationStore';
import useUserStore from '../../stores/userStore';
import { black, slate300, white } from '../../utils/colors';
import { generateCircuitInputsInApp } from '../../utils/generateInputsInApp';
// import { generateCircuitInputsInApp } from '../../utils/generateInputsInApp';
import { generateProof } from '../../utils/prover';
import { CircuitName } from '../../utils/zkeyDownload';
@@ -188,34 +188,34 @@ const ProveScreen: React.FC = () => {
switch (selectedApp.mode) {
case 'prove_onchain':
case 'register':
const cscaInputs = generateCircuitInputsDSC(
dscSecret as string,
passportData.dsc,
max_cert_bytes,
selectedApp.devMode,
);
[dscProof, proof] = await Promise.all([
sendCSCARequest(cscaInputs),
generateProof(circuitName, inputs),
]);
const cscaPem = getCSCAFromSKI(
authorityKeyIdentifier,
DEVELOPMENT_MODE,
);
const { signatureAlgorithm: signatureAlgorithmDsc } =
parseCertificateSimple(cscaPem);
attestation = buildAttestation({
mode: selectedApp.mode,
proof: proof.proof,
publicSignals: proof.publicSignals,
signatureAlgorithm: signatureAlgorithm,
hashFunction: parsedPassportData.signedAttrHashFunction,
userIdType: selectedApp.userIdType,
dscProof: (dscProof as any).proof,
dscPublicSignals: (dscProof as any).pub_signals,
signatureAlgorithmDsc: signatureAlgorithmDsc,
hashFunctionDsc: parsedPassportData.signedAttrHashFunction,
});
// const cscaInputs = generateCircuitInputsDSC(
// dscSecret as string,
// passportData.dsc,
// MAX_CERT_BYTES,
// selectedApp.devMode,
// );
// [dscProof, proof] = await Promise.all([
// sendCSCARequest(cscaInputs),
// generateProof(circuitName, inputs),
// ]);
// const cscaPem = getCSCAFromSKI(
// authorityKeyIdentifier,
// DEVELOPMENT_MODE,
// );
// const { signatureAlgorithm: signatureAlgorithmDsc } =
// parseCertificateSimple(cscaPem);
// attestation = buildAttestation({
// mode: selectedApp.mode,
// proof: proof.proof,
// publicSignals: proof.publicSignals,
// signatureAlgorithm: signatureAlgorithm,
// hashFunction: parsedPassportData.signedAttrHashFunction,
// userIdType: selectedApp.userIdType,
// dscProof: (dscProof as any).proof,
// dscPublicSignals: (dscProof as any).pub_signals,
// signatureAlgorithmDsc: signatureAlgorithmDsc,
// hashFunctionDsc: parsedPassportData.signedAttrHashFunction,
// });
break;
default:
proof = await generateProof(circuitName, inputs);

View File

@@ -2,7 +2,7 @@ import React from 'react';
import { ScrollView, Separator, Text, XStack, YStack } from 'tamagui';
import { parsePassportData } from '../../../common/src/utils/parsePassportData';
import { parsePassportData } from '../../../common/src/utils/passports/passport_parsing/parsePassportData';
import useUserStore from '../stores/userStore';
import { separatorColor, textBlack } from '../utils/colors';
@@ -126,7 +126,7 @@ const UserInfo: React.FC = () => {
<InfoRow
label="CSCA Signature Algorithm"
value={passportMetaData?.cscaSignature || 'None'}
value={passportMetaData?.cscaSignatureAlgorithm || 'None'}
/>
<Separator borderColor={separatorColor} />

View File

@@ -2,6 +2,7 @@ import { useToastController } from '@tamagui/toast';
import { create } from 'zustand';
import { OpenPassportApp } from '../../../common/src/utils/appType';
import { segmentClient } from '../../App';
import {
IsZkeyDownloading,
ShowWarningModalProps,
@@ -24,10 +25,12 @@ interface NavigationState {
setNfcSheetIsOpen: (isOpen: boolean) => void;
zkeyDownloadedPercentage: number;
setZkeyDownloadedPercentage: (percentage: number) => void;
trackEvent: (eventName: string, properties?: Record<string, any>) => void;
trackNavigation: (tab: string) => void;
}
const useNavigationStore = create<NavigationState>((set, get) => ({
zkeyDownloadedPercentage: 100,
zkeyDownloadedPercentage: 0,
setZkeyDownloadedPercentage: (percentage: number) =>
set({ zkeyDownloadedPercentage: percentage }),
isZkeyDownloading: {
@@ -54,7 +57,10 @@ const useNavigationStore = create<NavigationState>((set, get) => ({
setToast: toast => set({ toast }),
setSelectedApp: app => set({ selectedApp: app }),
setSelectedTab: tab => set({ selectedTab: tab }),
setSelectedTab: (tab: string) => {
const { trackNavigation } = get();
trackNavigation(tab);
},
update: patch => {
set({
@@ -64,6 +70,21 @@ const useNavigationStore = create<NavigationState>((set, get) => ({
},
nfcSheetIsOpen: false,
setNfcSheetIsOpen: isOpen => set({ nfcSheetIsOpen: isOpen }),
trackEvent: (eventName: string, properties?: Record<string, any>) => {
if (segmentClient) {
segmentClient.track(eventName, properties);
}
},
trackNavigation: (tab: string) => {
if (segmentClient) {
segmentClient.track('Navigation Change', {
tab,
});
}
set({ selectedTab: tab });
},
}));
export default useNavigationStore;

View File

@@ -3,12 +3,15 @@ import { resetGenericPassword } from 'react-native-keychain';
import { DEFAULT_DOB, DEFAULT_DOE, DEFAULT_PNUMBER } from '@env';
import { create } from 'zustand';
import { generateDscSecret } from '../../../common/src/utils/csca';
// import { generateDscSecret } from '../../../common/src/utils/csca';
import { PassportMetadata } from '../../../common/src/utils/passports/passport_parsing/parsePassportData';
import { PassportData, Proof } from '../../../common/src/utils/types';
import {
loadPassportData,
loadPassportMetadata,
loadSecretOrCreateIt,
storePassportData,
storePassportMetadata,
} from '../utils/keychain';
interface UserState {
@@ -18,6 +21,7 @@ interface UserState {
countryCode: string;
registered: boolean;
passportData: PassportData | null;
passportMetadata: PassportMetadata | null;
secret: string;
cscaProof: Proof | null;
localProof: Proof | null;
@@ -35,6 +39,8 @@ interface UserState {
setUserLoaded: (userLoaded: boolean) => void;
proofVerificationResult: string;
setProofVerificationResult: (proofVerificationResult: string) => void;
setPassportMetadata: (metadata: PassportMetadata) => void;
clearPassportMetadataFromStorage: () => void;
}
const useUserStore = create<UserState>((set, get) => ({
@@ -46,6 +52,7 @@ const useUserStore = create<UserState>((set, get) => ({
dscSecret: null,
registered: false,
passportData: null,
passportMetadata: null,
secret: '',
cscaProof: null,
localProof: null,
@@ -58,6 +65,10 @@ const useUserStore = create<UserState>((set, get) => ({
setUserLoaded: (userLoaded: boolean) => {
set({ userLoaded });
},
setPassportMetadata: async (metadata: PassportMetadata) => {
await storePassportMetadata(metadata);
set({ passportMetadata: metadata });
},
proofVerificationResult: 'null',
setProofVerificationResult: (proofVerificationResult: string) => {
set({ proofVerificationResult });
@@ -68,16 +79,18 @@ const useUserStore = create<UserState>((set, get) => ({
// - If the commitment is present in the tree, proceed to main screen
// - If the commitment is not present in the tree, proceed to main screen AND try registering it in the background
initUserStore: async () => {
// download zkeys if they are not already downloaded
const secret = await loadSecretOrCreateIt();
set({ secret });
const dscSecret = await generateDscSecret();
set({ dscSecret });
// const dscSecret = await generateDscSecret();
// set({ dscSecret });
const passportDataString = await loadPassportData();
if (!passportDataString) {
console.log('No passport data found, starting onboarding flow');
const passportMetadataString = await loadPassportMetadata();
if (!passportDataString || !passportMetadataString) {
console.log(
'No passport data or metadata found, starting onboarding flow',
);
set({
userLoaded: true,
});
@@ -87,6 +100,9 @@ const useUserStore = create<UserState>((set, get) => ({
// const isAlreadyRegistered = await isCommitmentRegistered(secret, JSON.parse(passportData));
const isAlreadyRegistered = true;
const passportData: PassportData = JSON.parse(passportDataString);
const passportMetadata: PassportMetadata = JSON.parse(
passportMetadataString,
);
if (!isAlreadyRegistered) {
console.log(
@@ -94,9 +110,9 @@ const useUserStore = create<UserState>((set, get) => ({
);
set({
passportData: passportData,
passportMetadata: passportMetadata,
userLoaded: true,
});
return;
}
@@ -105,9 +121,10 @@ const useUserStore = create<UserState>((set, get) => ({
);
set({
passportData: passportData,
passportMetadata: passportMetadata,
registered: true,
userLoaded: true,
});
set({ userLoaded: true });
},
// When reading passport for the first time:
@@ -152,6 +169,11 @@ const useUserStore = create<UserState>((set, get) => ({
dateOfBirth: '',
dateOfExpiry: '',
}),
clearPassportMetadataFromStorage: async () => {
await resetGenericPassword({ service: 'passportMetadata' });
set({ passportMetadata: null });
},
}));
export default useUserStore;

View File

@@ -15,11 +15,11 @@ import {
OpenPassportApp,
} from '../../../common/src/utils/appType';
import {
generateCircuitInputsDisclose,
generateCircuitInputsProve,
} from '../../../common/src/utils/generateInputs';
import { fetchTreeFromUrl } from '../../../common/src/utils/pubkeyTree';
import { revealBitmapFromAttributes } from '../../../common/src/utils/revealBitmap';
generateCircuitInputsRegister,
generateCircuitInputsVCandDisclose,
} from '../../../common/src/utils/circuits/generateInputs';
import { fetchTreeFromUrl } from '../../../common/src/utils/trees';
import { revealBitmapFromAttributes } from '../../../common/src/utils/circuits/formatOutputs';
import { PassportData } from '../../../common/src/utils/types';
import useUserStore from '../stores/userStore';
@@ -99,7 +99,7 @@ export const generateCircuitInputsInApp = async (
disclosureOptionsDisclose.excludedCountries.value.map(country =>
getCountryCode(country),
);
return generateCircuitInputsDisclose(
return generateCircuitInputsVCandDisclose(
secret,
PASSPORT_ATTESTATION_ID,
passportData,

View File

@@ -2,6 +2,7 @@ import * as Keychain from 'react-native-keychain';
import { ethers } from 'ethers';
import { PassportMetadata } from '../../../common/src/utils/passports/passport_parsing/parsePassportData';
import { PassportData } from '../../../common/src/utils/types';
export async function loadSecretOrCreateIt() {
@@ -36,3 +37,18 @@ export async function storePassportData(passportData: PassportData) {
{ service: 'passportData' },
);
}
export async function loadPassportMetadata() {
const metadataCreds = await Keychain.getGenericPassword({
service: 'passportMetadata',
});
return metadataCreds === false ? false : metadataCreds.password;
}
export async function storePassportMetadata(metadata: PassportMetadata) {
await Keychain.setGenericPassword(
'passportMetadata',
JSON.stringify(metadata),
{ service: 'passportMetadata' },
);
}

View File

@@ -2,10 +2,9 @@ import { NativeModules, Platform } from 'react-native';
import PassportReader from 'react-native-passport-reader';
// @ts-ignore
import * as amplitude from '@amplitude/analytics-react-native';
import { Buffer } from 'buffer';
import { parsePassportData } from '../../../common/src/utils/parsePassportData';
import { parsePassportData } from '../../../common/src/utils/passports/passport_parsing/parsePassportData';
import { PassportData } from '../../../common/src/utils/types';
import useNavigationStore from '../stores/navigationStore';
import useUserStore from '../stores/userStore';
@@ -16,12 +15,15 @@ export const scan = async (
) => {
const { passportNumber, dateOfBirth, dateOfExpiry } = useUserStore.getState();
const { toast } = useNavigationStore.getState();
const { toast, trackEvent } = useNavigationStore.getState();
const check = checkInputs(passportNumber, dateOfBirth, dateOfExpiry);
if (!check.success) {
amplitude.track('inputs_invalid', { error: check.message });
trackEvent('Inputs Failed', {
success: false,
error: check.message,
});
toast.show('Unvailable', {
message: check.message,
customData: {
@@ -31,20 +33,26 @@ export const scan = async (
return;
}
trackEvent('NFC Started', {
success: true,
});
console.log('scanning...');
if (Platform.OS === 'android') {
scanAndroid(setModalProofStep);
scanAndroid(setModalProofStep, Date.now());
} else {
scanIOS(setModalProofStep);
scanIOS(setModalProofStep, Date.now());
}
};
const scanAndroid = async (
setModalProofStep: (modalProofStep: number) => void,
startTime: number,
) => {
const { passportNumber, dateOfBirth, dateOfExpiry } = useUserStore.getState();
const { toast, setNfcSheetIsOpen } = useNavigationStore.getState();
const { toast, setNfcSheetIsOpen, trackEvent } =
useNavigationStore.getState();
setNfcSheetIsOpen(true);
try {
@@ -55,12 +63,15 @@ const scanAndroid = async (
});
console.log('scanned');
setNfcSheetIsOpen(false);
amplitude.track('nfc_scan_successful');
handleResponseAndroid(response, setModalProofStep);
trackEvent('NFC Success', {
success: true,
duration_ms: Date.now() - startTime,
});
} catch (e: any) {
console.log('error during scan:', e);
setNfcSheetIsOpen(false);
amplitude.track('nfc_scan_unsuccessful', { error: e.message });
if (e.message.includes('InvalidMRZKey')) {
toast.show('Error', {
message:
@@ -70,6 +81,10 @@ const scanAndroid = async (
},
timeout: 5000,
});
trackEvent('Invalid Key', {
success: false,
error: e.message,
});
useNavigationStore.getState().setSelectedTab('scan');
} else {
toast.show('Error', {
@@ -78,13 +93,21 @@ const scanAndroid = async (
type: 'error',
},
});
trackEvent('NFC Failed', {
success: false,
error: e.message,
duration_ms: Date.now() - startTime,
});
}
}
};
const scanIOS = async (setModalProofStep: (modalProofStep: number) => void) => {
const scanIOS = async (
setModalProofStep: (modalProofStep: number) => void,
startTime: number,
) => {
const { passportNumber, dateOfBirth, dateOfExpiry } = useUserStore.getState();
const { toast } = useNavigationStore.getState();
const { toast, trackEvent } = useNavigationStore.getState();
console.log('passportNumber', passportNumber);
console.log('dateOfBirth', dateOfBirth);
@@ -98,18 +121,13 @@ const scanIOS = async (setModalProofStep: (modalProofStep: number) => void) => {
);
console.log('scanned');
handleResponseIOS(response, setModalProofStep);
amplitude.track('nfc_scan_successful');
trackEvent('NFC Success', {
success: true,
duration_ms: Date.now() - startTime,
});
} catch (e: any) {
console.log('error during scan:', e);
amplitude.track('nfc_scan_unsuccessful', { error: e.message });
// if (!e.message.includes("UserCanceled")) {
// toast.show('Failed to read passport', {
// message: e.message,
// customData: {
// type: "error",
// },
// })
// }
if (e.message.includes('InvalidMRZKey')) {
toast.show('Error', {
message:
@@ -120,6 +138,10 @@ const scanIOS = async (setModalProofStep: (modalProofStep: number) => void) => {
timeout: 5000,
});
useNavigationStore.getState().setSelectedTab('scan');
trackEvent('Invalid Key', {
success: false,
error: e.message,
});
} else {
toast.show('Error', {
message: e.message,
@@ -127,6 +149,10 @@ const scanIOS = async (setModalProofStep: (modalProofStep: number) => void) => {
type: 'error',
},
});
trackEvent('NFC Failed', {
success: false,
error: e.message,
});
}
}
};
@@ -135,7 +161,7 @@ const handleResponseIOS = async (
response: any,
_setModalProofStep: (modalProofStep: number) => void,
) => {
const { toast } = useNavigationStore.getState();
const { toast, trackEvent } = useNavigationStore.getState();
const parsed = JSON.parse(response);
@@ -149,21 +175,16 @@ const handleResponseIOS = async (
const signedAttributes = parsed?.signedAttributes; // this is what we call eContent in android world
const mrz = parsed?.passportMRZ;
const signatureBase64 = parsed?.signatureBase64;
console.log('dataGroupsPresent', parsed?.dataGroupsPresent);
console.log('placeOfBirth', parsed?.placeOfBirth);
console.log('activeAuthenticationPassed', parsed?.activeAuthenticationPassed);
console.log('isPACESupported', parsed?.isPACESupported);
console.log(
'isChipAuthenticationSupported',
parsed?.isChipAuthenticationSupported,
);
console.log('residenceAddress', parsed?.residenceAddress);
console.log('passportPhoto', parsed?.passportPhoto.substring(0, 100) + '...');
console.log(
'encapsulatedContentDigestAlgorithm',
parsed?.encapsulatedContentDigestAlgorithm,
);
console.log('documentSigningCertificate', parsed?.documentSigningCertificate);
const pem = JSON.parse(parsed?.documentSigningCertificate).PEM.replace(
/\n/g,
'',
@@ -186,18 +207,6 @@ const handleResponseIOS = async (
Buffer.from(signatureBase64, 'base64'),
).map(byte => (byte > 127 ? byte - 256 : byte));
// amplitude.track('nfc_response_parsed', {
// dataGroupsPresent: parsed?.dataGroupsPresent,
// eContentLength: signedEContentArray?.length,
// concatenatedDataHashesLength: concatenatedDataHashesArraySigned?.length,
// encryptedDigestLength: encryptedDigestArray?.length,
// activeAuthenticationPassed: parsed?.activeAuthenticationPassed,
// isPACESupported: parsed?.isPACESupported,
// isChipAuthenticationSupported: parsed?.isChipAuthenticationSupported,
// encapsulatedContentDigestAlgorithm: parsed?.encapsulatedContentDigestAlgorithm,
// dsc: pem,
// });
const passportData = {
mrz,
dsc: pem,
@@ -207,24 +216,28 @@ const handleResponseIOS = async (
eContent: concatenatedDataHashesArraySigned,
signedAttr: signedEContentArray,
encryptedDigest: encryptedDigestArray,
photoBase64: 'data:image/jpeg;base64,' + parsed.passportPhoto,
photoBase64: parsed?.passportPhoto
? 'data:image/jpeg;base64,' + parsed?.passportPhoto
: '',
mockUser: false,
parsed: false,
};
const parsedPassportData = parsePassportData(passportData);
amplitude.track('nfc_response_parsed', parsedPassportData);
try {
useUserStore.getState().registerPassportData(passportData);
useNavigationStore.getState().setSelectedTab('next');
parsePassportDataAsync(passportData);
} catch (e: any) {
console.log('error during parsing:', e);
amplitude.track('error_parsing_nfc_response', { error: e.message });
toast.show('Error', {
toast.show('Error during passport data parsing', {
message: e.message,
customData: {
type: 'error',
},
});
trackEvent('Passport ParseFailed', {
success: false,
error: e.message,
});
}
};
@@ -232,18 +245,18 @@ const handleResponseAndroid = async (
response: any,
_setModalProofStep: (modalProofStep: number) => void,
) => {
const { toast } = useNavigationStore.getState();
const { toast, trackEvent } = useNavigationStore.getState();
const {
mrz,
eContent,
encryptedDigest,
photo,
digestAlgorithm,
signerInfoDigestAlgorithm,
digestEncryptionAlgorithm,
LDSVersion,
unicodeVersion,
// digestAlgorithm,
// signerInfoDigestAlgorithm,
// digestEncryptionAlgorithm,
// LDSVersion,
// unicodeVersion,
encapContent,
documentSigningCertificate,
dataGroupHashes,
@@ -272,8 +285,9 @@ const handleResponseAndroid = async (
eContent: JSON.parse(encapContent),
signedAttr: JSON.parse(eContent),
encryptedDigest: JSON.parse(encryptedDigest),
photoBase64: photo.base64,
photoBase64: photo?.base64 ?? '',
mockUser: false,
parsed: false,
};
console.log(
@@ -287,7 +301,7 @@ const handleResponseAndroid = async (
2,
),
);
/***
console.log('mrz', passportData?.mrz);
console.log('dataGroupHashes', passportData?.eContent);
console.log('eContent', passportData?.eContent);
@@ -303,31 +317,56 @@ const handleResponseAndroid = async (
console.log('unicodeVersion', unicodeVersion);
console.log('encapContent', encapContent);
console.log('documentSigningCertificate', documentSigningCertificate);
const parsedPassportData = parsePassportData(passportData);
amplitude.track('nfc_response_parsed', parsedPassportData);
// amplitude.track('nfc_response_parsed', {
// dataGroupHashesLength: passportData?.eContent?.length,
// eContentLength: passportData?.eContent?.length,
// encryptedDigestLength: passportData?.encryptedDigest?.length,
// digestAlgorithm: digestAlgorithm,
// signerInfoDigestAlgorithm: signerInfoDigestAlgorithm,
// digestEncryptionAlgorithm: digestEncryptionAlgorithm,
// dsc: pem,
// mockUser: false
// });
***/
try {
await useUserStore.getState().registerPassportData(passportData);
useNavigationStore.getState().setSelectedTab('next');
parsePassportDataAsync(passportData);
} catch (e: any) {
console.log('error during parsing:', e);
amplitude.track('error_parsing_nfc_response', { error: e.message });
toast.show('Error', {
message: e.message,
customData: {
type: 'error',
},
});
trackEvent('Passport ParseFailed', {
success: false,
error: e.message,
});
}
};
async function parsePassportDataAsync(passportData: PassportData) {
const { trackEvent } = useNavigationStore.getState();
const parsedPassportData = parsePassportData(passportData);
useUserStore.getState().setPassportMetadata(parsedPassportData);
await useUserStore.getState().registerPassportData(passportData);
trackEvent('Passport Parsed', {
success: true,
data_groups: parsedPassportData.dataGroups,
dg1_hash_function: parsedPassportData.dg1HashFunction,
dg1_hash_offset: parsedPassportData.dg1HashOffset,
dg_padding_bytes: parsedPassportData.dgPaddingBytes,
e_content_size: parsedPassportData.eContentSize,
e_content_hash_function: parsedPassportData.eContentHashFunction,
e_content_hash_offset: parsedPassportData.eContentHashOffset,
signed_attr_size: parsedPassportData.signedAttrSize,
signed_attr_hash_function: parsedPassportData.signedAttrHashFunction,
signature_algorithm: parsedPassportData.signatureAlgorithm,
salt_length: parsedPassportData.saltLength,
curve_or_exponent: parsedPassportData.curveOrExponent,
signature_algorithm_bits: parsedPassportData.signatureAlgorithmBits,
country_code: parsedPassportData.countryCode,
csca_found: parsedPassportData.cscaFound,
csca_hash_function: parsedPassportData.cscaHashFunction,
csca_signature_algorithm: parsedPassportData.cscaSignatureAlgorithm,
csca_salt_length: parsedPassportData.cscaSaltLength,
csca_curve_or_exponent: parsedPassportData.cscaCurveOrExponent,
csca_signature_algorithm_bits:
parsedPassportData.cscaSignatureAlgorithmBits,
dsc: parsedPassportData.dsc,
});
useNavigationStore.getState().setSelectedTab('next');
}

View File

@@ -6,7 +6,7 @@ import * as amplitude from '@amplitude/analytics-react-native';
import '@react-navigation/native';
import { Buffer } from 'buffer';
import { parsePassportData } from '../../../common/src/utils/parsePassportData';
import { parsePassportData } from '../../../common/src/utils/passports/passport_parsing/parsePassportData';
import { PassportData } from '../../../common/src/utils/types';
import useNavigationStore from '../stores/navigationStore';
import useUserStore from '../stores/userStore';
@@ -179,6 +179,7 @@ const handleResponseIOS = async (response: any) => {
encryptedDigest: encryptedDigestArray,
photoBase64: 'data:image/jpeg;base64,' + parsed.passportPhoto,
mockUser: false,
parsed: false,
};
const parsedPassportData = parsePassportData(passportData);
amplitude.track('nfc_response_parsed', parsedPassportData);
@@ -238,6 +239,7 @@ const handleResponseAndroid = async (response: any) => {
encryptedDigest: JSON.parse(encryptedDigest),
photoBase64: photo.base64,
mockUser: false,
parsed: false,
};
console.log(

View File

@@ -1,27 +1,30 @@
import { NativeModules, Platform } from 'react-native';
import RNFS from 'react-native-fs';
import * as amplitude from '@amplitude/analytics-react-native';
import useNavigationStore from '../stores/navigationStore';
import { parseProofAndroid } from './utils';
export const generateProof = async (circuit: string, inputs: any) => {
console.log('launching generateProof function');
console.log('inputs in prover.ts', inputs);
console.log('circuit', circuit);
const startTime = Date.now();
const { trackEvent } = useNavigationStore.getState();
trackEvent('Proof Started', {
success: true,
circuit: circuit,
});
// Example: "/data/user/0/com.proofofpassportapp/files/register_sha256WithRSAEncryption_65537.zkey" on android
const zkey_path = `${RNFS.DocumentDirectoryPath}/${circuit}.zkey`;
const dat_path = `${RNFS.DocumentDirectoryPath}/${circuit}.dat`;
const witness_calculator = circuit;
if (!zkey_path || !witness_calculator || !dat_path) {
trackEvent('Proof Failed', {
success: false,
error: 'Required parameters are missing',
circuit: circuit,
});
throw new Error('Required parameters are missing');
}
console.log('zkey_path', zkey_path);
console.log('witness_calculator', witness_calculator);
console.log('dat_path', dat_path);
try {
const response = await NativeModules.Prover.runProveAction(
@@ -31,15 +34,24 @@ export const generateProof = async (circuit: string, inputs: any) => {
inputs,
);
// console.log('local proof:', response);
if (Platform.OS === 'android') {
const parsedResponse = parseProofAndroid(response);
console.log('parsedResponse', parsedResponse);
trackEvent('Proof Generated', {
success: true,
duration_ms: Date.now() - startTime,
circuit: circuit,
});
return formatProof(parsedResponse);
} else {
const parsedResponse = JSON.parse(response);
console.log('parsedResponse', parsedResponse);
trackEvent('Proof Generated', {
success: true,
duration_ms: Date.now() - startTime,
circuit: circuit,
});
return formatProof({
proof: parsedResponse.proof,
@@ -47,31 +59,47 @@ export const generateProof = async (circuit: string, inputs: any) => {
});
}
} catch (err: any) {
console.log('err', err);
amplitude.track('error_generating_proof', {
trackEvent('Proof Failed', {
success: false,
error: err.message,
duration_ms: Date.now() - startTime,
circuit: circuit,
zkey_path: zkey_path,
witness_calculator: witness_calculator,
dat_path: dat_path,
});
throw new Error(err);
}
};
export const formatProof = (rawProof: any): any => {
return {
proof: {
pi_a: [rawProof.proof.a[0], rawProof.proof.a[1], '1'],
pi_b: [
[rawProof.proof.b[0][0], rawProof.proof.b[0][1]],
[rawProof.proof.b[1][0], rawProof.proof.b[1][1]],
['1', '0'],
],
pi_c: [rawProof.proof.c[0], rawProof.proof.c[1], '1'],
protocol: 'groth16',
curve: 'bn128',
},
publicSignals: (rawProof as any).pub_signals,
};
const { trackEvent } = useNavigationStore.getState();
try {
const formattedProof = {
proof: {
pi_a: [rawProof.proof.a[0], rawProof.proof.a[1], '1'],
pi_b: [
[rawProof.proof.b[0][0], rawProof.proof.b[0][1]],
[rawProof.proof.b[1][0], rawProof.proof.b[1][1]],
['1', '0'],
],
pi_c: [rawProof.proof.c[0], rawProof.proof.c[1], '1'],
protocol: 'groth16',
curve: 'bn128',
},
publicSignals: (rawProof as any).pub_signals,
};
trackEvent('Proof Formatted', {
success: true,
});
return formattedProof;
} catch (err: any) {
trackEvent('Proof FormatFailed', {
success: false,
error: err.message,
});
throw err;
}
};

View File

@@ -5,7 +5,6 @@ import pako from 'pako';
import { Mode, OpenPassportApp } from '../../../common/src/utils/appType';
import { getCircuitNameOld } from '../../../common/src/utils/certificate_parsing/parseCertificateSimple';
import { parsePassportData } from '../../../common/src/utils/parsePassportData';
import useNavigationStore from '../stores/navigationStore';
import useUserStore from '../stores/userStore';
import { downloadZkey } from './zkeyDownload';
@@ -26,81 +25,66 @@ export const scanQRCode = () => {
const { toast, setSelectedApp, setSelectedTab } =
useNavigationStore.getState();
Linking.getInitialURL()
.then(url => {
if (url) {
handleUniversalLink(url);
} else {
if (Platform.OS === 'ios') {
console.log('Scanning QR code on iOS without Universal Link');
const qrScanner = NativeModules.QRScannerBridge;
if (qrScanner && qrScanner.scanQRCode) {
qrScanner
.scanQRCode()
.then((result: string) => {
const params = parseUrlParams(result);
const encodedData = params.get('data');
handleQRCodeScan(
encodedData as string,
toast,
setSelectedApp,
setSelectedTab,
);
})
.catch((error: any) => {
console.error('QR Scanner Error:', error);
toast.show('Error', {
message: 'Failed to scan QR code',
type: 'error',
});
});
} else {
console.error('QR Scanner module not found for iOS');
toast.show('Error', {
message: 'QR Scanner not available',
type: 'error',
});
}
} else if (Platform.OS === 'android') {
const qrScanner = NativeModules.QRCodeScanner;
if (qrScanner && qrScanner.scanQRCode) {
qrScanner
.scanQRCode()
.then((result: string) => {
const params = parseUrlParams(result);
const encodedData = params.get('data');
handleQRCodeScan(
encodedData as string,
toast,
setSelectedApp,
setSelectedTab,
);
})
.catch((error: any) => {
console.error('QR Scanner Error:', error);
toast.show('Error', {
message: 'Failed to scan QR code',
type: 'error',
});
});
} else {
console.error('QR Scanner module not found for Android');
toast.show('Error', {
message: 'QR Scanner not available',
type: 'error',
});
}
}
}
})
.catch(err => {
console.error('An error occurred while getting initial URL', err);
if (Platform.OS === 'ios') {
console.log('Scanning QR code on iOS');
const qrScanner = NativeModules.QRScannerBridge;
if (qrScanner && qrScanner.scanQRCode) {
qrScanner
.scanQRCode()
.then((result: string) => {
const params = parseUrlParams(result);
const encodedData = params.get('data');
handleQRCodeScan(
encodedData as string,
toast,
setSelectedApp,
setSelectedTab,
);
})
.catch((error: any) => {
console.error('QR Scanner Error:', error);
toast.show('Error', {
message: 'Failed to scan QR code',
type: 'error',
});
});
} else {
console.error('QR Scanner module not found for iOS');
toast.show('Error', {
message: 'Failed to process initial link',
message: 'QR Scanner not available',
type: 'error',
});
});
}
} else if (Platform.OS === 'android') {
const qrScanner = NativeModules.QRCodeScanner;
if (qrScanner && qrScanner.scanQRCode) {
qrScanner
.scanQRCode()
.then((result: string) => {
const params = parseUrlParams(result);
const encodedData = params.get('data');
handleQRCodeScan(
encodedData as string,
toast,
setSelectedApp,
setSelectedTab,
);
})
.catch((error: any) => {
console.error('QR Scanner Error:', error);
toast.show('Error', {
message: 'Failed to scan QR code',
type: 'error',
});
});
} else {
console.error('QR Scanner module not found for Android');
toast.show('Error', {
message: 'QR Scanner not available',
type: 'error',
});
}
}
};
const handleQRCodeScan = (
@@ -110,8 +94,8 @@ const handleQRCodeScan = (
setSelectedTab: any,
) => {
try {
const passportData = useUserStore.getState().passportData;
if (passportData) {
const { passportData, passportMetadata } = useUserStore.getState();
if (passportData && passportMetadata) {
const decodedResult = atob(result);
const uint8Array = new Uint8Array(
decodedResult.split('').map(char => char.charCodeAt(0)),
@@ -120,7 +104,6 @@ const handleQRCodeScan = (
const unpackedData = msgpack.decode(decompressedData);
const openPassportApp: OpenPassportApp = unpackedData;
setSelectedApp(openPassportApp);
const passportMetadata = parsePassportData(passportData);
const circuitName =
openPassportApp.mode === 'vc_and_disclose'

View File

@@ -5,7 +5,7 @@ import pako from 'pako';
import { Mode, OpenPassportApp } from '../../../common/src/utils/appType';
import { getCircuitNameOld } from '../../../common/src/utils/certificate_parsing/parseCertificateSimple';
import { parsePassportData } from '../../../common/src/utils/parsePassportData';
import { parsePassportData } from '../../../common/src/utils/passports/passport_parsing/parsePassportData';
import useNavigationStore from '../stores/navigationStore';
import useUserStore from '../stores/userStore';
import { downloadZkey } from './zkeyDownload';

View File

@@ -1,56 +0,0 @@
import { LeanIMT, LeanIMTHashFunction } from '@openpassport/zk-kit-lean-imt';
import axios from 'axios';
import { poseidon2 } from 'poseidon-lite';
import {
COMMITMENT_TREE_TRACKER_URL,
PASSPORT_ATTESTATION_ID,
} from '../../../common/src/constants/constants';
import { findIndexInTree } from '../../../common/src/utils/generateInputs';
import {
generateCommitment,
getLeaf,
} from '../../../common/src/utils/pubkeyTree';
import { PassportData } from '../../../common/src/utils/types';
import { formatMrz, packBytes } from '../../../common/src/utils/utils';
export async function isCommitmentRegistered(
secret: string,
passportData: PassportData,
) {
let response;
console.log(COMMITMENT_TREE_TRACKER_URL);
try {
response = await axios.get(COMMITMENT_TREE_TRACKER_URL);
} catch (error) {
console.error('Error fetching commitment tree:', error);
throw error; // rethrow the error after logging
}
console.log('response.data:', response.data);
const hashFunction: LeanIMTHashFunction = (a: bigint, b: bigint) =>
poseidon2([a, b]);
const imt = LeanIMT.import(hashFunction, response.data);
const pubkey_leaf = getLeaf(passportData.dsc);
const formattedMrz = formatMrz(passportData.mrz);
const mrz_bytes = packBytes(formattedMrz);
const commitment = generateCommitment(
secret,
PASSPORT_ATTESTATION_ID,
pubkey_leaf,
mrz_bytes,
passportData.dg2Hash?.map(x => x.toString()) || [],
);
console.log('commitment', commitment.toString());
try {
findIndexInTree(imt as any, commitment); // this will throw if not found
return true;
} catch (err) {
return false;
}
}

View File

@@ -1,148 +0,0 @@
import axios from 'axios';
import { ethers } from 'ethers';
import {
CHAIN_NAME,
RELAYER_URL,
RPC_URL,
SignatureAlgorithmIndex,
} from '../../../common/src/constants/constants';
import {
formatCallData_disclose,
formatCallData_dsc,
formatCallData_register,
} from '../../../common/src/utils/formatCallData';
import { Proof } from '../../../common/src/utils/types';
import registerArtefacts from '../../deployments/artifacts/Deploy_Registry#OpenPassportRegister.json';
import sbtArtefacts from '../../deployments/artifacts/Deploy_Registry#SBT.json';
import contractAddresses from '../../deployments/deployed_addresses.json';
import groth16ExportSolidityCallData from './snarkjs';
export const sendRegisterTransaction = async (
proof: Proof,
cscaProof: Proof,
sigAlgIndex: SignatureAlgorithmIndex,
) => {
const provider = new ethers.JsonRpcProvider(RPC_URL);
if (
!contractAddresses['Deploy_Registry#OpenPassportRegister'] ||
!registerArtefacts.abi
) {
console.log('contracts addresses or abi not found');
return;
}
// Format the proof and publicInputs as calldata for the verifier contract
//console.log("exporting local proof:", proof, proof.proof, proof.pub_signals);
const cd = groth16ExportSolidityCallData(proof.proof, proof.pub_signals);
const callData = JSON.parse(`[${cd}]`);
//console.log('callData', callData);
const formattedCallData_register = formatCallData_register(callData);
console.log('formattedCallData_register', formattedCallData_register);
//console.log("exporting csca proof", cscaProof, cscaProof.proof, cscaProof.pub_signals)
const cd_csca = groth16ExportSolidityCallData(
cscaProof.proof,
cscaProof.pub_signals,
);
const callData_csca = JSON.parse(`[${cd_csca}]`);
//console.log('callData_csca', callData_csca);
const formattedCallData_csca = formatCallData_dsc(callData_csca);
console.log('formattedCallData_csca', formattedCallData_csca);
try {
const registerContract = new ethers.Contract(
contractAddresses['Deploy_Registry#OpenPassportRegister'],
registerArtefacts.abi,
provider,
);
const transactionRequest =
await registerContract.validateProof.populateTransaction(
formattedCallData_register,
formattedCallData_csca,
sigAlgIndex,
sigAlgIndex,
);
console.log('transactionRequest', transactionRequest);
const response = await axios.post(RELAYER_URL, {
chain: CHAIN_NAME,
tx_data: transactionRequest,
});
console.log('response status', response.status);
console.log('response data', response.data);
return response;
} catch (err: any) {
console.log('err', err);
if (err.isAxiosError && err.response) {
const errorMessage = err.response.data.error;
console.log('Server error message:', errorMessage);
// parse blockchain error and show it
const match = errorMessage.match(/execution reverted: "([^"]*)"/);
if (match && match[1]) {
console.log('Parsed blockchain error:', match[1]);
throw new Error(match[1]);
} else {
throw new Error(errorMessage);
}
}
}
};
export const mintSBT = async (proof: Proof) => {
const provider = new ethers.JsonRpcProvider(RPC_URL);
if (!contractAddresses['Deploy_Registry#SBT'] || !sbtArtefacts.abi) {
console.log('contracts addresses or abi not found');
return;
}
// Format the proof and publicInputs as calldata for the verifier contract
const cd = groth16ExportSolidityCallData(proof.proof, proof.pub_signals);
const parsedCallData_disclose = JSON.parse(`[${cd}]`);
console.log('parsedCallData_disclose', parsedCallData_disclose);
const formattedCallData_disclose = formatCallData_disclose(
parsedCallData_disclose,
);
try {
const proofOfPassportContract = new ethers.Contract(
contractAddresses['Deploy_Registry#SBT'],
sbtArtefacts.abi,
provider,
);
const transactionRequest =
await proofOfPassportContract.mint.populateTransaction(
formattedCallData_disclose,
);
console.log('transactionRequest', transactionRequest);
const response = await axios.post(RELAYER_URL, {
chain: CHAIN_NAME,
tx_data: transactionRequest,
});
console.log('response status', response.status);
console.log('response data', response.data);
return response;
} catch (err: any) {
console.log('err', err);
if (err.isAxiosError && err.response) {
const errorMessage = err.response.data.error;
console.log('Server error message:', errorMessage);
// parse blockchain error and show it
const match = errorMessage.match(/execution reverted: "([^"]*)"/);
if (match && match[1]) {
console.log('Parsed blockchain error:', match[1]);
throw new Error(match[1]);
} else {
throw new Error(errorMessage);
}
}
}
};

View File

@@ -85,10 +85,14 @@ export const parseProofAndroid = (response: string) => {
};
export function getFirstName(mrz: string): string {
const names = mrz.split('<<');
const firstName = names[1].split('<')[0].trim();
const capitalized = firstName.charAt(0) + firstName.slice(1).toLowerCase();
return capitalized || 'Unknown';
try {
const names = mrz.split('<<');
const firstName = names[1].split('<')[0].trim();
const capitalized = firstName.charAt(0) + firstName.slice(1).toLowerCase();
return capitalized || 'Unknown';
} catch (error) {
return '';
}
}
export function checkInputs(

View File

@@ -1,7 +1,6 @@
import RNFS from 'react-native-fs';
import { unzip } from 'react-native-zip-archive';
import * as amplitude from '@amplitude/analytics-react-native';
import NetInfo from '@react-native-community/netinfo';
import axios from 'axios';
@@ -54,33 +53,59 @@ export type IsZkeyDownloading = {
// => this should be fine is the function is never called after the commitment is registered.
export async function downloadZkey(circuit: CircuitName) {
const { isZkeyDownloading, update } = useNavigationStore.getState();
const { isZkeyDownloading, update, trackEvent } =
useNavigationStore.getState();
const startTime = Date.now();
trackEvent('Download Started', {
success: true,
circuit: circuit,
});
const downloadRequired = await isDownloadRequired(circuit, isZkeyDownloading);
if (!downloadRequired) {
console.log(`zkey and dat for ${circuit} already downloaded`);
amplitude.track('zkey_already_downloaded', { circuit: circuit });
trackEvent('Download Skipped', {
success: true,
circuit: circuit,
reason: 'already_downloaded',
});
return;
}
const networkInfo = await NetInfo.fetch();
console.log('Network type:', networkInfo.type);
if (networkInfo.type === 'wifi') {
fetchZkeyAndDat(circuit);
} else {
const zkeyResponse = await axios.head(zkeyZipUrls[circuit]);
const datResponse = await axios.head(datZipUrls[circuit]);
const expectedSize =
parseInt(zkeyResponse.headers['content-length'], 10) +
parseInt(datResponse.headers['content-length'], 10);
try {
const networkInfo = await NetInfo.fetch();
if (networkInfo.type === 'wifi') {
fetchZkeyAndDat(circuit);
} else {
const zkeyResponse = await axios.head(zkeyZipUrls[circuit]);
const datResponse = await axios.head(datZipUrls[circuit]);
const expectedSize =
parseInt(zkeyResponse.headers['content-length'], 10) +
parseInt(datResponse.headers['content-length'], 10);
update({
showWarningModal: {
show: true,
update({
showWarningModal: {
show: true,
circuit: circuit,
size: expectedSize,
},
});
trackEvent('Download Paused', {
success: true,
circuit: circuit,
size: expectedSize,
},
reason: 'no_wifi',
expected_size: expectedSize,
});
}
} catch (error: any) {
trackEvent('Download Failed', {
success: false,
error: error.message,
circuit: circuit,
duration_ms: Date.now() - startTime,
});
throw error;
}
}
@@ -136,11 +161,19 @@ export async function isDownloadRequired(
}
export async function fetchZkeyAndDat(circuit: CircuitName) {
console.log(`fetching zkey and dat for ${circuit} ...`);
amplitude.track('fetching_zkey_and_dat', { circuit: circuit });
const startTime = Date.now();
const {
isZkeyDownloading,
toast,
update,
setZkeyDownloadedPercentage,
trackEvent,
} = useNavigationStore.getState();
const { isZkeyDownloading, toast, update, setZkeyDownloadedPercentage } =
useNavigationStore.getState();
trackEvent('Files Download Started', {
success: true,
circuit: circuit,
});
update({
isZkeyDownloading: {
@@ -149,7 +182,6 @@ export async function fetchZkeyAndDat(circuit: CircuitName) {
},
});
const startTime = Date.now();
let previousPercentComplete = -1;
const downloadFile = async (url: string, fileName: string) => {
@@ -158,7 +190,11 @@ export async function fetchZkeyAndDat(circuit: CircuitName) {
toFile: `${RNFS.DocumentDirectoryPath}/${fileName}`,
background: false,
begin: () => {
console.log(`Download of ${fileName} has begun`);
trackEvent('File Download Started', {
success: true,
circuit: circuit,
file_type: fileName.includes('zkey') ? 'zkey' : 'dat',
});
},
progress: (res: any) => {
if (fileName.endsWith('.zkey.zip')) {
@@ -169,7 +205,6 @@ export async function fetchZkeyAndDat(circuit: CircuitName) {
percentComplete % 5 === 0 &&
percentComplete !== previousPercentComplete
) {
console.log(`${percentComplete}%`);
previousPercentComplete = percentComplete;
setZkeyDownloadedPercentage(percentComplete);
}
@@ -178,9 +213,11 @@ export async function fetchZkeyAndDat(circuit: CircuitName) {
};
await RNFS.downloadFile(options).promise;
console.log(`Download of ${fileName} complete`);
RNFS.readDir(RNFS.DocumentDirectoryPath).then(result => {
console.log('Directory contents before unzipping:', result);
trackEvent('File Download Completed', {
success: true,
circuit: circuit,
file_type: fileName.includes('zkey') ? 'zkey' : 'dat',
});
};
@@ -198,60 +235,88 @@ export async function fetchZkeyAndDat(circuit: CircuitName) {
},
});
console.log(
'zkey and dat download succeeded, took ' +
(Date.now() - startTime) / 1000 +
' seconds',
);
await saveFileSize(circuit, 'zkey');
await saveFileSize(circuit, 'dat');
await RNFS.unlink(`${RNFS.DocumentDirectoryPath}/${circuit}.zkey.zip`);
await RNFS.unlink(`${RNFS.DocumentDirectoryPath}/${circuit}.dat.zip`);
console.log('zip files deleted');
const result = await RNFS.readDir(RNFS.DocumentDirectoryPath);
console.log('Directory contents at the end:', result);
trackEvent('Files Download Completed', {
success: true,
circuit: circuit,
duration_ms: Date.now() - startTime,
});
} catch (error: any) {
console.error(error);
update({
isZkeyDownloading: {
...isZkeyDownloading,
[circuit]: false,
},
});
trackEvent('Files Download Failed', {
success: false,
error: error.message,
circuit: circuit,
duration_ms: Date.now() - startTime,
});
toast.show('Error', {
message: `Error: ${error.message}`,
customData: {
type: 'error',
},
customData: { type: 'error' },
});
}
}
async function unzipFile(circuit: CircuitName, fileType: 'zkey' | 'dat') {
const unzipPath = `${RNFS.DocumentDirectoryPath}/${circuit}_temp`;
await unzip(
`${RNFS.DocumentDirectoryPath}/${circuit}.${fileType}.zip`,
unzipPath,
);
const files = await RNFS.readDir(unzipPath);
const targetFile = files.find(file => file.name.endsWith(`.${fileType}`));
if (targetFile) {
const destinationPath = `${RNFS.DocumentDirectoryPath}/${circuit}.${fileType}`;
if (await RNFS.exists(destinationPath)) {
await RNFS.unlink(destinationPath);
console.log(`Old ${fileType} file deleted`);
}
await RNFS.moveFile(targetFile.path, destinationPath);
console.log(`File moved to ${circuit}.${fileType}`);
} else {
throw new Error(
`${fileType.toUpperCase()} file not found in the unzipped directory`,
const { trackEvent } = useNavigationStore.getState();
const startTime = Date.now();
trackEvent('File Unzip Started', {
success: true,
circuit: circuit,
file_type: fileType,
});
try {
const unzipPath = `${RNFS.DocumentDirectoryPath}/${circuit}_temp`;
await unzip(
`${RNFS.DocumentDirectoryPath}/${circuit}.${fileType}.zip`,
unzipPath,
);
const files = await RNFS.readDir(unzipPath);
const targetFile = files.find(file => file.name.endsWith(`.${fileType}`));
if (targetFile) {
const destinationPath = `${RNFS.DocumentDirectoryPath}/${circuit}.${fileType}`;
if (await RNFS.exists(destinationPath)) {
await RNFS.unlink(destinationPath);
console.log(`Old ${fileType} file deleted`);
}
await RNFS.moveFile(targetFile.path, destinationPath);
console.log(`File moved to ${circuit}.${fileType}`);
} else {
throw new Error(
`${fileType.toUpperCase()} file not found in the unzipped directory`,
);
}
await RNFS.unlink(unzipPath);
trackEvent('File Unzip Completed', {
success: true,
circuit: circuit,
file_type: fileType,
duration_ms: Date.now() - startTime,
});
} catch (error: any) {
trackEvent('File Unzip Failed', {
success: false,
error: error.message,
circuit: circuit,
file_type: fileType,
duration_ms: Date.now() - startTime,
});
throw error;
}
await RNFS.unlink(unzipPath);
}
async function saveFileSize(circuit: CircuitName, fileType: 'zkey' | 'dat') {

File diff suppressed because it is too large Load Diff

3
circuits/.gitignore vendored
View File

@@ -1,5 +1,6 @@
inputs
build
build/
!scripts/build/
node_modules/
err.log
.env

View File

@@ -1,64 +1,115 @@
pragma circom 2.1.9;
include "../utils/passport/disclose/verify_commitment.circom";
include "../utils/passport/disclose/disclose.circom";
include "../utils/passport/disclose/proveCountryIsNotInList.circom";
include "../utils/passport/ofac/ofac_name.circom";
include "../utils/passport/disclose/verify_commitment.circom";
include "../utils/passport/date/isValid.circom";
template VC_AND_DISCLOSE( nLevels,FORBIDDEN_COUNTRIES_LIST_LENGTH) {
/// @title VC_AND_DISCLOSE
/// @notice Verify user's commitment is part of the merkle tree and optionally disclose data from DG1
/// @param nLevels Maximum number of levels in the merkle tree
/// @param MAX_FORBIDDEN_COUNTRIES_LIST_LENGTH Maximum number of countries present in the forbidden countries list
/// @input secret Secret of the user — used to reconstruct commitment and generate nullifier
/// @input attestation_id Attestation ID of the credential used to generate the commitment
/// @input dg1 Data group 1 of the passport
/// @input eContent_shaBytes_packed_hash Hash of the eContent packed
/// @input dsc_tree_leaf Leaf of the DSC tree, to keep a record of the full CSCA and DSC that were used
/// @input merkle_root Root of the commitment merkle tree
/// @input leaf_depth Actual size of the merkle tree
/// @input path Path of the commitment in the merkle tree
/// @input siblings Siblings of the commitment in the merkle tree
/// @input selector_dg1 bitmap used which bytes from the dg1 are revealed
/// @input majority Majority user wants to prove he is older than: YY — ASCII
/// @input current_date Current date: YYMMDD — number
/// @input selector_older_than bitmap used to reveal the majority
/// @input forbidden_countries_list Forbidden countries list user wants to prove he is not from
/// @input smt_leaf_key value of the leaf of the smt corresponding to his path
/// @input smt_root root of the smt
/// @input smt_siblings siblings of the smt
/// @input selector_ofac bitmap used to reveal the OFAC verification result
/// @input scope Scope of the application users generates the proof for
/// @input user_identifier User identifier — address or UUID
/// @output revealedData_packed Packed revealed data
/// @output forbidden_countries_list_packed Packed forbidden countries list
/// @output nullifier Scope nullifier - not deterministic on the passport data
template VC_AND_DISCLOSE(nLevels, MAX_FORBIDDEN_COUNTRIES_LIST_LENGTH) {
signal input secret;
signal input attestation_id;
signal input pubkey_leaf;
signal input dg1[93];
signal input dg2_hash[64];
signal input eContent_shaBytes_packed_hash;
signal input dsc_tree_leaf;
signal input merkle_root;
signal input merkletree_size;
signal input leaf_depth;
signal input path[nLevels];
signal input siblings[nLevels];
signal input selector_dg1[88]; // 88 for MRZ
signal input selector_older_than;
signal input scope;
signal input current_date[6]; // YYMMDD - num
signal input majority[2]; // YY - ASCII
signal input user_identifier;
signal input selector_dg1[88];
signal input majority[2];
signal input current_date[6];
signal input selector_older_than;
signal input forbidden_countries_list[MAX_FORBIDDEN_COUNTRIES_LIST_LENGTH * 3];
// ofac check
signal input smt_leaf_key;
signal input smt_root;
signal input smt_siblings[256];
signal input selector_ofac;
// forbidden countries list
signal input forbidden_countries_list[FORBIDDEN_COUNTRIES_LIST_LENGTH * 3];
signal input scope;
signal input user_identifier;
// verify commitment is part of the merkle tree
VERIFY_COMMITMENT(nLevels)(secret, attestation_id, pubkey_leaf, dg1, dg2_hash, merkle_root, merkletree_size, path, siblings);
VERIFY_COMMITMENT(nLevels)(
secret,
attestation_id,
dg1,
eContent_shaBytes_packed_hash,
dsc_tree_leaf,
merkle_root,
leaf_depth,
path,
siblings
);
// verify passport validity and disclose optional data
component disclose = DISCLOSE();
// verify passport validity
signal validity_ASCII[6];
for (var i = 0; i < 6; i++) {
validity_ASCII[i] <== dg1[70 +i];
}
IsValid()(current_date,validity_ASCII);
// disclose optional data
component disclose = DISCLOSE(10);
disclose.dg1 <== dg1;
disclose.selector_dg1 <== selector_dg1;
disclose.selector_older_than <== selector_older_than;
disclose.current_date <== current_date;
disclose.majority <== majority;
// generate scope nullifier
component poseidon_nullifier = Poseidon(2);
poseidon_nullifier.inputs[0] <== secret;
poseidon_nullifier.inputs[1] <== scope;
signal output nullifier <== poseidon_nullifier.out;
disclose.smt_leaf_key <== smt_leaf_key;
disclose.smt_root <== smt_root;
disclose.smt_siblings <== smt_siblings;
disclose.selector_ofac <== selector_ofac;
disclose.forbidden_countries_list <== forbidden_countries_list;
signal output revealedData_packed[3] <== disclose.revealedData_packed;
signal output older_than[2] <== disclose.older_than;
// COUNTRY IS IN LIST
signal output forbidden_countries_list_packed_disclosed[2] <== ProveCountryIsNotInList(FORBIDDEN_COUNTRIES_LIST_LENGTH)(dg1, forbidden_countries_list);
var chunkLength = computeIntChunkLength(MAX_FORBIDDEN_COUNTRIES_LIST_LENGTH * 3);
signal output forbidden_countries_list_packed[chunkLength] <== disclose.forbidden_countries_list_packed;
// OFAC
signal ofacCheckResult <== OFAC_NAME()(dg1,smt_leaf_key,smt_root,smt_siblings);
signal ofacIntermediaryOutput <== ofacCheckResult * selector_ofac;
signal output ofac_result <== ofacIntermediaryOutput;
signal output nullifier <== Poseidon(2)([secret, scope]);
}
component main { public [ merkle_root, smt_root, scope, user_identifier, current_date, attestation_id] } = VC_AND_DISCLOSE(16,20);
component main {
public [
merkle_root,
smt_root,
scope,
user_identifier,
current_date,
attestation_id
]
} = VC_AND_DISCLOSE(33, 10);

View File

@@ -9,57 +9,132 @@ include "@zk-kit/binary-merkle-root.circom/src/binary-merkle-root.circom";
include "../utils/passport/customHashers.circom";
include "../utils/passport/signatureAlgorithm.circom";
include "../utils/passport/signatureVerifier.circom";
include "@openpassport/zk-email-circuits/utils/bytes.circom";
include "../utils/passport/checkPubkeysEqual.circom";
include "../utils/passport/constants.circom";
include "../utils/crypto/bitify/bytes.circom";
include "../utils/passport/BytesToNum.circom";
/// @title DSC
/// @notice Circuit for verifying DSC certificate signature using CSCA certificate
/// @param signatureAlgorithm Algorithm used for DSC signature verification - contains the information about the final hash algorithm
/// @param n_csca Number of bits per chunk the CSCA key is split into
/// @param k_csca Number of chunks the CSCA key is split into
/// @input raw_csca Raw CSCA certificate data
/// @input raw_csca_actual_length Actual length of CSCA certificate
/// @input csca_pubKey_offset Offset of CSCA public key in certificate
/// @input csca_pubKey_actual_size Actual size of CSCA public key in bytes
/// @input raw_dsc Raw DSC certificate data
/// @input raw_dsc_padded_length Actual length of DSC certificate
/// @input csca_pubKey CSCA public key for signature verification
/// @input signature DSC signature
/// @input merkle_root Root of CSCA Merkle tree
/// @input path Path indices for CSCA Merkle proof
/// @input siblings Sibling hashes for CSCA Merkle proof
/// @output dsc_tree_leaf Leaf to be added to the DSC Merkle tree
template DSC(
signatureAlgorithm,
n_csca,
k_csca
) {
var MAX_CSCA_LENGTH = getMaxCSCALength();
var MAX_DSC_LENGTH = getMaxDSCLength();
var nLevels = getMaxCSCALevels();
template DSC(signatureAlgorithm, n_dsc, k_dsc, n_csca, k_csca, max_cert_bytes, dscPubkeyBytesLength, nLevels) {
// variables verification
assert(max_cert_bytes % 64 == 0);
assert(n_csca * k_csca > max_cert_bytes);
assert(MAX_CSCA_LENGTH % 64 == 0);
assert(MAX_DSC_LENGTH % 64 == 0);
// assert(n_csca * k_csca > max_dsc_bytes); // not sure what this is for
assert(n_csca <= (255 \ 2));
var hashLength = getHashLength(signatureAlgorithm);
var minKeyLength = getMinKeyLength(signatureAlgorithm);
var kLengthFactor = getKLengthFactor(signatureAlgorithm);
var kScaled = k_csca * kLengthFactor;
var hashLength = getHashLength(signatureAlgorithm);
var MAX_CSCA_PUBKEY_LENGTH = n_csca * kScaled / 8;
signal input raw_csca[MAX_CSCA_LENGTH];
signal input raw_csca_actual_length;
signal input csca_pubKey_offset;
signal input csca_pubKey_actual_size;
signal input raw_dsc[MAX_DSC_LENGTH];
signal input raw_dsc_padded_length;
signal input raw_dsc_cert[max_cert_bytes];
signal input raw_dsc_cert_padded_bytes;
signal input csca_pubKey[kScaled];
signal input signature[kScaled];
signal input dsc_pubKey[k_dsc];
signal input dsc_pubKey_offset;
signal input secret;
signal input merkle_root;
signal input path[nLevels];
signal input siblings[nLevels];
// first, compute raw_dsc_actual_length
// by getting the values of the last 4 bytes of the padded length
// cf sha padding
signal last_four_bytes_of_padded_length[4] <== SelectSubArray(MAX_DSC_LENGTH, 4)(raw_dsc, raw_dsc_padded_length - 4, 4);
signal computed_length_bits <== BytesToNum()(last_four_bytes_of_padded_length);
signal raw_dsc_actual_length <== computed_length_bits / 8;
// leaf
signal leaf <== LeafHasher(kScaled)(csca_pubKey, signatureAlgorithm);
// sanity check: raw_dsc[raw_dsc_actual_length] should be 128
signal raw_dsc_at_actual_length <== ItemAtIndex(MAX_DSC_LENGTH)(raw_dsc, raw_dsc_actual_length);
signal isByte128 <== IsEqual()([raw_dsc_at_actual_length, 128]);
isByte128 === 1;
signal computed_merkle_root <== BinaryMerkleRoot(nLevels)(leaf, nLevels, path, siblings);
merkle_root === computed_merkle_root;
// verify certificate signature
signal hashedCertificate[hashLength] <== ShaBytesDynamic(hashLength, max_cert_bytes)(raw_dsc_cert, raw_dsc_cert_padded_bytes);
SignatureVerifier(signatureAlgorithm, n_csca, k_csca)(hashedCertificate, csca_pubKey, signature);
// verify DSC csca_pubKey
component shiftLeft = VarShiftLeft(max_cert_bytes, dscPubkeyBytesLength); // use select subarray for dscPubKey variable length
shiftLeft.in <== raw_dsc_cert;
shiftLeft.shift <== dsc_pubKey_offset;
component spbt_1 = SplitBytesToWords(dscPubkeyBytesLength, n_dsc, k_dsc);
spbt_1.in <== shiftLeft.out;
for (var i = 0; i < k_dsc; i++) {
dsc_pubKey[i] === spbt_1.out[i];
// check that raw_dsc is padded with 0s after the sha padding
// this should guarantee the dsc commitment is unique for each commitment
component byte_checks[MAX_DSC_LENGTH];
for (var i = 0; i < MAX_DSC_LENGTH; i++) {
byte_checks[i] = GreaterThan(12);
byte_checks[i].in[0] <== i;
byte_checks[i].in[1] <== raw_dsc_padded_length;
// If i >= raw_dsc_padded_length, the byte must be 0
raw_dsc[i] * byte_checks[i].out === 0;
}
// check csca_pubKey_actual_size is at least the minimum key length
signal csca_pubKey_actual_size_in_range <== GreaterEqThan(12)([
csca_pubKey_actual_size,
minKeyLength * kLengthFactor / 8
]);
csca_pubKey_actual_size_in_range === 1;
// blinded dsc commitment
signal pubkeyHash <== CustomHasher(k_dsc)(dsc_pubKey);
signal output blinded_dsc_commitment <== Poseidon(2)([secret, pubkeyHash]);
}
// check offsets refer to valid ranges
signal csca_pubKey_offset_in_range <== LessEqThan(12)([
csca_pubKey_offset + csca_pubKey_actual_size,
raw_csca_actual_length
]);
csca_pubKey_offset_in_range === 1;
// compute leaf in the CSCA Merkle tree and verify inclusion
signal csca_hash <== PackBytesAndPoseidon(MAX_CSCA_LENGTH)(raw_csca);
signal csca_tree_leaf <== Poseidon(2)([csca_hash, raw_csca_actual_length]);
signal computed_merkle_root <== BinaryMerkleRoot(nLevels)(csca_tree_leaf, nLevels, path, siblings);
merkle_root === computed_merkle_root;
// get CSCA public key from the certificate
signal extracted_csca_pubKey[MAX_CSCA_PUBKEY_LENGTH] <== SelectSubArray(MAX_CSCA_LENGTH, MAX_CSCA_PUBKEY_LENGTH)(
raw_csca,
csca_pubKey_offset,
csca_pubKey_actual_size
);
// check if the CSCA public key is the same as the one in the certificate
// If we end up adding the pubkey in the CSCA leaf, we'll be able to remove this check
CheckPubkeysEqual(n_csca, kScaled, kLengthFactor, MAX_CSCA_PUBKEY_LENGTH)(
csca_pubKey,
extracted_csca_pubKey,
csca_pubKey_actual_size
);
// verify DSC signature
// raw_dsc_padded_length is constrained because an incorrect one
// would yield hashes that have not been signed
signal hashedCertificate[hashLength] <== ShaBytesDynamic(hashLength, MAX_DSC_LENGTH)(raw_dsc, raw_dsc_padded_length);
SignatureVerifier(signatureAlgorithm, n_csca, k_csca)(hashedCertificate, csca_pubKey, signature);
// generate DSC leaf as poseidon(dsc_hash_with_actual_length, csca_tree_leaf)
signal dsc_hash <== PackBytesAndPoseidon(MAX_DSC_LENGTH)(raw_dsc);
signal dsc_hash_with_actual_length <== Poseidon(2)([dsc_hash, raw_dsc_actual_length]);
signal output dsc_tree_leaf <== Poseidon(2)([dsc_hash_with_actual_length, csca_tree_leaf]);
}

View File

@@ -1,5 +0,0 @@
pragma circom 2.1.9;
include "../dsc.circom";
component main { public [ merkle_root ] } = DSC(11, 120, 35, 120, 35, 1664, 256, 12);

View File

@@ -1,5 +0,0 @@
pragma circom 2.1.9;
include "../dsc.circom";
component main { public [ merkle_root ] } = DSC(10, 120, 35, 120, 35, 1664, 256, 12);

View File

@@ -1,5 +0,0 @@
pragma circom 2.1.9;
include "../dsc.circom";
component main { public [ merkle_root ] } = DSC(12, 120, 35, 120, 35, 1664, 256, 12);

View File

@@ -0,0 +1,5 @@
pragma circom 2.1.9;
include "../dsc.circom";
component main { public [ merkle_root] } = DSC(36, 64, 4);

View File

@@ -0,0 +1,5 @@
pragma circom 2.1.9;
include "../dsc.circom";
component main { public [ merkle_root ] } = DSC(11, 120, 35);

View File

@@ -0,0 +1,5 @@
pragma circom 2.1.9;
include "../dsc.circom";
component main { public [ merkle_root] } = DSC(21, 64, 4);

View File

@@ -0,0 +1,5 @@
pragma circom 2.1.9;
include "../dsc.circom";
component main { public [ merkle_root] } = DSC(37, 64, 6);

View File

@@ -0,0 +1,5 @@
pragma circom 2.1.9;
include "../dsc.circom";
component main { public [ merkle_root] } = DSC(8, 64, 4);

View File

@@ -0,0 +1,6 @@
pragma circom 2.1.9;
include "../dsc.circom";
component main { public [ merkle_root] } = DSC(23, 64, 6);

View File

@@ -0,0 +1,5 @@
pragma circom 2.1.9;
include "../dsc.circom";
component main { public [ merkle_root] } = DSC(40, 64, 8);

View File

@@ -0,0 +1,5 @@
pragma circom 2.1.9;
include "../dsc.circom";
component main { public [ merkle_root ] } = DSC(10, 120, 35);

View File

@@ -0,0 +1,5 @@
pragma circom 2.1.9;
include "../dsc.circom";
component main { public [ merkle_root ] } = DSC(16, 120, 35);

View File

@@ -0,0 +1,5 @@
pragma circom 2.1.9;
include "../dsc.circom";
component main { public [ merkle_root ] } = DSC(19, 120, 35);

View File

@@ -0,0 +1,5 @@
pragma circom 2.1.9;
include "../dsc.circom";
component main { public [ merkle_root ] } = DSC(12, 120, 35);

View File

@@ -0,0 +1,7 @@
pragma circom 2.1.9;
include "../dsc.circom";
component main { public [ merkle_root] } = DSC(22, 64, 6);

View File

@@ -0,0 +1,7 @@
pragma circom 2.1.9;
include "../dsc.circom";
component main { public [ merkle_root] } = DSC(38, 64, 8);

View File

@@ -0,0 +1,7 @@
pragma circom 2.1.9;
include "../dsc.circom";
component main { public [ merkle_root] } = DSC(9, 64, 6);

View File

@@ -0,0 +1,7 @@
pragma circom 2.1.9;
include "../dsc.circom";
component main { public [ merkle_root] } = DSC(29, 64, 8);

View File

@@ -0,0 +1,7 @@
pragma circom 2.1.9;
include "../dsc.circom";
component main { public [ merkle_root] } = DSC(41, 66, 8);

View File

@@ -0,0 +1,5 @@
pragma circom 2.1.9;
include "../dsc.circom";
component main { public [ merkle_root ] } = DSC(15, 120, 35);

View File

@@ -0,0 +1,5 @@
pragma circom 2.1.9;
include "../dsc.circom";
component main { public [ merkle_root ] } = DSC(39, 120, 35);

View File

@@ -1,5 +0,0 @@
pragma circom 2.1.9;
include "../register.circom";
component main = REGISTER(160, 160, 27, 32, 7, 320, 128);

View File

@@ -1,5 +0,0 @@
pragma circom 2.1.9;
include "../register.circom";
component main = REGISTER(160, 160, 7, 64, 4, 320, 128);

View File

@@ -1,5 +0,0 @@
pragma circom 2.1.9;
include "../register.circom";
component main = REGISTER(160, 160 , 3, 64, 32, 320, 128);

View File

@@ -0,0 +1,5 @@
pragma circom 2.1.9;
include "../register.circom";
component main { public [ merkle_root ] } = REGISTER(160, 160, 3, 120, 35, 384, 128);

View File

@@ -2,4 +2,4 @@ pragma circom 2.1.9;
include "../register.circom";
component main = REGISTER(160, 256 , 1, 120, 35, 320, 128);
component main { public [ merkle_root ] } = REGISTER(160, 256, 1, 120, 35, 384, 128);

View File

@@ -0,0 +1,5 @@
pragma circom 2.1.9;
include "../register.circom";
component main { public [ merkle_root ] } = REGISTER(224, 224, 30, 32, 7, 512, 128);

View File

@@ -1,5 +0,0 @@
pragma circom 2.1.9;
include "../register.circom";
component main = REGISTER(256, 224, 30, 32, 7, 448, 128);

View File

@@ -0,0 +1,5 @@
pragma circom 2.1.9;
include "../register.circom";
component main { public [ merkle_root ] } = REGISTER(256, 224, 44, 32, 7, 512, 128);

View File

@@ -1,5 +0,0 @@
pragma circom 2.1.9;
include "../register.circom";
component main = REGISTER(256, 256, 28, 32, 7, 448, 128);

View File

@@ -2,4 +2,4 @@ pragma circom 2.1.9;
include "../register.circom";
component main = REGISTER(256, 256, 21, 64, 4, 448, 128);
component main { public [ merkle_root ] } = REGISTER(256, 256, 21, 64, 4, 512, 128);

View File

@@ -0,0 +1,5 @@
pragma circom 2.1.9;
include "../register.circom";
component main { public [ merkle_root ] } = REGISTER(256, 256, 37, 64, 6, 512, 128);

View File

@@ -2,4 +2,4 @@ pragma circom 2.1.9;
include "../register.circom";
component main = REGISTER(256, 256, 8, 64, 4, 448, 128);
component main { public [ merkle_root ] } = REGISTER(256, 256, 8, 64, 4, 512, 128);

View File

@@ -2,4 +2,4 @@ pragma circom 2.1.9;
include "../register.circom";
component main = REGISTER(256, 256, 23, 64, 6, 448, 128);
component main { public [ merkle_root ] } = REGISTER(256, 256, 23, 64, 6, 512, 128);

View File

@@ -2,4 +2,4 @@ pragma circom 2.1.9;
include "../register.circom";
component main = REGISTER(256, 256, 13, 120, 35, 448, 128);
component main { public [ merkle_root ] } = REGISTER(256, 256, 13, 120, 35, 512, 128);

View File

@@ -1,5 +0,0 @@
pragma circom 2.1.9;
include "../register.circom";
component main { public [ scope, user_identifier, current_date ] } = REGISTER(256, 256, 14, 96, 32, 448, 128);

View File

@@ -2,4 +2,4 @@ pragma circom 2.1.9;
include "../register.circom";
component main = REGISTER(256 ,256 ,1, 120, 35, 448, 128);
component main { public [ merkle_root ] } = REGISTER(256, 256, 1, 120, 35, 512, 128);

View File

@@ -0,0 +1,5 @@
pragma circom 2.1.9;
include "../register.circom";
component main { public [ merkle_root ] } = REGISTER(256, 256, 43, 120, 35, 512, 128);

View File

@@ -1,5 +0,0 @@
pragma circom 2.1.9;
include "../register.circom";
component main = REGISTER(256,256, 17, 120, 35, 448, 128);

View File

@@ -0,0 +1,5 @@
pragma circom 2.1.9;
include "../register.circom";
component main { public [ merkle_root ] } = REGISTER(256, 256, 4, 120, 35, 512, 128);

View File

@@ -0,0 +1,5 @@
pragma circom 2.1.9;
include "../register.circom";
component main { public [ merkle_root ] } = REGISTER(256, 256, 19, 120, 35, 512, 128);

View File

@@ -1,5 +0,0 @@
pragma circom 2.1.9;
include "../register.circom";
component main = REGISTER(256, 256, 12, 120, 35, 448, 128);

View File

@@ -1,5 +0,0 @@
pragma circom 2.1.9;
include "../register.circom";
component main = REGISTER(384, 384, 24, 64, 4, 640, 256);

View File

@@ -2,4 +2,4 @@ pragma circom 2.1.9;
include "../register.circom";
component main = REGISTER(384, 384, 22, 64, 6, 640, 256);
component main { public [ merkle_root ] } = REGISTER(384, 384, 22, 64, 6, 768, 256);

View File

@@ -0,0 +1,5 @@
pragma circom 2.1.9;
include "../register.circom";
component main { public [ merkle_root ] } = REGISTER(384, 384, 38, 64, 8, 768, 256);

View File

@@ -2,4 +2,4 @@ pragma circom 2.1.9;
include "../register.circom";
component main = REGISTER(384, 384, 9, 64, 6, 640, 256);
component main { public [ merkle_root ] } = REGISTER(384, 384, 9, 64, 6, 768, 256);

View File

@@ -1,5 +0,0 @@
pragma circom 2.1.9;
include "../register.circom";
component main = REGISTER(512, 512, 25, 64, 4, 768, 256);

View File

@@ -1,5 +0,0 @@
pragma circom 2.1.9;
include "../register.circom";
component main = REGISTER(512, 512, 26, 64, 6, 768, 256);

View File

@@ -2,4 +2,4 @@ pragma circom 2.1.9;
include "../register.circom";
component main = REGISTER(512, 512, 29, 64, 8, 768, 256);
component main { public [ merkle_root ] } = REGISTER(512, 512, 29, 64, 8, 896, 256);

View File

@@ -2,4 +2,4 @@ pragma circom 2.1.9;
include "../register.circom";
component main = REGISTER(512, 512, 15, 120, 35, 768, 256);
component main { public [ merkle_root ] } = REGISTER(512, 512, 15, 120, 35, 896, 256);

View File

@@ -0,0 +1,5 @@
pragma circom 2.1.9;
include "../register.circom";
component main { public [ merkle_root ] } = REGISTER(512, 512, 42, 120, 35, 896, 256);

View File

@@ -1,78 +1,164 @@
pragma circom 2.1.9;
include "../utils/passport/customHashers.circom";
include "../utils/passport/computeCommitment.circom";
include "../utils/passport/signatureAlgorithm.circom";
include "../utils/passport/date/isValid.circom";
include "circomlib/circuits/poseidon.circom";
include "../utils/passport/passportVerifier.circom";
include "../utils/passport/disclose/disclose.circom";
include "../utils/passport/disclose/proveCountryIsNotInList.circom";
include "../utils/passport/ofac/ofac_name.circom";
include "../utils/passport/constants.circom";
include "../utils/crypto/bitify/splitWordsToBytes.circom";
include "../utils/crypto/bitify/bytes.circom";
include "@zk-kit/binary-merkle-root.circom/src/binary-merkle-root.circom";
include "../utils/passport/checkPubkeysEqual.circom";
/// @title REGISTER
/// @notice Main circuit to verify passport data and be used to several purposes to enable passport
/// @dev Handles passport verification, OFAC checks, selective disclosure, and commitment generation
/// @param DG_HASH_ALGO Hash algorithm used for DG (Document Group) hashing
/// @notice Main circuit verifies the integrity of the passport data, the signature, and generates commitment and nullifier
/// @param DG_HASH_ALGO Hash algorithm used for DG hashing
/// @param ECONTENT_HASH_ALGO Hash algorithm used for eContent
/// @param signatureAlgorithm Algorithm used for passport signature verification
/// @param signatureAlgorithm Algorithm used for passport signature verification - contains the information about the final hash algorithm
/// @param n Number of bits per chunk the key is split into.
/// @param k Number of chunks the key is split into.
/// @param MAX_ECONTENT_PADDED_LEN Maximum length of padded eContent
/// @param MAX_SIGNED_ATTR_PADDED_LEN Maximum length of padded signed attributes
/// @input raw_dsc Raw DSC certificate data
/// @input raw_dsc_actual_length Actual length of DSC certificate
/// @input dsc_pubKey_offset Offset of DSC public key in certificate
/// @input dsc_pubKey_actual_size Actual size of DSC public key
/// @input dg1 Document Group 1 data (93 bytes)
/// @input dg1_hash_offset Offset for DG1 hash
/// @input dg2_hash Document Group 2 hash (64 bytes)
/// @input eContent eContent data
/// @input eContent eContent data - contains all DG hashes
/// @input eContent_padded_length Padded length of eContent
/// @input signed_attr Signed attributes data
/// @input signed_attr Signed attributes
/// @input signed_attr_padded_length Padded length of signed attributes
/// @input signed_attr_econtent_hash_offset Offset for eContent hash in signed attributes
/// @input pubKey Public key for signature verification
/// @input signature Passport signature
/// @input user_identifier User identifier for commitment
/// @input secret Secret for commitment generation. Supposed to be saved by the user to access this commitment.
/// @input dsc_secret One time secret data to generate the blinded commitment. This blinded dsc commitment is used to find the link between a proof from this circuit and a proof from the dsc circuit.
/// @output nullifier Generated nullifier
/// @output commitment Unique commitment for the passport data and their secret
/// @output blinded_dsc_commitment To find the link between a proof from this circuit and a proof from the dsc circuit.
/// @input pubKey_dsc DSC public key for signature verification
/// @input signature_passport Passport signature
/// @input merkle_root Root of DSC Merkle tree
/// @input leaf_depth Actual size of the merkle tree
/// @input path Path indices for DSC Merkle proof
/// @input siblings Sibling hashes for DSC Merkle proof
/// @input csca_tree_leaf Leaf of CSCA Merkle tree
/// @input secret Secret for commitment generation. Saved by the user to access their commitment
/// @output nullifier Generated nullifier - deterministic on the passport data
/// @output commitment Commitment that will be added to the onchain registration tree
template REGISTER(
DG_HASH_ALGO,
ECONTENT_HASH_ALGO,
signatureAlgorithm,
n,
k,
MAX_ECONTENT_PADDED_LEN,
MAX_SIGNED_ATTR_PADDED_LEN
) {
var MAX_DSC_LENGTH = getMaxDSCLength();
var nLevels = getMaxDSCLevels();
template REGISTER(DG_HASH_ALGO, ECONTENT_HASH_ALGO, signatureAlgorithm, n, k, MAX_ECONTENT_PADDED_LEN, MAX_SIGNED_ATTR_PADDED_LEN) {
assert(MAX_DSC_LENGTH % 64 == 0);
// This means the attestation is a passport
var attestation_id = 1;
var minKeyLength = getMinKeyLength(signatureAlgorithm);
var kLengthFactor = getKLengthFactor(signatureAlgorithm);
var kScaled = k * kLengthFactor;
var HASH_LEN_BITS = getHashLength(signatureAlgorithm);
var HASH_LEN_BYTES = HASH_LEN_BITS / 8;
var ECONTENT_HASH_ALGO_BYTES = ECONTENT_HASH_ALGO / 8;
var MAX_DSC_PUBKEY_LENGTH = n * kScaled / 8;
signal input raw_dsc[MAX_DSC_LENGTH];
signal input raw_dsc_actual_length;
signal input dsc_pubKey_offset;
signal input dsc_pubKey_actual_size;
signal input dg1[93];
signal input dg1_hash_offset;
signal input dg2_hash[64];
signal input eContent[MAX_ECONTENT_PADDED_LEN];
signal input eContent_padded_length;
signal input signed_attr[MAX_SIGNED_ATTR_PADDED_LEN];
signal input signed_attr_padded_length;
signal input signed_attr_econtent_hash_offset;
signal input pubKey[kScaled];
signal input signature[kScaled];
signal input pubKey_dsc[kScaled];
signal input signature_passport[kScaled];
signal input merkle_root;
signal input leaf_depth;
signal input path[nLevels];
signal input siblings[nLevels];
signal input csca_tree_leaf;
signal input secret;
signal input dsc_secret;
signal attestation_id <== 1;
// check dsc_pubKey_actual_size is at least the minimum key length
signal dsc_pubKey_actual_size_in_range <== GreaterEqThan(12)([
dsc_pubKey_actual_size,
minKeyLength * kLengthFactor / 8
]);
dsc_pubKey_actual_size_in_range === 1;
// check offsets refer to valid ranges
signal dsc_pubKey_offset_in_range <== LessEqThan(12)([
dsc_pubKey_offset + dsc_pubKey_actual_size,
raw_dsc_actual_length
]);
dsc_pubKey_offset_in_range === 1;
// generate DSC leaf as poseidon(dsc_hash, csca_tree_leaf)
signal dsc_hash <== PackBytesAndPoseidon(MAX_DSC_LENGTH)(raw_dsc);
signal dsc_hash_with_actual_length <== Poseidon(2)([dsc_hash, raw_dsc_actual_length]);
signal dsc_tree_leaf <== Poseidon(2)([dsc_hash_with_actual_length, csca_tree_leaf]);
signal computed_merkle_root <== BinaryMerkleRoot(nLevels)(dsc_tree_leaf, leaf_depth, path, siblings);
merkle_root === computed_merkle_root;
// get DSC public key from the certificate
signal extracted_dsc_pubKey[MAX_DSC_PUBKEY_LENGTH] <== SelectSubArray(MAX_DSC_LENGTH, MAX_DSC_PUBKEY_LENGTH)(
raw_dsc,
dsc_pubKey_offset,
dsc_pubKey_actual_size
);
// check if the DSC public key is the same as the one in the certificate
CheckPubkeysEqual(n, kScaled, kLengthFactor, MAX_DSC_PUBKEY_LENGTH)(
pubKey_dsc,
extracted_dsc_pubKey,
dsc_pubKey_actual_size
);
// verify passport signature
signal signedAttrShaBytes[HASH_LEN_BYTES] <== PassportVerifier(DG_HASH_ALGO, ECONTENT_HASH_ALGO, signatureAlgorithm, n, k, MAX_ECONTENT_PADDED_LEN, MAX_SIGNED_ATTR_PADDED_LEN)(dg1,dg1_hash_offset, dg2_hash, eContent,eContent_padded_length, signed_attr, signed_attr_padded_length, signed_attr_econtent_hash_offset, pubKey, signature);
component passportVerifier = PassportVerifier(
DG_HASH_ALGO,
ECONTENT_HASH_ALGO,
signatureAlgorithm,
n,
k,
MAX_ECONTENT_PADDED_LEN,
MAX_SIGNED_ATTR_PADDED_LEN
);
// nulifier
component passportDataHashed = CustomHasher(HASH_LEN_BYTES);
passportDataHashed.in <== signedAttrShaBytes;
signal output nullifier <== passportDataHashed.out;
passportVerifier.dg1 <== dg1;
passportVerifier.dg1_hash_offset <== dg1_hash_offset;
passportVerifier.eContent <== eContent;
passportVerifier.eContent_padded_length <== eContent_padded_length;
passportVerifier.signed_attr <== signed_attr;
passportVerifier.signed_attr_padded_length <== signed_attr_padded_length;
passportVerifier.signed_attr_econtent_hash_offset <== signed_attr_econtent_hash_offset;
passportVerifier.pubKey_dsc <== pubKey_dsc;
passportVerifier.signature_passport <== signature_passport;
// // REGISTRATION (optional)
// // generate the commitment
signal leaf <== LeafHasher(kScaled)(pubKey, signatureAlgorithm);
signal output commitment <== ComputeCommitment()(secret, attestation_id, leaf, dg1, dg2_hash);
signal output nullifier <== PackBytesAndPoseidon(HASH_LEN_BYTES)(passportVerifier.signedAttrShaBytes);
// blinded dsc commitment
signal pubkeyHash <== CustomHasher(kScaled)(pubKey);
signal output blinded_dsc_commitment <== Poseidon(2)([dsc_secret, pubkeyHash]);
}
// generate commitment
signal dg1_packed_hash <== PackBytesAndPoseidon(93)(dg1);
signal eContent_shaBytes_packed_hash <== PackBytesAndPoseidon(ECONTENT_HASH_ALGO_BYTES)(passportVerifier.eContentShaBytes);
signal output commitment <== Poseidon(5)([
secret,
attestation_id,
dg1_packed_hash,
eContent_shaBytes_packed_hash,
dsc_tree_leaf
]);
}

View File

@@ -1,5 +0,0 @@
pragma circom 2.1.6;
include "../../utils/passport/computeCommitment.circom";
component main = ComputeCommitment();

View File

@@ -0,0 +1,13 @@
pragma circom 2.1.9;
include "../../../utils/crypto/signature/ecdsa/ecdsaVerifier.circom";
template VerifyP521r1Sha512() {
signal input signature[2 * 8];
signal input pubKey[2 * 8];
signal input hashParsed[512];
EcdsaVerifier(41, 66, 8)(signature, pubKey, hashParsed);
}
component main = VerifyP521r1Sha512();

View File

@@ -1,3 +1,3 @@
pragma circom 2.1.9;
include "../../utils/passport/disclose/proveCountryIsNotInList.circom";
component main { public [ forbidden_countries_list ] } = ProveCountryIsNotInList(20);
component main { public [ forbidden_countries_list ] } = ProveCountryIsNotInList(10);

View File

@@ -0,0 +1,3 @@
include "../../utils/crypto/bitify/splitWordsToBytes.circom";
component main = WordsToBytesPadded(120,35,120 * 35 / 8, 525);

View File

@@ -1,34 +1,29 @@
// NOTE: this circuit is unaudited and should not be used in production
/// @title SplitBytesToWords
/// @notice split an array of bytes into an array of words
/// @notice useful for casting a message or modulus before RSA verification
/// @param l: number of bytes in the input array
/// @param n: number of bits in a word
/// @param k: number of words
/// @input in: array of bytes
/// @output out: array of words
template SplitBytesToWords (l,n,k) {
signal input in[l];
signal output out[k];
pragma circom 2.1.9;
component num2bits[l];
for (var i = 0 ; i < l ; i++){
num2bits[i] = Num2Bits(8);
num2bits[i].in <== in[i];
template BitsToBytesArray(bits_len){
var bytes_len = bits_len / 8;
component b2n[bytes_len];
signal input in[bits_len];
signal output out[bytes_len];
for (var i = 0; i < bytes_len; i++) {
b2n[i] = Bits2Num(8);
for (var j = 0; j < 8; j++) {
b2n[i].in[7 - j] <== in[i * 8 + j];
}
out[i] <== b2n[i].out;
}
component bits2num[k];
for (var i = 0 ; i < k ; i++){
bits2num[i] = Bits2Num(n);
for(var j = 0 ; j < n ; j++){
if(i*n + j >= 8 * l){
bits2num[i].in[j] <== 0;
}
else{
bits2num[i].in[j] <== num2bits[l - (( i * n + j) \ 8) - 1].out[ ((i * n + j) % 8)];
}
}
template BytesToBitsArray(bytes_len){
var bits_len = bytes_len * 8;
signal input in[bytes_len];
signal output out[bits_len];
component n2b[bytes_len];
for (var i = 0; i < bytes_len; i++) {
n2b[i] = Num2Bits(8);
n2b[i].in <== in[i];
for (var j = 0; j < 8; j++) {
out[i * 8 + j] <== n2b[i].out[7 - j];
}
}
for( var i = 0 ; i< k ; i++){
out[i] <== bits2num[i].out;
}
}

View File

@@ -0,0 +1,71 @@
pragma circom 2.1.9;
include "circomlib/circuits/bitify.circom";
template WordsToBytes(n_words, k_words, maxBytesLength) {
assert(n_words * k_words == maxBytesLength * 8);
signal input words[k_words];
signal output bytes[maxBytesLength];
component num2bits[k_words];
signal word_bits[k_words * n_words];
// Convert words to bits
for (var i = 0; i < k_words; i++) {
num2bits[i] = Num2Bits(n_words);
num2bits[i].in <== words[i];
}
for (var i = 0; i < k_words; i++) {
for (var j = 0; j < n_words; j++) {
word_bits[i * n_words + j] <== num2bits[i].out[j];
}
}
// Convert bits back to bytes
component bits2Num[maxBytesLength];
for (var i = 0; i < maxBytesLength; i++) {
bits2Num[i] = Bits2Num(8);
for (var j = 0; j < 8; j++) {
bits2Num[i].in[j] <== word_bits[i * 8 + j];
}
bytes[i] <== bits2Num[i].out;
}
}
template WordsToBytesPadded(n_words, k_words, maxBytesLength, paddedLength) {
assert(n_words * k_words == maxBytesLength * 8);
signal input words[k_words];
signal bytes[maxBytesLength];
signal output paddedBytes[paddedLength];
component num2bits[k_words];
signal word_bits[k_words * n_words];
// Convert words to bits
for (var i = 0; i < k_words; i++) {
num2bits[i] = Num2Bits(n_words);
num2bits[i].in <== words[i];
}
for (var i = 0; i < k_words; i++) {
for (var j = 0; j < n_words; j++) {
word_bits[i * n_words + j] <== num2bits[i].out[j];
}
}
// Convert bits back to bytes
component bits2Num[maxBytesLength];
for (var i = 0; i < maxBytesLength; i++) {
bits2Num[i] = Bits2Num(8);
for (var j = 0; j < 8; j++) {
bits2Num[i].in[j] <== word_bits[i * 8 + j];
}
bytes[i] <== bits2Num[i].out;
}
for (var i = 0; i < paddedLength; i++) {
paddedBytes[i] <== bytes[maxBytesLength - i - 1];
}
}

View File

@@ -9,29 +9,30 @@ include "./dynamic/sha512Bytes.circom";
/// @title ShaBytesDynamic
/// @notice Computes the hash of an input message using a specified hash length and padded input
/// @param hashLen Desired length of the hash in bits (e.g., 512, 384, 256, 224, 160)
/// @param max_num_bits Maximum number of bits in the padded input
/// @param max_num_bytes Maximum number of bytes in the padded input
/// @input in_padded Padded input message, represented as an array of bits
/// @input in_len_padded_bytes Length of the padded input in bytes
/// @output hash The computed hash of the input message, with length specified by `hashLen`
template ShaBytesDynamic(hashLen, max_num_bits) {
signal input in_padded[max_num_bits];
template ShaBytesDynamic(hashLen, max_num_bytes) {
signal input in_padded[max_num_bytes];
signal input in_len_padded_bytes;
signal output hash[hashLen];
signal output hash_bits[hashLen];
if (hashLen == 512) {
hash <== Sha512Bytes(max_num_bits)(in_padded, in_len_padded_bytes);
hash_bits <== Sha512Bytes(max_num_bytes)(in_padded, in_len_padded_bytes);
}
if (hashLen == 384) {
hash <== Sha384Bytes(max_num_bits)(in_padded, in_len_padded_bytes);
hash_bits <== Sha384Bytes(max_num_bytes)(in_padded, in_len_padded_bytes);
}
if (hashLen == 256) {
hash <== Sha256Bytes(max_num_bits)(in_padded, in_len_padded_bytes);
hash_bits <== Sha256Bytes(max_num_bytes)(in_padded, in_len_padded_bytes);
}
if (hashLen == 224) {
hash <== Sha224Bytes(max_num_bits)(in_padded, in_len_padded_bytes);
hash_bits <== Sha224Bytes(max_num_bytes)(in_padded, in_len_padded_bytes);
}
if (hashLen == 160) {
hash <== Sha1Bytes(max_num_bits)(in_padded, in_len_padded_bytes);
hash_bits <== Sha1Bytes(max_num_bytes)(in_padded, in_len_padded_bytes);
}
}

View File

@@ -1,25 +0,0 @@
pragma circom 2.1.9;
include "./static/Sha256BytesStatic.circom";
include "./static/Sha1BytesStatic.circom";
include "./static/Sha384BytesStatic.circom";
include "./static/Sha512BytesStatic.circom";
template ShaBytesStatic(hashLen, dataLen) {
signal input data[dataLen];
signal output hash[hashLen];
if (hashLen == 512) {
hash <== Sha512BytesStatic(dataLen)(data);
}
if (hashLen == 384) {
hash <== Sha384BytesStatic(dataLen)(data);
}
if (hashLen == 256) {
hash <== Sha256BytesStatic(dataLen)(data);
}
if (hashLen == 160) {
hash <== Sha1BytesStatic(dataLen)(data);
}
}

View File

@@ -1,28 +0,0 @@
pragma circom 2.1.9;
include "circomlib/circuits/bitify.circom";
include "../../sha2/sha384/sha384_hash_bits.circom";
template Sha384BytesStatic(max_num_bytes) {
signal input in_padded[max_num_bytes];
signal output out[384];
var num_bits = max_num_bytes * 8;
component sha = Sha384HashBitsStatic(num_bits);
component bytes[max_num_bytes];
for (var i = 0; i < max_num_bytes; i++) {
bytes[i] = Num2Bits(8);
bytes[i].in <== in_padded[i];
for (var j = 0; j < 8; j++) {
sha.in[i*8+j] <== bytes[i].out[7-j];
}
}
// Connect output bits
for (var i = 0; i < 384; i++) {
out[i] <== sha.out[i];
}
}

View File

@@ -1,27 +0,0 @@
pragma circom 2.1.9;
include "circomlib/circuits/bitify.circom";
include "../../sha2/sha512/sha512_hash_bits.circom";
template Sha512BytesStatic(max_num_bytes) {
signal input in_padded[max_num_bytes];
signal output out[512];
var num_bits = max_num_bytes * 8;
component sha = Sha512HashBitsStatic(num_bits);
component bytes[max_num_bytes];
for (var i = 0; i < max_num_bytes; i++) {
bytes[i] = Num2Bits(8);
bytes[i].in <== in_padded[i];
for (var j = 0; j < 8; j++) {
sha.in[i*8+j] <== bytes[i].out[7-j];
}
}
for (var i = 0; i < 512; i++) {
out[i] <== sha.out[i];
}
}

View File

@@ -66,7 +66,7 @@ template EcdsaVerifier(signatureAlgorithm, n, k) {
}
function get_a(signatureAlgorithm) {
if (signatureAlgorithm == 7 || signatureAlgorithm == 8) {
if (signatureAlgorithm == 7 || signatureAlgorithm == 8) { //secp256r1
return [
18446744073709551612,
4294967295,
@@ -74,7 +74,7 @@ function get_a(signatureAlgorithm) {
18446744069414584321
];
}
if (signatureAlgorithm == 9 || signatureAlgorithm == 23) {
if (signatureAlgorithm == 9 || signatureAlgorithm == 23) { //secp384r1
return [
4294967292,
18446744069414584320,
@@ -84,7 +84,7 @@ function get_a(signatureAlgorithm) {
18446744073709551615
];
}
if (signatureAlgorithm == 21 || signatureAlgorithm == 24 || signatureAlgorithm == 25) {
if (signatureAlgorithm == 21 || signatureAlgorithm == 24 || signatureAlgorithm == 25 || signatureAlgorithm == 36) { //brainpoolP256r1
return [
16810331318623712729,
18122579188607900780,
@@ -92,7 +92,7 @@ function get_a(signatureAlgorithm) {
9032542404991529047
];
}
if (signatureAlgorithm == 22 || signatureAlgorithm == 26) {
if (signatureAlgorithm == 22 || signatureAlgorithm == 26 || signatureAlgorithm == 37) { // brainpoolP384r1
return [
335737924824737830,
9990533504564909291,
@@ -103,7 +103,7 @@ function get_a(signatureAlgorithm) {
];
}
if (signatureAlgorithm == 27 || signatureAlgorithm == 28 || signatureAlgorithm == 30) {
if (signatureAlgorithm == 27 || signatureAlgorithm == 28 || signatureAlgorithm == 30) { // brainpoolP224r1
return [
3402800963,
2953063001,
@@ -115,7 +115,7 @@ function get_a(signatureAlgorithm) {
];
}
if (signatureAlgorithm == 29) {
if (signatureAlgorithm == 29 || signatureAlgorithm == 38) { // brainpoolP512r1
return [
16699818341992010954,
9156125524185237433,
@@ -128,11 +128,36 @@ function get_a(signatureAlgorithm) {
];
}
if (signatureAlgorithm == 41) { //p521
return [
73786976294838206460,
73786976294838206463,
73786976294838206463,
73786976294838206463,
73786976294838206463,
73786976294838206463,
73786976294838206463,
576460752303423487
];
}
if (signatureAlgorithm == 44) { //p224
return [
4294967294,
4294967295,
4294967295,
4294967294,
4294967295,
4294967295,
4294967295
];
}
return [0];
}
function get_b(signatureAlgorithm) {
if (signatureAlgorithm == 7 || signatureAlgorithm == 8) {
if (signatureAlgorithm == 7 || signatureAlgorithm == 8) { //secp256r1
return [
4309448131093880907,
7285987128567378166,
@@ -150,7 +175,7 @@ function get_b(signatureAlgorithm) {
12912154004749740004
];
}
if (signatureAlgorithm == 21 || signatureAlgorithm == 24 || signatureAlgorithm == 25) {
if (signatureAlgorithm == 21 || signatureAlgorithm == 24 || signatureAlgorithm == 25 || signatureAlgorithm == 36) { //brainpoolP256r1
return [
7767825457231955894,
10773760575486288334,
@@ -158,7 +183,7 @@ function get_b(signatureAlgorithm) {
2800214691157789508
];
}
if (signatureAlgorithm == 22 || signatureAlgorithm == 26) {
if (signatureAlgorithm == 22 || signatureAlgorithm == 26 || signatureAlgorithm == 37) { //brainpoolP384r1
return [
4230998357940653073,
8985869839777909140,
@@ -169,7 +194,7 @@ function get_b(signatureAlgorithm) {
];
}
if (signatureAlgorithm == 27 || signatureAlgorithm == 28 || signatureAlgorithm == 30) {
if (signatureAlgorithm == 27 || signatureAlgorithm == 28 || signatureAlgorithm == 30) { // brainpoolP224r1
return [
946618379,
1725674354,
@@ -181,7 +206,7 @@ function get_b(signatureAlgorithm) {
];
}
if (signatureAlgorithm == 29) {
if (signatureAlgorithm == 29 || signatureAlgorithm == 38) { // brainpoolP512r1
return [
2885045271355914019,
10970857440773072349,
@@ -194,11 +219,36 @@ function get_b(signatureAlgorithm) {
];
}
if (signatureAlgorithm == 41) { //p521
return [
35687965819361312512,
33244719099633405244,
68122903767798193136,
64948772962036742733,
36008729323586384137,
4298886627987975365,
30118149759215298644,
91854278977009778
];
}
if (signatureAlgorithm == 44) { //p224
return [
592838580,
655046979,
3619674298,
1346678967,
4114690646,
201634731,
3020229253
];
}
return [0];
}
function get_p(signatureAlgorithm) {
if (signatureAlgorithm == 7 || signatureAlgorithm == 8) {
if (signatureAlgorithm == 7 || signatureAlgorithm == 8) { //secp256r1
return [
18446744073709551615,
4294967295,
@@ -206,7 +256,7 @@ function get_p(signatureAlgorithm) {
18446744069414584321
];
}
if (signatureAlgorithm == 9 || signatureAlgorithm == 23) {
if (signatureAlgorithm == 9 || signatureAlgorithm == 23) { //secp384r1
return [
4294967295,
18446744069414584320,
@@ -216,7 +266,7 @@ function get_p(signatureAlgorithm) {
18446744073709551615
];
}
if (signatureAlgorithm == 21 || signatureAlgorithm == 24 || signatureAlgorithm == 25) {
if (signatureAlgorithm == 21 || signatureAlgorithm == 24 || signatureAlgorithm == 25 || signatureAlgorithm == 36) { //brainpoolP256r1
return [
2311270323689771895,
7943213001558335528,
@@ -224,7 +274,7 @@ function get_p(signatureAlgorithm) {
12248480212390422972
];
}
if (signatureAlgorithm == 22 || signatureAlgorithm == 26) {
if (signatureAlgorithm == 22 || signatureAlgorithm == 26 || signatureAlgorithm == 37) { //brainpoolP384r1
return [
9747760000893709395,
12453481191562877553,
@@ -235,7 +285,7 @@ function get_p(signatureAlgorithm) {
];
}
if (signatureAlgorithm == 27 || signatureAlgorithm == 28 || signatureAlgorithm == 30) {
if (signatureAlgorithm == 27 || signatureAlgorithm == 28 || signatureAlgorithm == 30) { //brainpoolP224r1
return [
2127085823,
2547681781,
@@ -247,7 +297,7 @@ function get_p(signatureAlgorithm) {
];
}
if (signatureAlgorithm == 29) {
if (signatureAlgorithm == 29 || signatureAlgorithm == 38) { //brainpoolP512r1
return [
2930260431521597683,
2918894611604883077,
@@ -260,5 +310,30 @@ function get_p(signatureAlgorithm) {
];
}
if (signatureAlgorithm == 41) { //p521
return [
73786976294838206463,
73786976294838206463,
73786976294838206463,
73786976294838206463,
73786976294838206463,
73786976294838206463,
73786976294838206463,
576460752303423487
];
}
if (signatureAlgorithm == 44) { // p224
return [
1,
0,
0,
4294967295,
4294967295,
4294967295,
4294967295
];
}
return [0];
}

View File

@@ -157,6 +157,21 @@ template VerifyRsaPss3Sig(CHUNK_SIZE, CHUNK_NUMBER, SALT_LEN, HASH_TYPE, KEY_LEN
db[i] <== xor.out[i];
}
}
//Step -10 of https://datatracker.ietf.org/doc/html/rfc8017#section-9.1.2
component db2Num[DB_MASK_LEN];
for (var i = 0; i < DB_MASK_LEN; i++) {
db2Num[i] = Bits2Num(8);
for (var j = 0; j < 8; j++) {
db2Num[i].in[7 - j] <== db[i*8 + j];
}
}
// Check leading zeros
for (var i = 0; i < DB_MASK_LEN - SALT_LEN - 1; i++) {
db2Num[i].out === 0;
}
// Check 0x01 byte
db2Num[DB_MASK_LEN - SALT_LEN - 1].out === 1;
//inserting salt
for (var i = 0; i < SALT_LEN_BITS; i++) {

View File

@@ -57,7 +57,7 @@ include "../FpPowMod.circom";
/// @input signature The RSA signature split into chunks
/// @input hashed The hash of the original message
template VerifyRsaPss65537Sig(CHUNK_SIZE, CHUNK_NUMBER, SALT_LEN, HASH_TYPE, KEY_LENGTH) {
assert((HASH_TYPE == 384 && SALT_LEN == 48) || (HASH_TYPE == 256 && SALT_LEN == 64) || (HASH_TYPE == 256 && SALT_LEN == 32));
assert((HASH_TYPE == 384 && SALT_LEN == 48) || (HASH_TYPE == 256 && SALT_LEN == 64) || (HASH_TYPE == 256 && SALT_LEN == 32) || (HASH_TYPE == 512 && SALT_LEN == 64));
signal input pubkey[CHUNK_NUMBER];
signal input signature[CHUNK_NUMBER];
@@ -114,7 +114,7 @@ template VerifyRsaPss65537Sig(CHUNK_SIZE, CHUNK_NUMBER, SALT_LEN, HASH_TYPE, KEY
assert(EM_LEN >= HASH_LEN + SALT_LEN + 2);
//should end with 0xBC (188 in decimal)
assert(eM[0] == 188);
eM[0] === 188;
var DB_MASK_LEN = EM_LEN - HASH_LEN - 1;
@@ -159,6 +159,21 @@ template VerifyRsaPss65537Sig(CHUNK_SIZE, CHUNK_NUMBER, SALT_LEN, HASH_TYPE, KEY
db[i] <== xor.out[i];
}
}
//Step -10 of https://datatracker.ietf.org/doc/html/rfc8017#section-9.1.2
component db2Num[DB_MASK_LEN];
for (var i = 0; i < DB_MASK_LEN; i++) {
db2Num[i] = Bits2Num(8);
for (var j = 0; j < 8; j++) {
db2Num[i].in[7 - j] <== db[i*8 + j];
}
}
// Check leading zeros
for (var i = 0; i < DB_MASK_LEN - SALT_LEN - 1; i++) {
db2Num[i].out === 0;
}
// Check 0x01 byte
db2Num[DB_MASK_LEN - SALT_LEN - 1].out === 1;
//inserting salt
for (var i = 0; i < SALT_LEN_BITS; i++) {

View File

@@ -0,0 +1,35 @@
pragma circom 2.1.9;
include "circomlib/circuits/bitify.circom";
// Converts 4 bytes into a single number
// Each byte must be in range [0,255]
template BytesToNum() {
signal input bytes[4]; // Assuming MSB first ordering
signal output out;
// First convert each byte to bits
component byte0 = Num2Bits(8);
component byte1 = Num2Bits(8);
component byte2 = Num2Bits(8);
component byte3 = Num2Bits(8);
byte0.in <== bytes[0];
byte1.in <== bytes[1];
byte2.in <== bytes[2];
byte3.in <== bytes[3];
// Now combine all bits into a single number
component bits2Num = Bits2Num(32);
// Connect in LSB to MSB order (reverse byte order from before)
for (var i = 0; i < 8; i++) {
bits2Num.in[i] <== byte3.out[i]; // Least significant byte
bits2Num.in[8+i] <== byte2.out[i]; // Third byte
bits2Num.in[16+i] <== byte1.out[i]; // Second byte
bits2Num.in[24+i] <== byte0.out[i]; // Most significant byte
}
// The final output
out <== bits2Num.out;
}

View File

@@ -0,0 +1,55 @@
pragma circom 2.1.9;
include "../crypto/bitify/bytes.circom";
include "../crypto/bitify/splitWordsToBytes.circom";
/// @title CheckPubkeysEqual
/// @notice Checks if the CSCA public key given is the same as the one in the certificate
/// @param kScaled Number of chunks the given key is split into.
/// @param MAX_CSCA_PUBKEY_LENGTH Maximum length of the parsed CSCA public key
/// @input csca_pubKey CSCA public key given by the user, formatted for signature verification
/// @input extracted_csca_pubKey CSCA public key extracted from the certificate
template CheckPubkeysEqual(n, kScaled, kLengthFactor, MAX_CSCA_PUBKEY_LENGTH) {
signal input csca_pubKey[kScaled];
signal input extracted_csca_pubKey[MAX_CSCA_PUBKEY_LENGTH];
signal input csca_pubKey_actual_size;
signal csca_pubKey_bytes[MAX_CSCA_PUBKEY_LENGTH] <== WordsToBytes(n, kScaled, n * kScaled / 8)(csca_pubKey);
// reverse bytes order
signal reversed_csca_pubKey_bytes[MAX_CSCA_PUBKEY_LENGTH];
for (var i = 0; i < MAX_CSCA_PUBKEY_LENGTH; i++) {
reversed_csca_pubKey_bytes[i] <== csca_pubKey_bytes[MAX_CSCA_PUBKEY_LENGTH - i - 1];
}
signal shifted_csca_pubKey_bytes[MAX_CSCA_PUBKEY_LENGTH] <== VarShiftLeft(MAX_CSCA_PUBKEY_LENGTH, MAX_CSCA_PUBKEY_LENGTH)(
reversed_csca_pubKey_bytes,
MAX_CSCA_PUBKEY_LENGTH - csca_pubKey_actual_size
);
// if kLengthFactor = 1 it's rsa, if kLengthFactor = 2 it's ecdsa
// if it's ecdsa, the position of the x and y coordinates are swapped
// in ecdsa, MAX_CSCA_PUBKEY_LENGTH is always the size of the key
if (kLengthFactor == 2) {
for (var i = 0; i < MAX_CSCA_PUBKEY_LENGTH / 2; i++) {
shifted_csca_pubKey_bytes[i] === extracted_csca_pubKey[MAX_CSCA_PUBKEY_LENGTH / 2 + i];
}
for (var i = 0; i < MAX_CSCA_PUBKEY_LENGTH / 2; i++) {
shifted_csca_pubKey_bytes[MAX_CSCA_PUBKEY_LENGTH / 2 + i] === extracted_csca_pubKey[i];
}
// if it's rsa, we just check if the keys are the same
// in rsa, csca_pubKey_actual_size is the size of the key in bytes
} else {
component lessThans[MAX_CSCA_PUBKEY_LENGTH];
for (var i = 0; i < MAX_CSCA_PUBKEY_LENGTH; i++) {
lessThans[i] = LessThan(14);
lessThans[i].in[0] <== i;
lessThans[i].in[1] <== csca_pubKey_actual_size;
// If i < csca_pubKey_actual_size => must match
// If i >= csca_pubKey_actual_size => no constraint
(shifted_csca_pubKey_bytes[i] - extracted_csca_pubKey[i]) * lessThans[i].out === 0;
}
}
}

View File

@@ -1,31 +0,0 @@
pragma circom 2.1.9;
include "circomlib/circuits/poseidon.circom";
include "@openpassport/zk-email-circuits/utils/bytes.circom";
include "./customHashers.circom";
template ComputeCommitment() {
signal input secret;
signal input attestation_id;
signal input leaf;
signal input dg1[93];
signal input dg2_hash[64];
signal output out;
component poseidon_hasher = Poseidon(7);
poseidon_hasher.inputs[0] <== secret;
poseidon_hasher.inputs[1] <== attestation_id;
poseidon_hasher.inputs[2] <== leaf;
signal dg1_packed[3] <== PackBytes(93)(dg1);
for (var i = 0; i < 3; i++) {
poseidon_hasher.inputs[i + 3] <== dg1_packed[i];
}
signal dg2Hash2 <== CustomHasher(64)(dg2_hash);
poseidon_hasher.inputs[6] <== dg2Hash2;
out <== poseidon_hasher.out;
}

View File

@@ -0,0 +1,26 @@
pragma circom 2.1.9;
/// @title Constants
/// @notice Contains constants for the passport circuit
/// @notice Maximum length of DSC certificate — currently 1792 bytes
/// @dev Empirically, we saw DSCs up to 1591 bytes.
function getMaxDSCLength(){
return 1792;
}
/// @notice Maximum length of CSCA certificate — currently 1792 bytes.
/// @dev Empirically, we saw CSCAs up to 1671 bytes in the master list.
function getMaxCSCALength(){
return 1792;
}
/// @notice Maximum number of levels in the CSCA Merkle tree — currently 12
function getMaxCSCALevels(){
return 12;
}
/// @notice Maximum number of levels in the DSC Merkle tree — currently 12
function getMaxDSCLevels(){
return 21;
}

View File

@@ -1,41 +1,60 @@
pragma circom 2.1.9;
include "../crypto/bigInt/bigIntFunc.circom";
include "circomlib/circuits/poseidon.circom";
include "@openpassport/zk-email-circuits/utils/bytes.circom";
/// @notice CutomHasher circuit - used to Poseidon up to 256 signals
/// @param k Number of signals to hash
/// @input in Input signals
/// @output out Output hash
template CustomHasher(k) {
signal input in[k];
var rounds = div_ceil(k, 16);
assert(rounds < 17);
component hash[rounds];
for (var i = 0; i < rounds ; i ++){
hash[i] = Poseidon(16);
signal output out;
if (k < 16){ // if k is less than 16, we can use a single poseidon hash
component hash = Poseidon(k);
for (var i = 0; i < k; i++){
hash.inputs[i] <== in[i];
}
out <== hash.out;
}
for (var i = 0; i < rounds ; i ++){
for (var j = 0; j < 16 ; j ++){
if (i * 16 + j < k){
hash[i].inputs[j] <== in[i * 16 + j];
} else {
hash[i].inputs[j] <== 0;
else{
// do up to 16 rounds of poseidon
var rounds = div_ceil(k, 16);
assert(rounds < 17);
component hash[rounds];
for (var i = 0; i < rounds ; i ++){
hash[i] = Poseidon(16);
}
for (var i = 0; i < rounds ; i ++){
for (var j = 0; j < 16 ; j ++){
if (i * 16 + j < k){
hash[i].inputs[j] <== in[i * 16 + j];
} else {
hash[i].inputs[j] <== 0;
}
}
}
}
component finalHash = Poseidon(rounds);
for (var i = 0 ; i < rounds ; i++) {
finalHash.inputs[i] <== hash[i].out;
component finalHash = Poseidon(rounds);
for (var i = 0 ; i < rounds ; i++) {
finalHash.inputs[i] <== hash[i].out;
}
out <== finalHash.out;
}
signal output out <== finalHash.out;
}
template LeafHasher(k) {
/// @notice PackBytesAndPoseidon circuit — used to pack a byte array and hash it
/// @param k Size of the array to pack
/// @param in Input array
/// @param out Output hash
template PackBytesAndPoseidon(k) {
var packed_length = computeIntChunkLength(k);
signal input in[k];
signal input sigAlg;
component leafHasher = CustomHasher(k+1);
leafHasher.in[0] <== sigAlg;
for (var i = 0; i < k; i++){
leafHasher.in[i+1] <== in[i];
}
signal output out <== leafHasher.out;
signal packed[packed_length] <== PackBytes(k)(in);
signal output out <== CustomHasher(packed_length)(packed);
}

Some files were not shown because too many files have changed in this diff Show More