merge origin main to fork UI

This commit is contained in:
Rémi Colin
2024-02-01 18:28:58 +01:00
166 changed files with 31852 additions and 8812 deletions

3
.idea/.gitignore generated vendored
View File

@@ -1,3 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml

5
.idea/misc.xml generated
View File

@@ -1,5 +0,0 @@
<project version="4">
<component name="ProjectRootManager">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

8
.idea/modules.xml generated
View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/mygit.iml" filepath="$PROJECT_DIR$/.idea/mygit.iml" />
</modules>
</component>
</project>

9
.idea/mygit.iml generated
View File

@@ -1,9 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

7
.idea/vcs.xml generated
View File

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
<mapping directory="$PROJECT_DIR$/app/android/android-passport-reader" vcs="Git" />
</component>
</project>

64
.idea/workspace.xml generated Normal file
View File

@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AutoImportSettings">
<option name="autoReloadType" value="NONE" />
</component>
<component name="ChangeListManager">
<list default="true" id="2da04ae5-ea98-46a0-b43b-66209d86487b" name="Changes" comment="">
<change beforePath="$PROJECT_DIR$/app/android/android-passport-reader/.idea/compiler.xml" beforeDir="false" afterPath="$PROJECT_DIR$/app/android/android-passport-reader/.idea/compiler.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/android/android-passport-reader/.idea/gradle.xml" beforeDir="false" afterPath="$PROJECT_DIR$/app/android/android-passport-reader/.idea/gradle.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/android/android-passport-reader/.idea/misc.xml" beforeDir="false" afterPath="$PROJECT_DIR$/app/android/android-passport-reader/.idea/misc.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/android/android-passport-reader/app/build.gradle" beforeDir="false" afterPath="$PROJECT_DIR$/app/android/android-passport-reader/app/build.gradle" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/android/android-passport-reader/app/src/main/AndroidManifest.xml" beforeDir="false" afterPath="$PROJECT_DIR$/app/android/android-passport-reader/app/src/main/AndroidManifest.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/android/android-passport-reader/build.gradle" beforeDir="false" afterPath="$PROJECT_DIR$/app/android/android-passport-reader/build.gradle" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/android/android-passport-reader/gradle.properties" beforeDir="false" afterPath="$PROJECT_DIR$/app/android/android-passport-reader/gradle.properties" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/android/android-passport-reader/gradle/wrapper/gradle-wrapper.properties" beforeDir="false" afterPath="$PROJECT_DIR$/app/android/android-passport-reader/gradle/wrapper/gradle-wrapper.properties" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/android/app/build.gradle" beforeDir="false" afterPath="$PROJECT_DIR$/app/android/app/build.gradle" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/android/app/src/main/AndroidManifest.xml" beforeDir="false" afterPath="$PROJECT_DIR$/app/android/app/src/main/AndroidManifest.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/android/app/src/main/java/com/awesomeproject/MainApplication.java" beforeDir="false" afterPath="$PROJECT_DIR$/app/android/app/src/main/java/com/awesomeproject/MainApplication.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/android/build.gradle" beforeDir="false" afterPath="$PROJECT_DIR$/app/android/build.gradle" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/android/settings.gradle" beforeDir="false" afterPath="$PROJECT_DIR$/app/android/settings.gradle" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/src/screens/EnterDetailsScreen.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/app/src/screens/EnterDetailsScreen.tsx" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="ClangdSettings">
<option name="formatViaClangd" value="false" />
</component>
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="MarkdownSettingsMigration">
<option name="stateVersion" value="1" />
</component>
<component name="ProjectId" id="2al63XibxiBXBmZ7AsW6D3mWJ0W" />
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent">{
&quot;keyToString&quot;: {
&quot;RunOnceActivity.OpenProjectViewOnStart&quot;: &quot;true&quot;,
&quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
&quot;RunOnceActivity.cidr.known.project.marker&quot;: &quot;true&quot;,
&quot;cf.first.check.clang-format&quot;: &quot;false&quot;,
&quot;cidr.known.project.marker&quot;: &quot;true&quot;,
&quot;git-widget-placeholder&quot;: &quot;master&quot;,
&quot;last_opened_file_path&quot;: &quot;/Users/remicolin/Documents/Proof of passport/mygit&quot;
}
}</component>
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="2da04ae5-ea98-46a0-b43b-66209d86487b" name="Changes" comment="" />
<created>1704880619858</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1704880619858</updated>
</task>
<servers />
</component>
</project>

View File

@@ -14,21 +14,28 @@ As a first application, users who can prove they indeed hold a valid passport ca
- `app`: Mobile app
- `circuits`: Circom circuits
- `contracts`: Solidity contracts
- `common`: Common utils
## Roadmap
- ✅ Basic passport verifier circuit
- 🚧 Optimization
- 🚧 Selective disclosure
- ✅ Selective disclosure
- ✅ Basic react native frontend
- ✅ Passport verification pipeline, android
- 🚧 Passport verification pipeline, iOS
- Passport verification pipeline, iOS
- ✅ Contracts
- ✅ On-chain registry of CSCA pubkeys based on the official ICAO masterlist
- 🚧 Optimizations
- 🚧 Reimplementation of the passport NFC specs in javascript
- 🚧 Contracts
- 🚧 On-chain registry of CSCA pubkeys based on the official ICAO masterlist
## FAQ
#### Does my passport support Proof of Passport ?
![EPassport_logo_grey](https://github.com/zk-passport/proof-of-passport/assets/62038140/c3263600-19b6-45f9-9b5f-63f352992c88)
If it has this symbol on the front cover, yes.
#### What exactly is being signed ?
The circuit looks like this:
@@ -91,6 +98,7 @@ The SBT circuit includes a commitment to your address. If someone else tries to
- Build a social network/anonymous message board for people from one specific country
- Create a sybil-resistance tool to protect social networks against spambots
- Do an airdrop farming protection tool
- Allow DeFi protocols to check if the nationality of a user is included in a set of forbidden states
- Gate an adult content website to a specific age
- Passport Wallet: use [active authentication](https://en.wikipedia.org/wiki/Biometric_passport#:~:text=Active%20Authentication%20(AA),Using%20AA%20is%20optional.) to build a wallet, a multisig or a recovery module using passport signatures
@@ -106,7 +114,4 @@ We are actively looking for contributors. Please check the [open issues](https:/
Contact me @FlorentTavernier on telegram for any feedback.
Thanks to [Youssef](https://github.com/yssf-io), [Aayush](https://twitter.com/yush_g), [Andy](https://twitter.com/viv_boop), [Vivek](https://twitter.com/viv_boop), [Marcus](https://github.com/base0010) and [Andrew](https://github.com/AndrewCLu) for contributing ideas and helping build this technology, and to [EF PSE](https://pse.dev/) for supporting this work through grants!
//check
Thanks to [Youssef](https://github.com/yssf-io), [Aayush](https://twitter.com/yush_g), [Andy](https://twitter.com/AndyGuzmanEth), [Vivek](https://twitter.com/viv_boop), [Marcus](https://github.com/base0010) and [Andrew](https://github.com/AndrewCLu) for contributing ideas and helping build this technology, and to [EF PSE](https://pse.dev/) for supporting this work through grants!

View File

@@ -8,6 +8,7 @@ import {
NativeModules,
DeviceEventEmitter,
TextInput,
Platform,
} from 'react-native';
import {
@@ -54,7 +55,10 @@ import {
dataHashesObjToArray,
formatAndConcatenateDataHashes,
formatMrz,
splitToWords
splitToWords,
hexStringToSignedIntArray,
formatProofIOS,
formatInputsIOS
} from '../common/src/utils/utils';
import { samplePassportData } from '../common/src/utils/passportDataStatic';
@@ -64,9 +68,15 @@ import axios from 'axios';
import groth16ExportSolidityCallData from './utils/snarkjs';
import contractAddresses from "./deployments/addresses.json"
import proofOfPassportArtefact from "./deployments/ProofOfPassport.json";
import EnterDetailsScreen from './src/screens/EnterDetailsScreen';
import MainScreen from './src/screens/MainScreen';
import { extractMRZInfo , Steps} from './src/utils/utils';
import forge from 'node-forge';
import { Buffer } from 'buffer';
global.Buffer = Buffer;
import CustomTextInput from './src/components/CustomTextInput';
import EnterDetailsScreen from './src/screens/EnterDetailsScreen';
console.log('DEFAULT_PNUMBER', DEFAULT_PNUMBER);
const SKIP_SCAN = false;
@@ -115,8 +125,15 @@ function App(): JSX.Element {
});
const startCameraScan = () => {
if (Platform.OS !== 'android') {
Toast.show({
type: 'info',
text1: "Camera scan supported soon on iOS",
})
return
}
NativeModules.CameraActivityModule.startCameraActivity()
.then((mrzInfo) => {
.then((mrzInfo : string) => {
try {
const { documentNumber, birthDate, expiryDate } = extractMRZInfo(mrzInfo);
setPassportNumber(documentNumber);
@@ -127,7 +144,7 @@ function App(): JSX.Element {
console.error('Invalid MRZ format:', error.message);
}
})
.catch((error) => {
.catch((error: any) => {
console.error('Camera Activity Error:', error);
});
};
@@ -164,14 +181,77 @@ function App(): JSX.Element {
}, []);
useEffect(() => {
if (Platform.OS !== 'android') {
NativeModules.Prover.runInitAction() // for mopro, ios only rn
}
if (SKIP_SCAN && passportData === null) {
setPassportData(samplePassportData as PassportData);
setStep(Steps.NFC_SCAN_COMPLETED);
}
}, []);
async function handleResponse(response: any) {
async function handleResponseIOS(response: any) {
const parsed = JSON.parse(response);
const eContentBase64 = parsed.eContentBase64; // this is what we call concatenatedDataHashes in our world
const signedAttributes = parsed.signedAttributes; // this is what we call eContent in our world
const signatureAlgorithm = parsed.signatureAlgorithm;
const mrz = parsed.passportMRZ;
const dataGroupHashes = parsed.dataGroupHashes;
const signatureBase64 = parsed.signatureBase64;
console.log('parsed.documentSigningCertificate', parsed.documentSigningCertificate)
const pem = JSON.parse(parsed.documentSigningCertificate).PEM.replace(/\\\\n/g, '\n')
console.log('pem', pem)
const cert = forge.pki.certificateFromPem(pem);
const publicKey = cert.publicKey;
console.log('publicKey', publicKey)
const modulus = (publicKey as any).n.toString(10);
const eContentArray = Array.from(Buffer.from(signedAttributes, 'base64'));
const signedEContentArray = eContentArray.map(byte => byte > 127 ? byte - 256 : byte);
const concatenatedDataHashesArray = Array.from(Buffer.from(eContentBase64, 'base64'));
const concatenatedDataHashesArraySigned = concatenatedDataHashesArray.map(byte => byte > 127 ? byte - 256 : byte);
const dgHashes = JSON.parse(dataGroupHashes);
console.log('dgHashes', dgHashes)
const dataGroupHashesArray = Object.keys(dgHashes)
.map(key => {
const dgNumber = parseInt(key.replace('DG', ''));
const hashArray = hexStringToSignedIntArray(dgHashes[key].computedHash);
return [dgNumber, hashArray];
})
.sort((a, b) => (a[0] as number) - (b[0] as number));
const encryptedDigestArray = Array.from(Buffer.from(signatureBase64, 'base64')).map(byte => byte > 127 ? byte - 256 : byte);
const passportData = {
mrz,
signatureAlgorithm,
pubKey: {
modulus: modulus,
},
dataGroupHashes: concatenatedDataHashesArraySigned,
eContent: signedEContentArray,
encryptedDigest: encryptedDigestArray,
};
console.log('mrz', passportData.mrz);
console.log('signatureAlgorithm', passportData.signatureAlgorithm);
console.log('pubKey', passportData.pubKey);
console.log('dataGroupHashes', passportData.dataGroupHashes);
console.log('eContent', passportData.eContent);
console.log('encryptedDigest', passportData.encryptedDigest);
setPassportData(passportData);
setStep('scanCompleted');
}
async function handleResponseAndroid(response: any) {
const {
mrz,
signatureAlgorithm,
@@ -216,11 +296,18 @@ function App(): JSX.Element {
})
return
}
// 1. start a scan
// 2. press the back of your android phone against the passport
// 3. wait for the scan(...) Promise to get resolved/rejected
console.log('scanning...');
setStep(Steps.NFC_SCANNING);
if (Platform.OS === 'android') {
scanAndroid();
} else {
scanIOS();
}
}
async function scanAndroid() {
try {
const response = await PassportReader.scan({
documentNumber: passportNumber,
@@ -229,7 +316,26 @@ function App(): JSX.Element {
});
console.log('response', response);
console.log('scanned');
handleResponse(response);
handleResponseAndroid(response);
} catch (e: any) {
console.log('error during scan :', e);
Toast.show({
type: 'error',
text1: e.message,
})
}
}
async function scanIOS() {
try {
const response = await NativeModules.PassportReader.scanPassport(
passportNumber,
dateOfBirth,
dateOfExpiry
);
console.log('response', response);
console.log('scanned');
handleResponseIOS(response);
} catch (e: any) {
console.log('error during scan :', e);
Toast.show({
@@ -254,10 +360,14 @@ function App(): JSX.Element {
// 2. Format all the data as inputs for the circuit
const formattedMrz = formatMrz(passportData.mrz);
const mrzHash = hash(formatMrz(passportData.mrz));
const concatenatedDataHashes = formatAndConcatenateDataHashes(
mrzHash,
passportData.dataGroupHashes as DataHash[],
);
const concatenatedDataHashes =
Array.isArray(passportData.dataGroupHashes[0])
? formatAndConcatenateDataHashes(
mrzHash,
passportData.dataGroupHashes as DataHash[],
)
: passportData.dataGroupHashes
const reveal_bitmap = Array.from({ length: 88 }, (_) => '0');
@@ -271,7 +381,7 @@ function App(): JSX.Element {
}
}
if (passportData.signatureAlgorithm !== "SHA256withRSA") {
if (!["SHA256withRSA", "sha256WithRSAEncryption"].includes(passportData.signatureAlgorithm)) {
console.log(`${passportData.signatureAlgorithm} not supported for proof right now.`);
setError(`${passportData.signatureAlgorithm} not supported for proof right now.`);
return;
@@ -280,7 +390,7 @@ function App(): JSX.Element {
const inputs = {
mrz: Array.from(formattedMrz).map(byte => String(byte)),
reveal_bitmap: reveal_bitmap.map(byte => String(byte)),
dataHashes: Array.from(concatenatedDataHashes.map(toUnsignedByte)).map(byte => String(byte)),
dataHashes: Array.from((concatenatedDataHashes as number[]).map(toUnsignedByte)).map(byte => String(byte)),
eContentBytes: Array.from(passportData.eContent.map(toUnsignedByte)).map(byte => String(byte)),
signature: splitToWords(
BigInt(bytesToBigDecimal(passportData.encryptedDigest)),
@@ -295,11 +405,21 @@ function App(): JSX.Element {
address,
}
// 3. Generate a proof of passport
const start = Date.now();
NativeModules.RNPassportReader.provePassport(inputs, (err: any, res: any) => {
const end = Date.now();
console.log('inputs', inputs)
const start = Date.now();
if (Platform.OS === 'android') {
await proveAndroid(inputs);
} else {
await proveIOS(inputs);
}
const end = Date.now();
console.log('Total proof time from frontend:', end - start);
setTotalTime(end - start);
};
async function proveAndroid(inputs: any) {
NativeModules.RNPassportReader.provePassport(inputs, (err: any, res: any) => {
if (err) {
console.error(err);
setError(
@@ -319,7 +439,6 @@ function App(): JSX.Element {
console.log('deserializedInputs', deserializedInputs);
setProofTime(parsedResponse.duration);
setTotalTime(end - start);
setProof({
proof: JSON.stringify(deserializedProof),
@@ -328,7 +447,41 @@ function App(): JSX.Element {
setGeneratingProof(false);
setStep(Steps.PROOF_GENERATED);
});
};
}
async function proveIOS(inputs: any) {
try {
console.log('running mopro init action')
await NativeModules.Prover.runInitAction()
console.log('running mopro prove action')
const response = await NativeModules.Prover.runProveAction({
...inputs,
address: [BigInt(address).toString()]
})
console.log('proof response:', response)
const parsedResponse = JSON.parse(response)
console.log('running mopro verify action')
const res = await NativeModules.Prover.runVerifyAction()
console.log('verify response:', res)
setProof({
proof: JSON.stringify(formatProofIOS(parsedResponse.proof)),
inputs: JSON.stringify(formatInputsIOS(parsedResponse.inputs)),
});
// setProofTime(response.duration);
setGeneratingProof(false)
setStep('proofGenerated');
} catch (err: any) {
console.log('err', err);
setError(
"err: " + err.toString(),
);
}
}
const handleMint = async () => {
setMinting(true)
@@ -353,22 +506,22 @@ function App(): JSX.Element {
console.log('callData', callData);
// format transaction
// for now, we do it all on mumbai
// for now, we do it all on sepolia
try {
const provider = new ethers.JsonRpcProvider('https://polygon-mumbai-bor.publicnode.com');
const proofOfPassportOnMumbai = new ethers.Contract(contractAddresses.ProofOfPassport, proofOfPassportArtefact.abi, provider);
const provider = new ethers.JsonRpcProvider('https://gateway.tenderly.co/public/sepolia');
const proofOfPassportOnSepolia = new ethers.Contract(contractAddresses.ProofOfPassport, proofOfPassportArtefact.abi, provider);
const transactionRequest = await proofOfPassportOnMumbai
const transactionRequest = await proofOfPassportOnSepolia
.mint.populateTransaction(...callData);
console.log('transactionRequest', transactionRequest);
const response = await axios.post(AWS_ENDPOINT, {
chain: "mumbai",
chain: "sepolia",
tx_data: transactionRequest
});
console.log('response status', response.status)
console.log('response data', response.data)
setMintText(`Network: Mumbai. Transaction hash: ${response.data.hash}`)
setMintText(`Network: Sepolia. Transaction hash: ${response.data.hash}`)
const receipt = await provider.waitForTransaction(response.data.hash);
console.log('receipt', receipt)
if (receipt?.status === 1) {
@@ -376,16 +529,37 @@ function App(): JSX.Element {
type: 'success',
text1: 'Proof of passport minted',
})
setMintText(`SBT minted. Network: Mumbai. Transaction hash: ${response.data.hash}`)
setMintText(`SBT minted. Network: Sepolia. Transaction hash: ${response.data.hash}`)
} else {
Toast.show({
type: 'error',
text1: 'Proof of passport minting failed',
})
setMintText(`Error minting SBT. Network: Mumbai. Transaction hash: ${response.data.hash}`)
setMintText(`Error minting SBT. Network: Sepolia. Transaction hash: ${response.data.hash}`)
}
} catch (err) {
} 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]);
Toast.show({
type: 'error',
text1: `Error: ${match[1]}`,
})
} else {
Toast.show({
type: 'error',
text1: `Error: mint failed`,
})
console.log('Failed to parse blockchain error');
}
}
setMintText(`Error minting SBT. Network: Sepolia.`)
}
};

View File

@@ -1,12 +1,14 @@
# Proof of Passport App
Only Android right now, under heavy development
### Requirements
#### Requirements
Install `nodejs v18`, [circom](https://docs.circom.io/) and [snarkjs](https://github.com/iden3/snarkjs)
Install `nodejs v18`
For android, install java, android studio and the android sdk
#### Installation
For ios, install Xcode and [cocoapods](https://cocoapods.org/)
### Installation
```bash
yarn
@@ -17,38 +19,65 @@ In `/common`, also run:
yarn
```
#### Add circuit build
### Build the app
Go to the `circuit` folder of the monorepo and build the circuit.
#### Build native lib
#### Build the android native module
In `/script`, run:
Run:
```
./build_rust.sh
./scripts/build_android_module.sh
```
This will build the `libhalo2_circom_passport.so` lib and copy it to the desired place to be used by the app.
The config used is in `android/react-native-passport-reader/android/build.gradle`.
You can go there to change the profile (`debug` or `release`)
You might need to set the rust-toolchain rust version as global default. Example:
```
rustup default 1.67.0
```
And install the targets like this:
This you modify the circuits, you might have to modify `ark-circom-passport/src/passport.rs` too.
#### Build the iOS native module
Run:
```
rustup target add aarch64-linux-android
./scripts/build_ios_module.sh
```
Now:
```
cd ios
pod install
./post_install.sh
cd ..
```
#### Run the server
To run the server, first connect your phone to your computer, allow access, then:
```
yarn start
```
Then press `a` for android or `i` for iOS
If you want to see the logs and have a better ios developer experience, open `/ios` in Xcode and launch the app from there instead.
> :warning: Due to the current limitations of mopro, see [#51](https://github.com/zk-passport/proof-of-passport/issues/51), the proving on iOS only works when the app is run on Xcode. It will not work with the react native server or in a .ipa build. We are working on fixing that.
To see the android logs you'll have to use the Android Studio Logcat.
To export an apk:
```
cd android
./gradlew assembleRelease
```
The built apk it located at `android/app/build/outputs/apk/release/app-release.apk`
#### Download zkey
If you want to mint a proof of passport SBT, instead of building the circuit yourself, run:
```
./scripts/download_current_zkey.sh
```
This will download the zkey currently deployed onchain in the proof of passport contract and place it in `circuits/build``
Then, build the android or iOS native module and run the app.

View File

@@ -53,8 +53,6 @@ dependencies {
androidTestImplementation 'androidx.test:runner:1.5.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
// ML Kit dependencies
implementation 'com.google.firebase:firebase-core:21.1.1'
implementation 'com.google.firebase:firebase-ml-common:22.1.2'
implementation 'com.google.android.gms:play-services-mlkit-text-recognition:18.0.2'
//NFC Passport
@@ -100,5 +98,4 @@ dependencies {
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.9.0'
}
apply plugin: 'com.google.gms.google-services'

View File

@@ -1,29 +0,0 @@
{
"project_info": {
"project_number": "535192847858",
"project_id": "passport-reader-7e4e8",
"storage_bucket": "passport-reader-7e4e8.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:535192847858:android:7adfb90700a24d15af176b",
"android_client_info": {
"package_name": "example.jllarraz.com.passportreader"
}
},
"oauth_client": [],
"api_key": [
{
"current_key": "AIzaSyAFicYp41w4dFSq4SIiZbpKTJVVYUrMSHw"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
}
}
}
],
"configuration_version": "1"
}

View File

@@ -16,7 +16,6 @@ package example.jllarraz.com.passportreader.mlkit
import android.graphics.Bitmap
import android.media.Image
import com.google.firebase.ml.common.FirebaseMLException
import com.google.mlkit.vision.common.InputImage
import io.fotoapparat.preview.Frame
@@ -26,7 +25,6 @@ import java.nio.ByteBuffer
interface VisionImageProcessor<T> {
/** Processes the images with the underlying machine learning models. */
@Throws(FirebaseMLException::class)
fun process(data: ByteBuffer, frameMetadata: FrameMetadata, graphicOverlay: GraphicOverlay?=null, isOriginalImageReturned:Boolean = true, listener: VisionProcessorBase.Listener<T>):Boolean
/** Processes the bitmap images. */

View File

@@ -17,7 +17,6 @@ import android.graphics.Bitmap
import android.media.Image
import com.google.android.gms.tasks.Task
import com.google.firebase.ml.common.FirebaseMLException
import com.google.mlkit.vision.common.InputImage
import example.jllarraz.com.passportreader.utils.ImageUtil
import io.fotoapparat.preview.Frame
@@ -270,7 +269,6 @@ abstract class VisionProcessorBase<T> : VisionImageProcessor<T> {
override fun stop() {}
@Throws(FirebaseMLException::class)
protected abstract fun detectInImage(image: InputImage): Task<T>

View File

@@ -250,8 +250,6 @@ class CameraMLKitFragment : CameraFragment() {
mHandler.post {
try {
binding?.statusViewTop?.text = getString(R.string.status_bar_ocr, mrzInfo.documentNumber, mrzInfo.dateOfBirth, mrzInfo.dateOfExpiry)
binding?.statusViewBottom?.text = getString(R.string.status_bar_success, timeRequired)
binding?.statusViewBottom?.setTextColor(resources.getColor(R.color.status_text))
if (cameraMLKitCallback != null) {
cameraMLKitCallback!!.onPassportRead(mrzInfo)
@@ -270,7 +268,6 @@ class CameraMLKitFragment : CameraFragment() {
}
mHandler.post {
try {
binding?.statusViewBottom?.text = getString(R.string.status_bar_failure, timeRequired)
binding?.statusViewBottom?.setTextColor(Color.RED)
binding?.statusViewTop?.text = ""
} catch (e: IllegalStateException) {

View File

@@ -16,9 +16,6 @@
<string name="warning_ocr_install_progress">Installation OCR: %1d%%</string>
<string name="status_bar_ocr">Document Number: %1s Date of Birth: %2s Expiration Date: %3s</string>
<string name="status_bar_success">OCR Detected - Time required: %2dms</string>
<string name="status_bar_failure">OCR Read failure - Time required: %1d ms</string>
<string name="nfc_title">Put your phone over your passport and don\'t move it</string>

View File

@@ -21,7 +21,6 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:7.4.2'
classpath 'com.google.gms:google-services:4.4.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"

View File

@@ -28,7 +28,7 @@ public class MainActivity extends ReactActivity {
*/
@Override
protected String getMainComponentName() {
return "Proof of Passport";
return "ProofOfPassport";
}
/**

View File

@@ -1,4 +1,4 @@
{
"name": "Proof of Passport",
"name": "ProofOfPassport",
"displayName": "Proof of Passport"
}

View File

@@ -1,6 +1,8 @@
# ark circom proof and verification of passport circuit
# rust module to generate the proof of passport android native lib
To run tests and see logs:
```
cargo test -- --nocapture
```
```
Could be replaced by mopro once mopro is available on Android.

14
app/ark-zkey/.gitignore vendored Normal file
View File

@@ -0,0 +1,14 @@
# Generated by Cargo
# will have compiled files and executables
debug/
target/
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock
# These are backup files generated by rustfmt
**/*.rs.bk
# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb

32
app/ark-zkey/Cargo.toml Normal file
View File

@@ -0,0 +1,32 @@
[package]
name = "ark-zkey"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[[bin]]
name = "arkzkey-util"
path = "src/bin/arkzkey_util.rs"
# XXX: Shouldn't be necessary, but this way we stay consistent with wasmer version and fix
# error[E0432]: unresolved import `wasmer` error
# (likely due to other packages)
[patch.crates-io]
# NOTE: Forked wasmer to work around memory limits
# See https://github.com/wasmerio/wasmer/commit/09c7070
wasmer = { git = "https://github.com/oskarth/wasmer.git", rev = "09c7070" }
[dependencies]
color-eyre = "0.6"
memmap2 = "0.9"
flame = "0.2"
flamer = "0.5"
ark-serialize = { version = "=0.4.1", features = ["derive"] }
ark-bn254 = { version = "=0.4.0" }
ark-groth16 = { version = "=0.4.0" }
ark-circom = { git = "https://github.com/arkworks-rs/circom-compat.git" }
ark-relations = { version = "=0.4.0" }
ark-ff = { version = "=0.4.1" }
ark-ec = { version = "=0.4.1" }

52
app/ark-zkey/README.md Normal file
View File

@@ -0,0 +1,52 @@
# ark-zkey
Library to read `zkey` faster by serializing to `arkworks` friendly format.
See https://github.com/oskarth/mopro/issues/25 for context.
## How to use
Run the following to convert a `zkey` to an `arkzkey`. This should be done as a pre-processing step.
`cargo run --bin arkzkey-util --release -- ../mopro-core/examples/circom/keccak256/target/keccak256_256_test_final.zkey`
This will generate and place an `arkzkey` file in the same directory as the original zkey.
You can also install it locally:
`cargo install --bin arkzkey-util --path . --release`
## Tests
```
cargo test multiplier2 --release -- --nocapture
cargo test keccak256 --release -- --nocapture
cargo test rsa --release -- --nocapture
```
## Benchmark (Keccak)
`cargo test keccak256 --release -- --nocapture`
```
[build] Processing zkey data...
test tests::test_keccak256_serialization_deserialization has been running for over 60 seconds
[build]Time to process zkey data: 158.753181958s
[build] Serializing proving key and constraint matrices
[build] Time to serialize proving key and constraint matrices: 42ns
[build] Writing arkzkey to: ../mopro-core/examples/circom/keccak256/target/keccak256_256_test_final.arkzkey
[build] Time to write arkzkey: 16.204274125s
Reading arkzkey from: ../mopro-core/examples/circom/keccak256/target/keccak256_256_test_final.arkzkey
Time to open arkzkey file: 51.75µs
Time to mmap arkzkey: 17.25µs
Time to deserialize proving key: 18.323550083s
Time to deserialize matrices: 46.935792ms
Time to read arkzkey: 18.3730695s
test tests::test_keccak256_serialization_deserialization ... ok
```
Vs naive:
`[build] Time to process zkey data: 158.753181958s`
**Result: 18s vs 158s**

View File

@@ -0,0 +1,36 @@
use std::env;
use std::path::{Path, PathBuf};
extern crate ark_zkey;
use ark_zkey::{convert_zkey, read_proving_key_and_matrices_from_zkey};
fn main() -> color_eyre::eyre::Result<()> {
color_eyre::install()?;
let args: Vec<String> = env::args().collect();
if args.len() != 2 {
eprintln!("Usage: zkey_to_arkzkey <path_to_zkey_file>");
std::process::exit(1);
}
let zkey_path = &args[1];
let (proving_key, constraint_matrices) = read_proving_key_and_matrices_from_zkey(zkey_path)?;
let arkzkey_path = get_arkzkey_path(zkey_path);
let arkzkey_path_str = arkzkey_path
.to_str()
.ok_or_else(|| color_eyre::eyre::eyre!("Failed to convert arkzkey path to string"))?;
convert_zkey(proving_key, constraint_matrices, &arkzkey_path_str)?;
println!("Converted zkey file saved to: {}", arkzkey_path.display());
Ok(())
}
fn get_arkzkey_path(zkey_path: &str) -> PathBuf {
let path = Path::new(zkey_path);
let mut arkzkey_path = path.to_path_buf();
arkzkey_path.set_extension("arkzkey");
arkzkey_path
}

247
app/ark-zkey/src/lib.rs Normal file
View File

@@ -0,0 +1,247 @@
use ark_bn254::{Bn254, Fr};
use ark_circom::read_zkey;
//use ark_ec::pairing::Pairing;
use ark_ff::Field;
use ark_groth16::ProvingKey;
//use ark_groth16::VerifyingKey;
use ark_relations::r1cs::ConstraintMatrices;
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
use color_eyre::eyre::{Result, WrapErr};
use memmap2::Mmap;
use std::fs::File;
//use std::io::Cursor;
//use std::io::{Read,self};
use std::io::BufReader;
use std::path::PathBuf;
use std::time::Instant;
#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug, PartialEq)]
pub struct SerializableProvingKey(pub ProvingKey<Bn254>);
#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug, PartialEq)]
pub struct SerializableMatrix<F: Field> {
pub data: Vec<Vec<(F, usize)>>,
}
#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug, PartialEq)]
pub struct SerializableConstraintMatrices<F: Field> {
pub num_instance_variables: usize,
pub num_witness_variables: usize,
pub num_constraints: usize,
pub a_num_non_zero: usize,
pub b_num_non_zero: usize,
pub c_num_non_zero: usize,
pub a: SerializableMatrix<F>,
pub b: SerializableMatrix<F>,
pub c: SerializableMatrix<F>,
}
impl<F: Field> From<Vec<Vec<(F, usize)>>> for SerializableMatrix<F> {
fn from(matrix: Vec<Vec<(F, usize)>>) -> Self {
SerializableMatrix { data: matrix }
}
}
impl<F: Field> From<SerializableMatrix<F>> for Vec<Vec<(F, usize)>> {
fn from(serializable_matrix: SerializableMatrix<F>) -> Self {
serializable_matrix.data
}
}
pub fn serialize_proving_key(pk: &SerializableProvingKey) -> Vec<u8> {
let mut serialized_data = Vec::new();
pk.serialize_compressed(&mut serialized_data)
.expect("Serialization failed");
serialized_data
}
pub fn deserialize_proving_key(data: Vec<u8>) -> SerializableProvingKey {
SerializableProvingKey::deserialize_compressed_unchecked(&mut &data[..])
.expect("Deserialization failed")
}
pub fn read_arkzkey(
arkzkey_path: &str,
) -> Result<(SerializableProvingKey, SerializableConstraintMatrices<Fr>)> {
let now = std::time::Instant::now();
let arkzkey_file_path = PathBuf::from(arkzkey_path);
let arkzkey_file = File::open(arkzkey_file_path).wrap_err("Failed to open arkzkey file")?;
println!("Time to open arkzkey file: {:?}", now.elapsed());
//let mut buf_reader = BufReader::new(arkzkey_file);
// Using mmap
let now = std::time::Instant::now();
let mmap = unsafe { Mmap::map(&arkzkey_file)? };
let mut cursor = std::io::Cursor::new(mmap);
println!("Time to mmap arkzkey: {:?}", now.elapsed());
// Was &mut buf_reader
let now = std::time::Instant::now();
let proving_key = SerializableProvingKey::deserialize_compressed_unchecked(&mut cursor)
.wrap_err("Failed to deserialize proving key")?;
println!("Time to deserialize proving key: {:?}", now.elapsed());
let now = std::time::Instant::now();
let constraint_matrices =
SerializableConstraintMatrices::deserialize_compressed_unchecked(&mut cursor)
.wrap_err("Failed to deserialize constraint matrices")?;
println!("Time to deserialize matrices: {:?}", now.elapsed());
Ok((proving_key, constraint_matrices))
}
// TODO: Return ProvingKey<Bn254>, ConstraintMatrices<Fr>?
pub fn read_arkzkey_from_bytes(
arkzkey_bytes: &[u8],
) -> Result<(ProvingKey<Bn254>, ConstraintMatrices<Fr>)> {
let mut cursor = std::io::Cursor::new(arkzkey_bytes);
let now = std::time::Instant::now();
let serialized_proving_key =
SerializableProvingKey::deserialize_compressed_unchecked(&mut cursor)
.wrap_err("Failed to deserialize proving key")?;
println!("Time to deserialize proving key: {:?}", now.elapsed());
let now = std::time::Instant::now();
let serialized_constraint_matrices =
SerializableConstraintMatrices::deserialize_compressed_unchecked(&mut cursor)
.wrap_err("Failed to deserialize constraint matrices")?;
println!("Time to deserialize matrices: {:?}", now.elapsed());
// Get on right form for API
let proving_key: ProvingKey<Bn254> = serialized_proving_key.0;
let constraint_matrices: ConstraintMatrices<Fr> = ConstraintMatrices {
num_instance_variables: serialized_constraint_matrices.num_instance_variables,
num_witness_variables: serialized_constraint_matrices.num_witness_variables,
num_constraints: serialized_constraint_matrices.num_constraints,
a_num_non_zero: serialized_constraint_matrices.a_num_non_zero,
b_num_non_zero: serialized_constraint_matrices.b_num_non_zero,
c_num_non_zero: serialized_constraint_matrices.c_num_non_zero,
a: serialized_constraint_matrices.a.data,
b: serialized_constraint_matrices.b.data,
c: serialized_constraint_matrices.c.data,
};
Ok((proving_key, constraint_matrices))
}
pub fn read_proving_key_and_matrices_from_zkey(
zkey_path: &str,
) -> Result<(SerializableProvingKey, SerializableConstraintMatrices<Fr>)> {
println!("Reading zkey from: {}", zkey_path);
let now = Instant::now();
let zkey_file_path = PathBuf::from(zkey_path);
let zkey_file = File::open(zkey_file_path).wrap_err("Failed to open zkey file")?;
let mut buf_reader = BufReader::new(zkey_file);
let (proving_key, matrices) =
read_zkey(&mut buf_reader).wrap_err("Failed to read zkey file")?;
println!("Time to read zkey: {:?}", now.elapsed());
println!("Serializing proving key and constraint matrices");
let now = Instant::now();
let serializable_proving_key = SerializableProvingKey(proving_key);
let serializable_constrain_matrices = SerializableConstraintMatrices {
num_instance_variables: matrices.num_instance_variables,
num_witness_variables: matrices.num_witness_variables,
num_constraints: matrices.num_constraints,
a_num_non_zero: matrices.a_num_non_zero,
b_num_non_zero: matrices.b_num_non_zero,
c_num_non_zero: matrices.c_num_non_zero,
a: SerializableMatrix { data: matrices.a },
b: SerializableMatrix { data: matrices.b },
c: SerializableMatrix { data: matrices.c },
};
println!(
"Time to serialize proving key and constraint matrices: {:?}",
now.elapsed()
);
Ok((serializable_proving_key, serializable_constrain_matrices))
}
pub fn convert_zkey(
proving_key: SerializableProvingKey,
constraint_matrices: SerializableConstraintMatrices<Fr>,
arkzkey_path: &str,
) -> Result<()> {
let arkzkey_file_path = PathBuf::from(arkzkey_path);
let serialized_path = PathBuf::from(arkzkey_file_path);
let mut file =
File::create(&serialized_path).wrap_err("Failed to create serialized proving key file")?;
proving_key
.serialize_compressed(&mut file)
.wrap_err("Failed to serialize proving key")?;
constraint_matrices
.serialize_compressed(&mut file)
.wrap_err("Failed to serialize constraint matrices")?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use std::time::Instant;
fn test_circuit_serialization_deserialization(zkey_path: &str) -> Result<()> {
let arkzkey_path = zkey_path.replace(".zkey", ".arkzkey");
let (original_proving_key, original_constraint_matrices) =
read_proving_key_and_matrices_from_zkey(zkey_path)?;
println!("[build] Writing arkzkey to: {}", arkzkey_path);
let now = Instant::now();
convert_zkey(
original_proving_key.clone(),
original_constraint_matrices.clone(),
&arkzkey_path,
)?;
println!("[build] Time to write arkzkey: {:?}", now.elapsed());
println!("Reading arkzkey from: {}", arkzkey_path);
let now = Instant::now();
let (deserialized_proving_key, deserialized_constraint_matrices) =
read_arkzkey(&arkzkey_path)?;
println!("Time to read arkzkey: {:?}", now.elapsed());
assert_eq!(
original_proving_key, deserialized_proving_key,
"Original and deserialized proving keys do not match"
);
assert_eq!(
original_constraint_matrices, deserialized_constraint_matrices,
"Original and deserialized constraint matrices do not match"
);
Ok(())
}
#[test]
fn test_multiplier2_serialization_deserialization() -> Result<()> {
test_circuit_serialization_deserialization(
"../mopro-core/examples/circom/multiplier2/target/multiplier2_final.zkey",
)
}
#[test]
fn test_keccak256_serialization_deserialization() -> Result<()> {
test_circuit_serialization_deserialization(
"../mopro-core/examples/circom/keccak256/target/keccak256_256_test_final.zkey",
)
}
#[test]
fn test_rsa_serialization_deserialization() -> Result<()> {
test_circuit_serialization_deserialization(
"../mopro-core/examples/circom/rsa/target/main_final.zkey",
)
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
{"ProofOfPassport":"0x7D459347d092D35f043f73021f06c19f834f8c3E","Groth16Verifier":"0x72d8Fb7Dd33c264b53D313ff3BF25D993c0AD050"}
{"ProofOfPassport":"0x50c47Be849dFac1FCbEDa031425896AF49d9FDFC","Groth16Verifier":"0xD6F5CA79b90a9E20B38df773122B4f50a1fda317"}

View File

@@ -1,368 +0,0 @@
import Foundation
import CommonCrypto
/// Encrypts a message using AES/CBC/NOPADDING with a specified key and initialisation vector
/// - Parameter key: Key use to encrypt
/// - Parameter message: Message to encrypt
/// - Parameter iv: Initialisation vector
@available(iOS 13, macOS 10.15, *)
public func AESEncrypt(key:[UInt8], message:[UInt8], iv:[UInt8]) -> [UInt8] {
let dataLength = message.count
let cryptLen = message.count + kCCBlockSizeAES128
var cryptData = Data(count: cryptLen)
let keyLength = size_t(key.count)
let operation: CCOperation = CCOperation(kCCEncrypt)
let algorithm: CCAlgorithm = CCAlgorithm(kCCAlgorithmAES)
let options: CCOptions = CCOptions(0)
var numBytesEncrypted = 0
var cryptStatus: CCCryptorStatus = CCCryptorStatus(kCCSuccess)
key.withUnsafeBytes {keyBytes in
message.withUnsafeBytes{ dataBytes in
iv.withUnsafeBytes{ ivBytes in
cryptData.withUnsafeMutableBytes{ cryptBytes in
cryptStatus = CCCrypt(operation,
algorithm,
options,
keyBytes.baseAddress,
keyLength,
ivBytes.baseAddress,
dataBytes.baseAddress,
dataLength,
cryptBytes.bindMemory(to: UInt8.self).baseAddress,
cryptLen,
&numBytesEncrypted)
}
}
}
}
if cryptStatus == kCCSuccess {
cryptData.count = Int(numBytesEncrypted)
return [UInt8](cryptData)
} else {
Log.error("AES Encrypt Error: \(cryptStatus)")
}
return []
}
/// Decrypts a message using AES/CBC/NOPADDING with a specified key and initialisation vector
/// - Parameter key: Key use to decrypt
/// - Parameter message: Message to decrypt
/// - Parameter iv: Initialisation vector
@available(iOS 13, macOS 10.15, *)
public func AESDecrypt(key:[UInt8], message:[UInt8], iv:[UInt8]) -> [UInt8] {
var fixedKey = key
if key.count == 16 {
fixedKey += key[0..<8]
}
let data = Data(message)
let dataLength = message.count
let cryptLen = data.count + kCCBlockSizeAES128
var cryptData = Data(count: cryptLen)
let keyLength = size_t(key.count)
let operation: CCOperation = UInt32(kCCDecrypt)
let algorithm: CCAlgorithm = UInt32(kCCAlgorithmAES)
let options: CCOptions = UInt32(0)
var numBytesEncrypted = 0
let cryptStatus = fixedKey.withUnsafeBytes {keyBytes in
message.withUnsafeBytes{ dataBytes in
cryptData.withUnsafeMutableBytes{ cryptBytes in
CCCrypt(operation,
algorithm,
options,
keyBytes.baseAddress,
keyLength,
iv,
dataBytes.baseAddress,
dataLength,
cryptBytes.bindMemory(to: UInt8.self).baseAddress,
cryptLen,
&numBytesEncrypted)
}
}
}
if cryptStatus == kCCSuccess {
cryptData.count = Int(numBytesEncrypted)
return [UInt8](cryptData)
} else {
Log.error("AES Decrypt Error: \(cryptStatus)")
}
return []
}
/// Decrypts a message using AES/ECB/NOPADDING with a specified key and initialisation vector
/// - Parameter key: Key use to decrypt
/// - Parameter message: Message to decrypt
/// - Parameter iv: Initialisation vector
@available(iOS 13, macOS 10.15, *)
public func AESECBEncrypt(key:[UInt8], message:[UInt8]) -> [UInt8] {
let dataLength = message.count
let cryptLen = message.count + kCCBlockSizeAES128
var cryptData = Data(count: cryptLen)
let keyLength = size_t(key.count)
let operation: CCOperation = CCOperation(kCCEncrypt)
let algorithm: CCAlgorithm = CCAlgorithm(kCCAlgorithmAES)
let options: CCOptions = CCOptions(kCCOptionECBMode)
var numBytesEncrypted = 0
let cryptStatus = key.withUnsafeBytes {keyBytes in
message.withUnsafeBytes{ dataBytes in
cryptData.withUnsafeMutableBytes{ cryptBytes in
CCCrypt(operation,
algorithm,
options,
keyBytes.baseAddress,
keyLength,
nil,
dataBytes.baseAddress,
dataLength,
cryptBytes.bindMemory(to: UInt8.self).baseAddress,
cryptLen,
&numBytesEncrypted)
}
}
}
if cryptStatus == kCCSuccess {
cryptData.count = Int(numBytesEncrypted)
return [UInt8](cryptData)
} else {
Log.error("AESECBEncrypt Error: \(cryptStatus)")
}
return []
}
/// Encrypts a message using DES3 with a specified key and initialisation vector
/// - Parameter key: Key use to encrypt
/// - Parameter message: Message to encrypt
/// - Parameter iv: Initialisation vector
@available(iOS 13, macOS 10.15, *)
public func tripleDESEncrypt(key:[UInt8], message:[UInt8], iv:[UInt8]) -> [UInt8] {
// Fix key data - if length is 16 then take the first 98 bytes and append them to the end to make 24 bytes
var fixedKey = key
if key.count == 16 {
fixedKey += key[0..<8]
}
let dataLength = message.count
let cryptLen = message.count + kCCBlockSize3DES
var cryptData = Data(count: cryptLen)
let keyLength = size_t(kCCKeySize3DES)
let operation: CCOperation = UInt32(kCCEncrypt)
let algorithm: CCAlgorithm = UInt32(kCCAlgorithm3DES)
let options: CCOptions = UInt32(0)
var numBytesEncrypted = 0
let cryptStatus = fixedKey.withUnsafeBytes {keyBytes in
message.withUnsafeBytes{ dataBytes in
iv.withUnsafeBytes{ ivBytes in
cryptData.withUnsafeMutableBytes{ cryptBytes in
CCCrypt(operation,
algorithm,
options,
keyBytes.baseAddress,
keyLength,
ivBytes.baseAddress,
dataBytes.baseAddress,
dataLength,
cryptBytes.bindMemory(to: UInt8.self).baseAddress,
cryptLen,
&numBytesEncrypted)
}
}
}
}
if cryptStatus == kCCSuccess {
cryptData.count = Int(numBytesEncrypted)
return [UInt8](cryptData)
} else {
Log.error("Error: \(cryptStatus)")
}
return []
}
/// Decrypts a message using DES3 with a specified key and initialisation vector
/// - Parameter key: Key use to decrypt
/// - Parameter message: Message to decrypt
/// - Parameter iv: Initialisation vector
@available(iOS 13, macOS 10.15, *)
public func tripleDESDecrypt(key:[UInt8], message:[UInt8], iv:[UInt8]) -> [UInt8] {
var fixedKey = key
if key.count == 16 {
fixedKey += key[0..<8]
}
let data = Data(message)
let dataLength = message.count
let cryptLen = data.count + kCCBlockSize3DES
var cryptData = Data(count: cryptLen)
let keyLength = size_t(kCCKeySize3DES)
let operation: CCOperation = UInt32(kCCDecrypt)
let algorithm: CCAlgorithm = UInt32(kCCAlgorithm3DES)
let options: CCOptions = UInt32(0)
var numBytesEncrypted = 0
let cryptStatus = fixedKey.withUnsafeBytes {keyBytes in
message.withUnsafeBytes{ dataBytes in
cryptData.withUnsafeMutableBytes{ cryptBytes in
CCCrypt(operation,
algorithm,
options,
keyBytes.baseAddress,
keyLength,
iv,
dataBytes.baseAddress,
dataLength,
cryptBytes.bindMemory(to: UInt8.self).baseAddress,
cryptLen,
&numBytesEncrypted)
}
}
}
if cryptStatus == kCCSuccess {
cryptData.count = Int(numBytesEncrypted)
return [UInt8](cryptData)
} else {
Log.error("Error: \(cryptStatus)")
}
return []
}
/// Encrypts a message using DES with a specified key and initialisation vector
/// - Parameter key: Key use to encrypt
/// - Parameter message: Message to encrypt
/// - Parameter iv: Initialisation vector
/// - Parameter options: Encryption options to use
@available(iOS 13, macOS 10.15, *)
public func DESEncrypt(key:[UInt8], message:[UInt8], iv:[UInt8], options:UInt32 = 0) -> [UInt8] {
let dataLength = message.count
let cryptLen = message.count + kCCBlockSizeDES
var cryptData = Data(count: cryptLen)
let keyLength = size_t(kCCKeySizeDES)
let operation: CCOperation = UInt32(kCCEncrypt)
let algorithm: CCAlgorithm = UInt32(kCCAlgorithmDES)
let options: CCOptions = options
var numBytesEncrypted = 0
let cryptStatus = key.withUnsafeBytes {keyBytes in
message.withUnsafeBytes{ dataBytes in
iv.withUnsafeBytes{ ivBytes in
cryptData.withUnsafeMutableBytes{ cryptBytes in
CCCrypt(operation,
algorithm,
options,
keyBytes.baseAddress,
keyLength,
ivBytes.baseAddress,
dataBytes.baseAddress,
dataLength,
cryptBytes.bindMemory(to: UInt8.self).baseAddress,
cryptLen,
&numBytesEncrypted)
}
}
}
}
if cryptStatus == kCCSuccess {
cryptData.count = Int(numBytesEncrypted)
return [UInt8](cryptData)
} else {
Log.error("Error: \(cryptStatus)")
}
return []
}
/// Decrypts a message using DES with a specified key and initialisation vector
/// - Parameter key: Key use to decrypt
/// - Parameter message: Message to decrypt
/// - Parameter iv: Initialisation vector
/// - Parameter options: Decryption options to use
@available(iOS 13, macOS 10.15, *)
public func DESDecrypt(key:[UInt8], message:[UInt8], iv:[UInt8], options:UInt32 = 0) -> [UInt8] {
let dataLength = message.count
let cryptLen = message.count + kCCBlockSizeDES
var cryptData = Data(count: cryptLen)
let keyLength = size_t(kCCKeySizeDES)
let operation: CCOperation = UInt32(kCCDecrypt)
let algorithm: CCAlgorithm = UInt32(kCCAlgorithmDES)
let options: CCOptions = options
var numBytesEncrypted = 0
let cryptStatus = key.withUnsafeBytes {keyBytes in
message.withUnsafeBytes{ dataBytes in
iv.withUnsafeBytes{ ivBytes in
cryptData.withUnsafeMutableBytes{ cryptBytes in
CCCrypt(operation,
algorithm,
options,
keyBytes.baseAddress,
keyLength,
nil,
dataBytes.baseAddress,
dataLength,
cryptBytes.bindMemory(to: UInt8.self).baseAddress,
cryptLen,
&numBytesEncrypted)
}
}
}
}
if cryptStatus == kCCSuccess {
cryptData.count = Int(numBytesEncrypted)
return [UInt8](cryptData)
} else {
Log.error("Error: \(cryptStatus)")
}
return []
}

View File

@@ -1,62 +0,0 @@
import Foundation
@available(iOS 13, macOS 10.15, *)
public class ActiveAuthenticationInfo : SecurityInfo {
var oid : String
var version : Int
var signatureAlgorithmOID : String?
static func checkRequiredIdentifier(_ oid : String) -> Bool {
return ID_AA_OID == oid
}
init(oid: String, version: Int, signatureAlgorithmOID: String? = nil) {
self.oid = oid
self.version = version
self.signatureAlgorithmOID = signatureAlgorithmOID
}
public override func getObjectIdentifier() -> String {
return oid
}
public override func getProtocolOIDString() -> String {
return ActiveAuthenticationInfo.toProtocolOIDString(oid:oid)
}
public func getSignatureAlgorithmOIDString() -> String? {
return ActiveAuthenticationInfo.toSignatureAlgorithmOIDString(oid: signatureAlgorithmOID)
}
private static func toProtocolOIDString(oid : String) -> String {
if ID_AA_OID == oid {
return "id-AA"
}
return oid
}
private static func toSignatureAlgorithmOIDString(oid: String?) -> String? {
if (ECDSA_PLAIN_SHA1_OID == oid) {
return "ecdsa-plain-SHA1";
}
if (ECDSA_PLAIN_SHA224_OID == oid) {
return "ecdsa-plain-SHA224";
}
if (ECDSA_PLAIN_SHA256_OID == oid) {
return "ecdsa-plain-SHA256";
}
if (ECDSA_PLAIN_SHA384_OID == oid) {
return "ecdsa-plain-SHA384";
}
if (ECDSA_PLAIN_SHA512_OID == oid) {
return "ecdsa-plain-SHA512";
}
if (ECDSA_PLAIN_RIPEMD160_OID == oid) {
return "ecdsa-plain-RIPEMD160";
}
return nil
}
}

View File

@@ -1,59 +0,0 @@
import Foundation
@available(iOS 13, macOS 10.15, *)
public class COM : DataGroup {
public private(set) var version : String = "Unknown"
public private(set) var unicodeVersion : String = "Unknown"
public private(set) var dataGroupsPresent : [String] = []
required init( _ data : [UInt8] ) throws {
try super.init(data)
datagroupType = .COM
}
override func parse(_ data: [UInt8]) throws {
var tag = try getNextTag()
if tag != 0x5F01 {
throw NFCPassportReaderError.InvalidResponse
}
// Version is 4 bytes (ascii) - AABB
// AA is major number, BB is minor number
// e.g. 48 49 48 55 -> 01 07 -> 1.7
var versionBytes = try getNextValue()
if versionBytes.count == 4 {
let aa = Int( String(cString: Array(versionBytes[0..<2] + [0]) )) ?? -1
let bb = Int( String(cString: Array(versionBytes[2...] + [0])) ) ?? -1
if aa != -1 && bb != -1 {
version = "\(aa).\(bb)"
}
}
tag = try getNextTag()
if tag != 0x5F36 {
throw NFCPassportReaderError.InvalidResponse
}
versionBytes = try getNextValue()
if versionBytes.count == 6 {
let aa = Int( String(cString: Array(versionBytes[0..<2] + [0])) ) ?? -1
let bb = Int( String(cString: Array(versionBytes[2..<4] + [0])) ) ?? -1
let cc = Int( String(cString: Array(versionBytes[4...]) + [0]) ) ?? -1
if aa != -1 && bb != -1 && cc != -1 {
unicodeVersion = "\(aa).\(bb).\(cc)"
}
}
tag = try getNextTag()
if tag != 0x5C {
throw NFCPassportReaderError.InvalidResponse
}
let vals = try getNextValue()
for v in vals {
if let index = DataGroupParser.tags.firstIndex(of: v) {
dataGroupsPresent.append( DataGroupParser.dataGroupNames[index] )
}
}
Log.debug( "DG Found - \(dataGroupsPresent)" )
}
}

View File

@@ -1,31 +0,0 @@
import Foundation
// SecurityInfos ::= SET of SecurityInfo
// SecurityInfo ::= SEQUENCE {
// protocol OBJECT IDENTIFIER,
// requiredData ANY DEFINED BY protocol,
// optionalData ANY DEFINED BY protocol OPTIONAL
@available(iOS 13, macOS 10.15, *)
public class CardAccess {
private var asn1 : ASN1Item!
public private(set) var securityInfos : [SecurityInfo] = [SecurityInfo]()
var paceInfo : PACEInfo? {
get {
return (securityInfos.filter { ($0 as? PACEInfo) != nil }).first as? PACEInfo
}
}
required init( _ data : [UInt8] ) throws {
let p = SimpleASN1DumpParser()
asn1 = try p.parse(data: Data(data))
// Bit of a hack at the moment - passing in the body - if we had a decent ASN1 parser then this would be better! ;)
for i in 0 ..< asn1.getNumberOfChildren() {
if let child = asn1.getChild(i),
let secInfo = SecurityInfo.getInstance( object:child, body : data ) {
securityInfos.append(secInfo)
}
}
}
}

View File

@@ -1,128 +0,0 @@
import Foundation
@available(iOS 13, macOS 10.15, *)
public class ChipAuthenticationInfo : SecurityInfo {
var oid : String
var version : Int
var keyId : Int?
static func checkRequiredIdentifier(_ oid : String) -> Bool {
return ID_CA_DH_3DES_CBC_CBC_OID == oid
|| ID_CA_ECDH_3DES_CBC_CBC_OID == oid
|| ID_CA_DH_AES_CBC_CMAC_128_OID == oid
|| ID_CA_DH_AES_CBC_CMAC_192_OID == oid
|| ID_CA_DH_AES_CBC_CMAC_256_OID == oid
|| ID_CA_ECDH_AES_CBC_CMAC_128_OID == oid
|| ID_CA_ECDH_AES_CBC_CMAC_192_OID == oid
|| ID_CA_ECDH_AES_CBC_CMAC_256_OID == oid
}
init(oid: String, version: Int, keyId: Int? = nil) {
self.oid = oid
self.version = version
self.keyId = keyId
}
public override func getObjectIdentifier() -> String {
return oid
}
public override func getProtocolOIDString() -> String {
return ChipAuthenticationInfo.toProtocolOIDString(oid:oid)
}
// The keyid refers to a specific key if there are multiple otherwise if not set, only one key is present so set to 0
public func getKeyId() -> Int {
return keyId ?? 0
}
/// Returns the key agreement algorithm - DH or ECDH for the given Chip Authentication oid
/// - Parameter oid: the object identifier
/// - Returns: key agreement algorithm
/// - Throws: InvalidDataPassed error if invalid oid specified
public static func toKeyAgreementAlgorithm( oid : String ) throws -> String {
if ID_CA_DH_3DES_CBC_CBC_OID == oid
|| ID_CA_DH_AES_CBC_CMAC_128_OID == oid
|| ID_CA_DH_AES_CBC_CMAC_192_OID == oid
|| ID_CA_DH_AES_CBC_CMAC_256_OID == oid {
return "DH";
} else if ID_CA_ECDH_3DES_CBC_CBC_OID == oid
|| ID_CA_ECDH_AES_CBC_CMAC_128_OID == oid
|| ID_CA_ECDH_AES_CBC_CMAC_192_OID == oid
|| ID_CA_ECDH_AES_CBC_CMAC_256_OID == oid {
return "ECDH";
}
throw NFCPassportReaderError.InvalidDataPassed( "Unable to lookup key agreement algorithm - invalid oid" )
}
/// Returns the cipher algorithm - DESede or AES for the given Chip Authentication oid
/// - Parameter oid: the object identifier
/// - Returns: the cipher algorithm type
/// - Throws: InvalidDataPassed error if invalid oid specified
public static func toCipherAlgorithm( oid : String ) throws -> String {
if ID_CA_DH_3DES_CBC_CBC_OID == oid
|| ID_CA_ECDH_3DES_CBC_CBC_OID == oid {
return "DESede";
} else if ID_CA_DH_AES_CBC_CMAC_128_OID == oid
|| ID_CA_DH_AES_CBC_CMAC_192_OID == oid
|| ID_CA_DH_AES_CBC_CMAC_256_OID == oid
|| ID_CA_ECDH_AES_CBC_CMAC_128_OID == oid
|| ID_CA_ECDH_AES_CBC_CMAC_192_OID == oid
|| ID_CA_ECDH_AES_CBC_CMAC_256_OID == oid {
return "AES";
}
throw NFCPassportReaderError.InvalidDataPassed( "Unable to lookup cipher algorithm - invalid oid" )
}
/// Returns the key length in bits (128, 192, or 256) for the given Chip Authentication oid
/// - Parameter oid: the object identifier
/// - Returns: the key length in bits
/// - Throws: InvalidDataPassed error if invalid oid specified
public static func toKeyLength( oid : String ) throws -> Int {
if ID_CA_DH_3DES_CBC_CBC_OID == oid
|| ID_CA_ECDH_3DES_CBC_CBC_OID == oid
|| ID_CA_DH_AES_CBC_CMAC_128_OID == oid
|| ID_CA_ECDH_AES_CBC_CMAC_128_OID == oid {
return 128;
} else if ID_CA_DH_AES_CBC_CMAC_192_OID == oid
|| ID_CA_ECDH_AES_CBC_CMAC_192_OID == oid {
return 192;
} else if ID_CA_DH_AES_CBC_CMAC_256_OID == oid
|| ID_CA_ECDH_AES_CBC_CMAC_256_OID == oid {
return 256;
}
throw NFCPassportReaderError.InvalidDataPassed( "Unable to get key length - invalid oid" )
}
private static func toProtocolOIDString(oid : String) -> String {
if ID_CA_DH_3DES_CBC_CBC_OID == oid {
return "id-CA-DH-3DES-CBC-CBC"
}
if ID_CA_DH_AES_CBC_CMAC_128_OID == oid {
return "id-CA-DH-AES-CBC-CMAC-128"
}
if ID_CA_DH_AES_CBC_CMAC_192_OID == oid {
return "id-CA-DH-AES-CBC-CMAC-192"
}
if ID_CA_DH_AES_CBC_CMAC_256_OID == oid {
return "id-CA-DH-AES-CBC-CMAC-256"
}
if ID_CA_ECDH_3DES_CBC_CBC_OID == oid {
return "id-CA-ECDH-3DES-CBC-CBC"
}
if ID_CA_ECDH_AES_CBC_CMAC_128_OID == oid {
return "id-CA-ECDH-AES-CBC-CMAC-128"
}
if ID_CA_ECDH_AES_CBC_CMAC_192_OID == oid {
return "id-CA-ECDH-AES-CBC-CMAC-192"
}
if ID_CA_ECDH_AES_CBC_CMAC_256_OID == oid {
return "id-CA-ECDH-AES-CBC-CMAC-256"
}
return oid
}
}

View File

@@ -1,46 +0,0 @@
import Foundation
@available(iOS 13, macOS 10.15, *)
public class ChipAuthenticationPublicKeyInfo : SecurityInfo {
var oid : String
var pubKey : OpaquePointer
var keyId : Int?
static func checkRequiredIdentifier(_ oid : String) -> Bool {
return ID_PK_DH_OID == oid
|| ID_PK_ECDH_OID == oid
}
init(oid:String, pubKey:OpaquePointer, keyId: Int? = nil) {
self.oid = oid
self.pubKey = pubKey
self.keyId = keyId
}
public override func getObjectIdentifier() -> String {
return oid
}
public override func getProtocolOIDString() -> String {
return ChipAuthenticationPublicKeyInfo.toProtocolOIDString(oid:oid)
}
// The keyid refers to a specific key if there are multiple otherwise if not set, only one key is present so set to 0
public func getKeyId() -> Int {
return keyId ?? 0
}
private static func toProtocolOIDString(oid : String) -> String {
if ID_PK_DH_OID == oid {
return "id-PK-DH"
}
if ID_PK_ECDH_OID == oid {
return "id-PK-ECDH"
}
return oid
}
}

View File

@@ -1,78 +0,0 @@
import Foundation
@available(iOS 13, macOS 10.15, *)
public class DataGroup {
public var datagroupType : DataGroupId = .Unknown
/// Body contains the actual data
public private(set) var body : [UInt8] = []
/// Data contains the whole DataGroup data (as that is what the hash is calculated from
public private(set) var data : [UInt8] = []
var pos = 0
required init( _ data : [UInt8] ) throws {
self.data = data
// Skip the first byte which is the header byte
pos = 1
let _ = try getNextLength()
self.body = [UInt8](data[pos...])
try parse(data)
}
func parse( _ data:[UInt8] ) throws {
}
func getNextTag() throws -> Int {
var tag = 0
// Fix for some passports that may have invalid data - ensure that we do have data!
guard data.count > pos else {
throw NFCPassportReaderError.TagNotValid
}
if binToHex(data[pos]) & 0x0F == 0x0F {
tag = Int(binToHex(data[pos..<pos+2]))
pos += 2
} else {
tag = Int(data[pos])
pos += 1
}
return tag
}
func getNextLength() throws -> Int {
let end = pos+4 < data.count ? pos+4 : data.count
let (len, lenOffset) = try asn1Length([UInt8](data[pos..<end]))
pos += lenOffset
return len
}
func getNextValue() throws -> [UInt8] {
let length = try getNextLength()
let value = [UInt8](data[pos ..< pos+length])
pos += length
return value
}
public func hash( _ hashAlgorythm: String ) -> [UInt8] {
var ret : [UInt8] = []
if hashAlgorythm == "SHA1" {
ret = calcSHA1Hash(self.data)
} else if hashAlgorythm == "SHA224" {
ret = calcSHA224Hash(self.data)
} else if hashAlgorythm == "SHA256" {
ret = calcSHA256Hash(self.data)
} else if hashAlgorythm == "SHA384" {
ret = calcSHA384Hash(self.data)
} else if hashAlgorythm == "SHA512" {
ret = calcSHA512Hash(self.data)
}
return ret
}
}

View File

@@ -1,108 +0,0 @@
import Foundation
@available(iOS 13, macOS 10.15, *)
public enum DocTypeEnum: String {
case TD1
case TD2
case OTHER
var desc: String {
get {
return self.rawValue
}
}
}
@available(iOS 13, macOS 10.15, *)
public class DataGroup1 : DataGroup {
public private(set) var elements : [String:String] = [:]
required init( _ data : [UInt8] ) throws {
try super.init(data)
datagroupType = .DG1
}
override func parse(_ data: [UInt8]) throws {
let tag = try getNextTag()
if tag != 0x5F1F {
throw NFCPassportReaderError.InvalidResponse
}
let body = try getNextValue()
let docType = getMRZType(length:body.count)
switch docType {
case .TD1:
self.parseTd1(body)
case .TD2:
self.parseTd2(body)
default:
self.parseOther(body)
}
// Store MRZ data
elements["5F1F"] = String(bytes: body, encoding:.utf8)
}
func parseTd1(_ data : [UInt8]) {
elements["5F03"] = String(bytes: data[0..<2], encoding:.utf8)
elements["5F28"] = String( bytes:data[2..<5], encoding:.utf8)
elements["5A"] = String( bytes:data[5..<14], encoding:.utf8)
elements["5F04"] = String( bytes:data[14..<15], encoding:.utf8)
elements["53"] = (String( bytes:data[15..<30], encoding:.utf8) ?? "") +
(String( bytes:data[48..<59], encoding:.utf8) ?? "")
elements["5F57"] = String( bytes:data[30..<36], encoding:.utf8)
elements["5F05"] = String( bytes:data[36..<37], encoding:.utf8)
elements["5F35"] = String( bytes:data[37..<38], encoding:.utf8)
elements["59"] = String( bytes:data[38..<44], encoding:.utf8)
elements["5F06"] = String( bytes:data[44..<45], encoding:.utf8)
elements["5F2C"] = String( bytes:data[45..<48], encoding:.utf8)
elements["5F07"] = String( bytes:data[59..<60], encoding:.utf8)
elements["5B"] = String( bytes:data[60...], encoding:.utf8)
}
func parseTd2(_ data : [UInt8]) {
elements["5F03"] = String( bytes:data[0..<2], encoding:.utf8)
elements["5F28"] = String( bytes:data[2..<5], encoding:.utf8)
elements["5B"] = String( bytes:data[5..<36], encoding:.utf8)
elements["5A"] = String( bytes:data[36..<45], encoding:.utf8)
elements["5F04"] = String( bytes:data[45..<46], encoding:.utf8)
elements["5F2C"] = String( bytes:data[46..<49], encoding:.utf8)
elements["5F57"] = String( bytes:data[49..<55], encoding:.utf8)
elements["5F05"] = String( bytes:data[55..<56], encoding:.utf8)
elements["5F35"] = String( bytes:data[56..<57], encoding:.utf8)
elements["59"] = String( bytes:data[57..<63], encoding:.utf8)
elements["5F06"] = String( bytes:data[63..<64], encoding:.utf8)
elements["53"] = String( bytes:data[64..<71], encoding:.utf8)
elements["5F07"] = String( bytes:data[71..<72], encoding:.utf8)
}
func parseOther(_ data : [UInt8]) {
elements["5F03"] = String( bytes:data[0..<2], encoding:.utf8)
elements["5F28"] = String( bytes:data[2..<5], encoding:.utf8)
elements["5B"] = String( bytes:data[5..<44], encoding:.utf8)
elements["5A"] = String( bytes:data[44..<53], encoding:.utf8)
elements["5F04"] = String( bytes:[data[53]], encoding:.utf8)
elements["5F2C"] = String( bytes:data[54..<57], encoding:.utf8)
elements["5F57"] = String( bytes:data[57..<63], encoding:.utf8)
elements["5F05"] = String( bytes:[data[63]], encoding:.utf8)
elements["5F35"] = String( bytes:[data[64]], encoding:.utf8)
elements["59"] = String( bytes:data[65..<71], encoding:.utf8)
elements["5F06"] = String( bytes:[data[71]], encoding:.utf8)
elements["53"] = String( bytes:data[72..<86], encoding:.utf8)
elements["5F02"] = String( bytes:[data[86]], encoding:.utf8)
elements["5F07"] = String( bytes:[data[87]], encoding:.utf8)
}
private func getMRZType(length: Int) -> DocTypeEnum {
if length == 0x5A {
return .TD1
}
if length == 0x48 {
return .TD2
}
return .OTHER
}
}

View File

@@ -1,62 +0,0 @@
import Foundation
@available(iOS 13, macOS 10.15, *)
public class DataGroup11 : DataGroup {
public private(set) var fullName : String?
public private(set) var personalNumber : String?
public private(set) var dateOfBirth : String?
public private(set) var placeOfBirth : String?
public private(set) var address : String?
public private(set) var telephone : String?
public private(set) var profession : String?
public private(set) var title : String?
public private(set) var personalSummary : String?
public private(set) var proofOfCitizenship : String?
public private(set) var tdNumbers : String?
public private(set) var custodyInfo : String?
required init( _ data : [UInt8] ) throws {
try super.init(data)
datagroupType = .DG11
}
override func parse(_ data: [UInt8]) throws {
var tag = try getNextTag()
if tag != 0x5C {
throw NFCPassportReaderError.InvalidResponse
}
_ = try getNextValue()
repeat {
tag = try getNextTag()
let val = try String( bytes:getNextValue(), encoding:.utf8)
if tag == 0x5F0E {
fullName = val
} else if tag == 0x5F10 {
personalNumber = val
} else if tag == 0x5F11 {
placeOfBirth = val
} else if tag == 0x5F2B {
dateOfBirth = val
} else if tag == 0x5F42 {
address = val
} else if tag == 0x5F12 {
telephone = val
} else if tag == 0x5F13 {
profession = val
} else if tag == 0x5F14 {
title = val
} else if tag == 0x5F15 {
personalSummary = val
} else if tag == 0x5F16 {
proofOfCitizenship = val
} else if tag == 0x5F17 {
tdNumbers = val
} else if tag == 0x5F18 {
custodyInfo = val
}
} while pos < data.count
}
}

View File

@@ -1,72 +0,0 @@
import Foundation
@available(iOS 13, macOS 10.15, *)
public class DataGroup12 : DataGroup {
public private(set) var issuingAuthority : String?
public private(set) var dateOfIssue : String?
public private(set) var otherPersonsDetails : String?
public private(set) var endorsementsOrObservations : String?
public private(set) var taxOrExitRequirements : String?
public private(set) var frontImage : [UInt8]?
public private(set) var rearImage : [UInt8]?
public private(set) var personalizationTime : String?
public private(set) var personalizationDeviceSerialNr : String?
required init( _ data : [UInt8] ) throws {
try super.init(data)
datagroupType = .DG12
}
override func parse(_ data: [UInt8]) throws {
var tag = try getNextTag()
if tag != 0x5C {
throw NFCPassportReaderError.InvalidResponse
}
// Skip the taglist - ideally we would check this but...
let _ = try getNextValue()
repeat {
tag = try getNextTag()
let val = try getNextValue()
if tag == 0x5F19 {
issuingAuthority = String( bytes:val, encoding:.utf8)
} else if tag == 0x5F26 {
dateOfIssue = parseDateOfIssue(value: val)
} else if tag == 0xA0 {
// Not yet handled
} else if tag == 0x5F1B {
endorsementsOrObservations = String( bytes:val, encoding:.utf8)
} else if tag == 0x5F1C {
taxOrExitRequirements = String( bytes:val, encoding:.utf8)
} else if tag == 0x5F1D {
frontImage = val
} else if tag == 0x5F1E {
rearImage = val
} else if tag == 0x5F55 {
personalizationTime = String( bytes:val, encoding:.utf8)
} else if tag == 0x5F56 {
personalizationDeviceSerialNr = String( bytes:val, encoding:.utf8)
}
} while pos < data.count
}
private func parseDateOfIssue(value: [UInt8]) -> String? {
if value.count == 4 {
return decodeBCD(value: value)
} else {
return decodeASCII(value: value)
}
}
private func decodeASCII(value: [UInt8]) -> String? {
return String(bytes:value, encoding:.utf8)
}
private func decodeBCD(value: [UInt8]) -> String? {
value.map({ String(format: "%02X", $0) }).joined()
}
}

View File

@@ -1,31 +0,0 @@
import Foundation
// SecurityInfos ::= SET of SecurityInfo
// SecurityInfo ::= SEQUENCE {
// protocol OBJECT IDENTIFIER,
// requiredData ANY DEFINED BY protocol,
// optionalData ANY DEFINED BY protocol OPTIONAL
@available(iOS 13, macOS 10.15, *)
public class DataGroup14 : DataGroup {
private var asn1 : ASN1Item!
public private(set) var securityInfos : [SecurityInfo] = [SecurityInfo]()
required init( _ data : [UInt8] ) throws {
try super.init(data)
datagroupType = .DG14
}
override func parse(_ data: [UInt8]) throws {
let p = SimpleASN1DumpParser()
asn1 = try p.parse(data: Data(body))
// Bit of a hack at the moment - passing in the body - if we had a decent ASN1 parser then this would be better! ;)
for i in 0 ..< asn1.getNumberOfChildren() {
if let child = asn1.getChild(i),
let secInfo = SecurityInfo.getInstance( object:child, body : body ) {
securityInfos.append(secInfo)
}
}
}
}

View File

@@ -1,40 +0,0 @@
import Foundation
import OpenSSL
@available(iOS 13, macOS 10.15, *)
public class DataGroup15 : DataGroup {
public private(set) var rsaPublicKey : OpaquePointer?
public private(set) var ecdsaPublicKey : OpaquePointer?
deinit {
if ( ecdsaPublicKey != nil ) {
EVP_PKEY_free(ecdsaPublicKey);
}
if ( rsaPublicKey != nil ) {
EVP_PKEY_free(rsaPublicKey);
}
}
required init( _ data : [UInt8] ) throws {
try super.init(data)
datagroupType = .DG15
}
override func parse(_ data: [UInt8]) throws {
// the public key can either be in EC (elliptic curve) or RSA format
// Try ec first and if this fails try RSA
// Note - this will be improved in a later version to read the ASN1 body to
// check the actual type
if let key = try? OpenSSLUtils.readECPublicKey( data:body ) {
// NOTE We are responsible for freeing the key!
ecdsaPublicKey = key
} else if let key = try? OpenSSLUtils.readRSAPublicKey( data:body ) {
rsaPublicKey = key
}
}
}

View File

@@ -1,164 +0,0 @@
import Foundation
#if !os(macOS)
import UIKit
#endif
@available(iOS 13, macOS 10.15, *)
public class DataGroup2 : DataGroup {
public private(set) var nrImages : Int = 0
public private(set) var versionNumber : Int = 0
public private(set) var lengthOfRecord : Int = 0
public private(set) var numberOfFacialImages : Int = 0
public private(set) var facialRecordDataLength : Int = 0
public private(set) var nrFeaturePoints : Int = 0
public private(set) var gender : Int = 0
public private(set) var eyeColor : Int = 0
public private(set) var hairColor : Int = 0
public private(set) var featureMask : Int = 0
public private(set) var expression : Int = 0
public private(set) var poseAngle : Int = 0
public private(set) var poseAngleUncertainty : Int = 0
public private(set) var faceImageType : Int = 0
public private(set) var imageDataType : Int = 0
public private(set) var imageWidth : Int = 0
public private(set) var imageHeight : Int = 0
public private(set) var imageColorSpace : Int = 0
public private(set) var sourceType : Int = 0
public private(set) var deviceType : Int = 0
public private(set) var quality : Int = 0
public private(set) var imageData : [UInt8] = []
#if !os(macOS)
func getImage() -> UIImage? {
if imageData.count == 0 {
return nil
}
let image = UIImage(data:Data(imageData) )
return image
}
#endif
required init( _ data : [UInt8] ) throws {
try super.init(data)
datagroupType = .DG2
}
override func parse(_ data: [UInt8]) throws {
var tag = try getNextTag()
if tag != 0x7F61 {
throw NFCPassportReaderError.InvalidResponse
}
_ = try getNextLength()
// Tag should be 0x02
tag = try getNextTag()
if tag != 0x02 {
throw NFCPassportReaderError.InvalidResponse
}
nrImages = try Int(getNextValue()[0])
// Next tag is 0x7F60
tag = try getNextTag()
if tag != 0x7F60 {
throw NFCPassportReaderError.InvalidResponse
}
_ = try getNextLength()
// Next tag is 0xA1 (Biometric Header Template) - don't care about this
tag = try getNextTag()
if tag != 0xA1 {
throw NFCPassportReaderError.InvalidResponse
}
_ = try getNextValue()
// Now we get to the good stuff - next tag is either 5F2E or 7F2E
tag = try getNextTag()
if tag != 0x5F2E && tag != 0x7F2E {
throw NFCPassportReaderError.InvalidResponse
}
let value = try getNextValue()
try parseISO19794_5( data:value )
}
func parseISO19794_5( data : [UInt8] ) throws {
// Validate header - 'F', 'A' 'C' 0x00 - 0x46414300
if data[0] != 0x46 && data[1] != 0x41 && data[2] != 0x43 && data[3] != 0x00 {
throw NFCPassportReaderError.InvalidResponse
}
var offset = 4
versionNumber = binToInt(data[offset..<offset+4])
offset += 4
lengthOfRecord = binToInt(data[offset..<offset+4])
offset += 4
numberOfFacialImages = binToInt(data[offset..<offset+2])
offset += 2
facialRecordDataLength = binToInt(data[offset..<offset+4])
offset += 4
nrFeaturePoints = binToInt(data[offset..<offset+2])
offset += 2
gender = binToInt(data[offset..<offset+1])
offset += 1
eyeColor = binToInt(data[offset..<offset+1])
offset += 1
hairColor = binToInt(data[offset..<offset+1])
offset += 1
featureMask = binToInt(data[offset..<offset+3])
offset += 3
expression = binToInt(data[offset..<offset+2])
offset += 2
poseAngle = binToInt(data[offset..<offset+3])
offset += 3
poseAngleUncertainty = binToInt(data[offset..<offset+3])
offset += 3
// Features (not handled). There shouldn't be any but if for some reason there were,
// then we are going to skip over them
// The Feature block is 8 bytes
offset += nrFeaturePoints * 8
faceImageType = binToInt(data[offset..<offset+1])
offset += 1
imageDataType = binToInt(data[offset..<offset+1])
offset += 1
imageWidth = binToInt(data[offset..<offset+2])
offset += 2
imageHeight = binToInt(data[offset..<offset+2])
offset += 2
imageColorSpace = binToInt(data[offset..<offset+1])
offset += 1
sourceType = binToInt(data[offset..<offset+1])
offset += 1
deviceType = binToInt(data[offset..<offset+2])
offset += 2
quality = binToInt(data[offset..<offset+2])
offset += 2
// Make sure that the image data at least has a valid header
// Either JPG or JPEG2000
let jpegHeader : [UInt8] = [0xff,0xd8,0xff,0xe0,0x00,0x10,0x4a,0x46,0x49,0x46]
let jpeg2000BitmapHeader : [UInt8] = [0x00,0x00,0x00,0x0c,0x6a,0x50,0x20,0x20,0x0d,0x0a]
let jpeg2000CodestreamBitmapHeader : [UInt8] = [0xff,0x4f,0xff,0x51]
if data.count < offset+jpeg2000CodestreamBitmapHeader.count {
throw NFCPassportReaderError.UnknownImageFormat
}
if [UInt8](data[offset..<offset+jpegHeader.count]) != jpegHeader &&
[UInt8](data[offset..<offset+jpeg2000BitmapHeader.count]) != jpeg2000BitmapHeader &&
[UInt8](data[offset..<offset+jpeg2000CodestreamBitmapHeader.count]) != jpeg2000CodestreamBitmapHeader {
throw NFCPassportReaderError.UnknownImageFormat
}
imageData = [UInt8](data[offset...])
}
}

View File

@@ -1,44 +0,0 @@
import Foundation
#if !os(macOS)
import UIKit
#endif
@available(iOS 13, macOS 10.15, *)
public class DataGroup7 : DataGroup {
public private(set) var imageData : [UInt8] = []
required init( _ data : [UInt8] ) throws {
try super.init(data)
datagroupType = .DG7
}
#if !os(macOS)
func getImage() -> UIImage? {
if imageData.count == 0 {
return nil
}
let image = UIImage(data:Data(imageData) )
return image
}
#endif
override func parse(_ data: [UInt8]) throws {
var tag = try getNextTag()
if tag != 0x02 {
throw NFCPassportReaderError.InvalidResponse
}
_ = try getNextValue()
tag = try getNextTag()
if tag != 0x5F43 {
throw NFCPassportReaderError.InvalidResponse
}
imageData = try getNextValue()
}
}

View File

@@ -1,98 +0,0 @@
import Foundation
@available(iOS 13, macOS 10.15, *)
public enum DataGroupId : Int, CaseIterable {
case COM = 0x60
case DG1 = 0x61
case DG2 = 0x75
case DG3 = 0x63
case DG4 = 0x76
case DG5 = 0x65
case DG6 = 0x66
case DG7 = 0x67
case DG8 = 0x68
case DG9 = 0x69
case DG10 = 0x6A
case DG11 = 0x6B
case DG12 = 0x6C
case DG13 = 0x6D
case DG14 = 0x6E
case DG15 = 0x6F
case DG16 = 0x70
case SOD = 0x77
case Unknown = 0x00
public func getName() -> String {
switch( self ) {
case .COM: return "COM"
case .DG1: return "DG1"
case .DG2: return "DG2"
case .DG3: return "DG3"
case .DG4: return "DG4"
case .DG5: return "DG5"
case .DG6: return "DG6"
case .DG7: return "DG7"
case .DG8: return "DG8"
case .DG9: return "DG9"
case .DG10: return "DG10"
case .DG11: return "DG11"
case .DG12: return "DG12"
case .DG13: return "DG13"
case .DG14: return "DG14"
case .DG15: return "DG15"
case .DG16: return "DG16"
case .SOD: return "SOD"
case .Unknown: return "Unknown"
}
}
static public func getIDFromName( name: String ) -> DataGroupId {
switch( name ) {
case "COM": return .COM
case "DG1": return .DG1
case "DG2": return .DG2
case "DG3": return .DG3
case "DG4": return .DG4
case "DG5": return .DG5
case "DG6": return .DG6
case "DG7": return .DG7
case "DG8": return .DG8
case "DG9": return .DG9
case "DG10": return .DG10
case "DG11": return .DG11
case "DG12": return .DG12
case "DG13": return .DG13
case "DG14": return .DG14
case "DG15": return .DG15
case "DG16": return .DG16
case "SOD": return .SOD
default: return .Unknown
}
}
func getFileIDTag() -> [UInt8]? {
switch( self ) {
case .COM: return [0x01,0x1E]
case .DG1: return [0x01,0x01]
case .DG2: return [0x01,0x02]
case .DG3: return [0x01,0x03]
case .DG4: return [0x01,0x04]
case .DG5: return [0x01,0x05]
case .DG6: return [0x01,0x06]
case .DG7: return [0x01,0x07]
case .DG8: return [0x01,0x08]
case .DG9: return [0x01,0x09]
case .DG10: return [0x01,0x0A]
case .DG11: return [0x01,0x0B]
case .DG12: return [0x01,0x0C]
case .DG13: return [0x01,0x0D]
case .DG14: return [0x01,0x0E]
case .DG15: return [0x01,0x0F]
case .DG16: return [0x01,0x10]
case .SOD: return [0x01,0x1D]
case .Unknown: return nil
}
}
}

View File

@@ -1,11 +0,0 @@
import Foundation
@available(iOS 13, macOS 10.15, *)
public class NotImplementedDG : DataGroup {
required init( _ data : [UInt8] ) throws {
try super.init(data)
datagroupType = .Unknown
}
}

View File

@@ -1,397 +0,0 @@
import Foundation
import OpenSSL
public enum PACEMappingType {
case GM // Generic Mapping
case IM // Integrated Mapping
case CAM // Chip Authentication Mapping
}
@available(iOS 13, macOS 10.15, *)
public class PACEInfo : SecurityInfo {
// Standardized domain parameters. Based on Table 6.
public static let PARAM_ID_GFP_1024_160 = 0
public static let PARAM_ID_GFP_2048_224 = 1
public static let PARAM_ID_GFP_2048_256 = 2
public static let PARAM_ID_ECP_NIST_P192_R1 = 8
public static let PARAM_ID_ECP_BRAINPOOL_P192_R1 = 9
public static let PARAM_ID_ECP_NIST_P224_R1 = 10
public static let PARAM_ID_ECP_BRAINPOOL_P224_R1 = 11
public static let PARAM_ID_ECP_NIST_P256_R1 = 12
public static let PARAM_ID_ECP_BRAINPOOL_P256_R1 = 13
public static let PARAM_ID_ECP_BRAINPOOL_P320_R1 = 14
public static let PARAM_ID_ECP_NIST_P384_R1 = 15
public static let PARAM_ID_ECP_BRAINPOOL_P384_R1 = 16
public static let PARAM_ID_ECP_BRAINPOOL_P512_R1 = 17
public static let PARAM_ID_ECP_NIST_P521_R1 = 18
static let allowedIdentifiers = [
ID_PACE_DH_GM_3DES_CBC_CBC,
ID_PACE_DH_GM_AES_CBC_CMAC_128,
ID_PACE_DH_GM_AES_CBC_CMAC_192,
ID_PACE_DH_GM_AES_CBC_CMAC_256,
ID_PACE_DH_IM_3DES_CBC_CBC,
ID_PACE_DH_IM_AES_CBC_CMAC_128,
ID_PACE_DH_IM_AES_CBC_CMAC_192,
ID_PACE_DH_IM_AES_CBC_CMAC_256,
ID_PACE_ECDH_GM_3DES_CBC_CBC,
ID_PACE_ECDH_GM_AES_CBC_CMAC_128,
ID_PACE_ECDH_GM_AES_CBC_CMAC_192,
ID_PACE_ECDH_GM_AES_CBC_CMAC_256,
ID_PACE_ECDH_IM_3DES_CBC_CBC,
ID_PACE_ECDH_IM_AES_CBC_CMAC_128,
ID_PACE_ECDH_IM_AES_CBC_CMAC_192,
ID_PACE_ECDH_IM_AES_CBC_CMAC_256,
ID_PACE_ECDH_CAM_AES_CBC_CMAC_128,
ID_PACE_ECDH_CAM_AES_CBC_CMAC_192,
ID_PACE_ECDH_CAM_AES_CBC_CMAC_256]
var oid : String
var version : Int
var parameterId : Int?
static func checkRequiredIdentifier(_ oid : String) -> Bool {
return allowedIdentifiers.contains( oid )
}
init(oid: String, version: Int, parameterId: Int?) {
self.oid = oid
self.version = version
self.parameterId = parameterId
}
public override func getObjectIdentifier() -> String {
return oid
}
public override func getProtocolOIDString() -> String {
return PACEInfo.toProtocolOIDString(oid:oid)
}
public func getVersion() -> Int {
return version
}
public func getParameterId() -> Int? {
return parameterId
}
public func getParameterSpec() throws -> Int32 {
return try PACEInfo.getParameterSpec(stdDomainParam: self.parameterId ?? -1 )
}
public func getMappingType() throws -> PACEMappingType {
return try PACEInfo.toMappingType(oid: oid); // Either GM, CAM, or IM.
}
public func getKeyAgreementAlgorithm() throws -> String {
return try PACEInfo.toKeyAgreementAlgorithm(oid: oid); // Either DH or ECDH.
}
public func getCipherAlgorithm() throws -> String {
return try PACEInfo.toCipherAlgorithm(oid: oid); // Either DESede or AES.
}
public func getDigestAlgorithm() throws -> String {
return try PACEInfo.toDigestAlgorithm(oid: oid); // Either SHA-1 or SHA-256.
}
public func getKeyLength() throws -> Int {
return try PACEInfo.toKeyLength(oid: oid); // Of the enc cipher. Either 128, 192, or 256.
}
/// Caller is required to free the returned EVP_PKEY value
public func createMappingKey( ) throws -> OpaquePointer {
// This will get freed later
let mappingKey : OpaquePointer = EVP_PKEY_new()
switch try getKeyAgreementAlgorithm() {
case "DH":
Log.debug( "Generating DH mapping keys")
//The EVP_PKEY_CTX_set_dh_rfc5114() and EVP_PKEY_CTX_set_dhx_rfc5114() macros are synonymous. They set the DH parameters to the values defined in RFC5114. The rfc5114 parameter must be 1, 2 or 3 corresponding to RFC5114 sections 2.1, 2.2 and 2.3. or 0 to clear the stored value. This macro can be called during parameter generation. The ctx must have a key type of EVP_PKEY_DHX. The rfc5114 parameter and the nid parameter are mutually exclusive.
var dhKey : OpaquePointer? = nil
switch try getParameterSpec() {
case 0:
Log.verbose( "Using DH_get_1024_160" )
dhKey = DH_get_1024_160()
case 1:
Log.verbose( "Using DH_get_2048_224" )
dhKey = DH_get_2048_224()
case 2:
Log.verbose( "Using DH_get_2048_256" )
dhKey = DH_get_2048_256()
default:
// Error
break
}
guard dhKey != nil else {
throw NFCPassportReaderError.InvalidDataPassed("Unable to create DH mapping key")
}
defer{ DH_free( dhKey ) }
DH_generate_key(dhKey)
EVP_PKEY_set1_DH(mappingKey, dhKey)
case "ECDH":
let parameterSpec = try getParameterSpec()
Log.debug( "Generating ECDH mapping keys from parameterSpec - \(parameterSpec)")
guard let ecKey = EC_KEY_new_by_curve_name(parameterSpec) else {
throw NFCPassportReaderError.InvalidDataPassed("Unable to create EC mapping key")
}
defer{ EC_KEY_free( ecKey ) }
EC_KEY_generate_key(ecKey)
EVP_PKEY_set1_EC_KEY(mappingKey, ecKey)
default:
throw NFCPassportReaderError.InvalidDataPassed("Unsupported agreement algorithm")
}
return mappingKey
}
public static func getParameterSpec(stdDomainParam : Int) throws -> Int32 {
switch (stdDomainParam) {
case PARAM_ID_GFP_1024_160:
return 0 // "rfc5114_1024_160";
case PARAM_ID_GFP_2048_224:
return 1 // "rfc5114_2048_224";
case PARAM_ID_GFP_2048_256:
return 2 // "rfc5114_2048_256";
case PARAM_ID_ECP_NIST_P192_R1:
return NID_X9_62_prime192v1 // "secp192r1";
case PARAM_ID_ECP_NIST_P224_R1:
return NID_secp224r1 // "secp224r1";
case PARAM_ID_ECP_NIST_P256_R1:
return NID_X9_62_prime256v1 //"secp256r1";
case PARAM_ID_ECP_NIST_P384_R1:
return NID_secp384r1 // "secp384r1";
case PARAM_ID_ECP_BRAINPOOL_P192_R1:
return NID_brainpoolP192r1 //"brainpoolp192r1";
case PARAM_ID_ECP_BRAINPOOL_P224_R1:
return NID_brainpoolP224r1 // "brainpoolp224r1";
case PARAM_ID_ECP_BRAINPOOL_P256_R1:
return NID_brainpoolP256r1 // "brainpoolp256r1";
case PARAM_ID_ECP_BRAINPOOL_P320_R1:
return NID_brainpoolP320r1 //"brainpoolp320r1";
case PARAM_ID_ECP_BRAINPOOL_P384_R1:
return NID_brainpoolP384r1 //"brainpoolp384r1";
case PARAM_ID_ECP_BRAINPOOL_P512_R1:
return NID_brainpoolP512r1 //"";
case PARAM_ID_ECP_NIST_P521_R1:
return NID_secp521r1 //"secp224r1";
default:
throw NFCPassportReaderError.InvalidDataPassed( "Unable to lookup p arameterSpec - invalid oid" )
}
}
public static func toMappingType( oid : String ) throws -> PACEMappingType {
if ID_PACE_DH_GM_3DES_CBC_CBC == oid
|| ID_PACE_DH_GM_AES_CBC_CMAC_128 == oid
|| ID_PACE_DH_GM_AES_CBC_CMAC_192 == oid
|| ID_PACE_DH_GM_AES_CBC_CMAC_256 == oid
|| ID_PACE_ECDH_GM_3DES_CBC_CBC == oid
|| ID_PACE_ECDH_GM_AES_CBC_CMAC_128 == oid
|| ID_PACE_ECDH_GM_AES_CBC_CMAC_192 == oid
|| ID_PACE_ECDH_GM_AES_CBC_CMAC_256 == oid {
return PACEMappingType.GM
} else if ID_PACE_DH_IM_3DES_CBC_CBC == oid
|| ID_PACE_DH_IM_AES_CBC_CMAC_128 == oid
|| ID_PACE_DH_IM_AES_CBC_CMAC_192 == oid
|| ID_PACE_DH_IM_AES_CBC_CMAC_256 == oid
|| ID_PACE_ECDH_IM_3DES_CBC_CBC == oid
|| ID_PACE_ECDH_IM_AES_CBC_CMAC_128 == oid
|| ID_PACE_ECDH_IM_AES_CBC_CMAC_192 == oid
|| ID_PACE_ECDH_IM_AES_CBC_CMAC_256 == oid {
return PACEMappingType.IM
} else if ID_PACE_ECDH_CAM_AES_CBC_CMAC_128 == oid
|| ID_PACE_ECDH_CAM_AES_CBC_CMAC_192 == oid
|| ID_PACE_ECDH_CAM_AES_CBC_CMAC_256 == oid {
return PACEMappingType.CAM
}
throw NFCPassportReaderError.InvalidDataPassed( "Unable to lookup mapping type - invalid oid" )
}
/// Returns the key agreement algorithm - DH or ECDH for the given Chip Authentication oid
/// - Parameter oid: the object identifier
/// - Returns: key agreement algorithm
/// - Throws: InvalidDataPassed error if invalid oid specified
public static func toKeyAgreementAlgorithm( oid : String ) throws -> String {
if ID_PACE_DH_GM_3DES_CBC_CBC == oid
|| ID_PACE_DH_GM_AES_CBC_CMAC_128 == oid
|| ID_PACE_DH_GM_AES_CBC_CMAC_192 == oid
|| ID_PACE_DH_GM_AES_CBC_CMAC_256 == oid
|| ID_PACE_DH_IM_3DES_CBC_CBC == oid
|| ID_PACE_DH_IM_AES_CBC_CMAC_128 == oid
|| ID_PACE_DH_IM_AES_CBC_CMAC_192 == oid
|| ID_PACE_DH_IM_AES_CBC_CMAC_256 == oid {
return "DH"
} else if ID_PACE_ECDH_GM_3DES_CBC_CBC == oid
|| ID_PACE_ECDH_GM_AES_CBC_CMAC_128 == oid
|| ID_PACE_ECDH_GM_AES_CBC_CMAC_192 == oid
|| ID_PACE_ECDH_GM_AES_CBC_CMAC_256 == oid
|| ID_PACE_ECDH_IM_3DES_CBC_CBC == oid
|| ID_PACE_ECDH_IM_AES_CBC_CMAC_128 == oid
|| ID_PACE_ECDH_IM_AES_CBC_CMAC_192 == oid
|| ID_PACE_ECDH_IM_AES_CBC_CMAC_256 == oid
|| ID_PACE_ECDH_CAM_AES_CBC_CMAC_128 == oid
|| ID_PACE_ECDH_CAM_AES_CBC_CMAC_192 == oid
|| ID_PACE_ECDH_CAM_AES_CBC_CMAC_256 == oid {
return "ECDH"
}
throw NFCPassportReaderError.InvalidDataPassed( "Unable to lookup key agreement algorithm - invalid oid" )
}
/// Returns the cipher algorithm - DESede or AES for the given Chip Authentication oid
/// - Parameter oid: the object identifier
/// - Returns: the cipher algorithm type
/// - Throws: InvalidDataPassed error if invalid oid specified
public static func toCipherAlgorithm( oid : String ) throws -> String {
if ID_PACE_DH_GM_3DES_CBC_CBC == oid
|| ID_PACE_DH_IM_3DES_CBC_CBC == oid
|| ID_PACE_ECDH_GM_3DES_CBC_CBC == oid
|| ID_PACE_ECDH_IM_3DES_CBC_CBC == oid {
return "DESede"
} else if ID_PACE_DH_GM_AES_CBC_CMAC_128 == oid
|| ID_PACE_DH_GM_AES_CBC_CMAC_192 == oid
|| ID_PACE_DH_GM_AES_CBC_CMAC_256 == oid
|| ID_PACE_DH_IM_AES_CBC_CMAC_128 == oid
|| ID_PACE_DH_IM_AES_CBC_CMAC_192 == oid
|| ID_PACE_DH_IM_AES_CBC_CMAC_256 == oid
|| ID_PACE_ECDH_GM_AES_CBC_CMAC_128 == oid
|| ID_PACE_ECDH_GM_AES_CBC_CMAC_192 == oid
|| ID_PACE_ECDH_GM_AES_CBC_CMAC_256 == oid
|| ID_PACE_ECDH_IM_AES_CBC_CMAC_128 == oid
|| ID_PACE_ECDH_IM_AES_CBC_CMAC_192 == oid
|| ID_PACE_ECDH_IM_AES_CBC_CMAC_256 == oid
|| ID_PACE_ECDH_CAM_AES_CBC_CMAC_128 == oid
|| ID_PACE_ECDH_CAM_AES_CBC_CMAC_192 == oid
|| ID_PACE_ECDH_CAM_AES_CBC_CMAC_256 == oid {
return "AES"
}
throw NFCPassportReaderError.InvalidDataPassed( "Unable to lookup cipher algorithm - invalid oid" )
}
public static func toDigestAlgorithm( oid : String ) throws -> String {
if ID_PACE_DH_GM_3DES_CBC_CBC == oid
|| ID_PACE_DH_IM_3DES_CBC_CBC == oid
|| ID_PACE_ECDH_GM_3DES_CBC_CBC == oid
|| ID_PACE_ECDH_IM_3DES_CBC_CBC == oid
|| ID_PACE_DH_GM_AES_CBC_CMAC_128 == oid
|| ID_PACE_DH_IM_AES_CBC_CMAC_128 == oid
|| ID_PACE_ECDH_GM_AES_CBC_CMAC_128 == oid
|| ID_PACE_ECDH_IM_AES_CBC_CMAC_128 == oid
|| ID_PACE_ECDH_CAM_AES_CBC_CMAC_128 == oid {
return "SHA-1"
} else if ID_PACE_DH_GM_AES_CBC_CMAC_192 == oid
|| ID_PACE_DH_IM_AES_CBC_CMAC_192 == oid
|| ID_PACE_ECDH_GM_AES_CBC_CMAC_192 == oid
|| ID_PACE_ECDH_IM_AES_CBC_CMAC_192 == oid
|| ID_PACE_ECDH_CAM_AES_CBC_CMAC_192 == oid
|| ID_PACE_DH_GM_AES_CBC_CMAC_256 == oid
|| ID_PACE_DH_IM_AES_CBC_CMAC_256 == oid
|| ID_PACE_ECDH_GM_AES_CBC_CMAC_256 == oid
|| ID_PACE_ECDH_IM_AES_CBC_CMAC_256 == oid
|| ID_PACE_ECDH_CAM_AES_CBC_CMAC_256 == oid {
return "SHA-256"
}
throw NFCPassportReaderError.InvalidDataPassed( "Unable to lookup digest algorithm - invalid oid" )
}
/// Returns the key length in bits (128, 192, or 256) for the given Chip Authentication oid
/// - Parameter oid: the object identifier
/// - Returns: the key length in bits
/// - Throws: InvalidDataPassed error if invalid oid specified
public static func toKeyLength( oid : String ) throws -> Int {
if ID_PACE_DH_GM_3DES_CBC_CBC == oid
|| ID_PACE_DH_IM_3DES_CBC_CBC == oid
|| ID_PACE_ECDH_GM_3DES_CBC_CBC == oid
|| ID_PACE_ECDH_IM_3DES_CBC_CBC == oid
|| ID_PACE_DH_GM_AES_CBC_CMAC_128 == oid
|| ID_PACE_DH_IM_AES_CBC_CMAC_128 == oid
|| ID_PACE_ECDH_GM_AES_CBC_CMAC_128 == oid
|| ID_PACE_ECDH_IM_AES_CBC_CMAC_128 == oid
|| ID_PACE_ECDH_CAM_AES_CBC_CMAC_128 == oid {
return 128
} else if ID_PACE_DH_GM_AES_CBC_CMAC_192 == oid
|| ID_PACE_ECDH_GM_AES_CBC_CMAC_192 == oid
|| ID_PACE_DH_IM_AES_CBC_CMAC_192 == oid
|| ID_PACE_ECDH_IM_AES_CBC_CMAC_192 == oid
|| ID_PACE_ECDH_CAM_AES_CBC_CMAC_192 == oid {
return 192
} else if ID_PACE_DH_GM_AES_CBC_CMAC_256 == oid
|| ID_PACE_DH_IM_AES_CBC_CMAC_256 == oid
|| ID_PACE_ECDH_GM_AES_CBC_CMAC_256 == oid
|| ID_PACE_ECDH_IM_AES_CBC_CMAC_256 == oid
|| ID_PACE_ECDH_CAM_AES_CBC_CMAC_256 == oid {
return 256
}
throw NFCPassportReaderError.InvalidDataPassed( "Unable to get key length - invalid oid" )
}
private static func toProtocolOIDString(oid : String) -> String {
if ID_PACE_DH_GM_3DES_CBC_CBC == oid {
return "id-PACE-DH-GM-3DES-CBC-CBC"
}
if ID_PACE_DH_GM_AES_CBC_CMAC_128 == oid {
return "id-PACE-DH-GM-AES-CBC-CMAC-128"
}
if ID_PACE_DH_GM_AES_CBC_CMAC_192 == oid {
return "id-PACE-DH-GM-AES-CBC-CMAC-192"
}
if ID_PACE_DH_GM_AES_CBC_CMAC_256 == oid {
return "id-PACE-DH-GM-AES-CBC-CMAC-256"
}
if ID_PACE_DH_IM_3DES_CBC_CBC == oid {
return "id-PACE-DH-IM-3DES-CBC-CBC"
}
if ID_PACE_DH_IM_AES_CBC_CMAC_128 == oid {
return "id-PACE-DH-IM-AES-CBC-CMAC-128"
}
if ID_PACE_DH_IM_AES_CBC_CMAC_192 == oid {
return "id-PACE-DH-IM-AES-CBC-CMAC-192"
}
if ID_PACE_DH_IM_AES_CBC_CMAC_256 == oid {
return "id-PACE_DH-IM-AES-CBC-CMAC-256"
}
if ID_PACE_ECDH_GM_3DES_CBC_CBC == oid {
return "id-PACE-ECDH-GM-3DES-CBC-CBC"
}
if ID_PACE_ECDH_GM_AES_CBC_CMAC_128 == oid {
return "id-PACE-ECDH-GM-AES-CBC-CMAC-128"
}
if ID_PACE_ECDH_GM_AES_CBC_CMAC_192 == oid {
return "id-PACE-ECDH-GM-AES-CBC-CMAC-192"
}
if ID_PACE_ECDH_GM_AES_CBC_CMAC_256 == oid {
return "id-PACE-ECDH-GM-AES-CBC-CMAC-256"
}
if ID_PACE_ECDH_IM_3DES_CBC_CBC == oid {
return "id-PACE-ECDH-IM_3DES-CBC-CBC"
}
if ID_PACE_ECDH_IM_AES_CBC_CMAC_128 == oid {
return "id-PACE-ECDH-IM-AES-CBC-CMAC-128"
}
if ID_PACE_ECDH_IM_AES_CBC_CMAC_192 == oid {
return "id-PACE-ECDH-IM-AES-CBC-CMAC-192"
}
if ID_PACE_ECDH_IM_AES_CBC_CMAC_256 == oid {
return "id-PACE-ECDH-IM-AES-CBC-CMAC-256"
}
if ID_PACE_ECDH_CAM_AES_CBC_CMAC_128 == oid {
return "id-PACE-ECDH-CAM-AES-CBC-CMAC-128"
}
if ID_PACE_ECDH_CAM_AES_CBC_CMAC_192 == oid {
return "id-PACE-ECDH-CAM-AES-CBC-CMAC-192"
}
if ID_PACE_ECDH_CAM_AES_CBC_CMAC_256 == oid {
return "id-PACE-ECDH-CAM-AES-CBC-CMAC-256"
}
return oid
}
}

View File

@@ -1,234 +0,0 @@
import Foundation
import OpenSSL
// Format of SOD: ASN1 - Signed Data (taken from rfc5652 - https://tools.ietf.org/html/rfc5652):
// The SOD is a CMS container of type Signed-data
//
// Note - ideally I'd be using a proper ASN1 parser, however currently there isn't a reliable one for Swift
// and I haven't written on (yet?). So for the moment, I'm relying on the output from ASN1Dump and a
// simple parser for that
//
// Sequence
// Object ID: signedData
// Content: SignedData
// SignedData ::= SEQUENCE {
// INTEGER version CMSVersion,
// SET digestAlgorithms DigestAlgorithmIdentifiers,
// SEQUENCE encapContentInfo EncapsulatedContentInfo,
// certificates [0] IMPLICIT CertificateSet OPTIONAL,
// crls [1] IMPLICIT RevocationInfoChoices OPTIONAL,
// SET signerInfos SignerInfos }
//
// AlgorithmIdentifier ::= SEQUENCE {
// algorithm OBJECT IDENTIFIER,
// parameters ANY OPTIONAL
// }
//
// EncapsulatedContentInfo ::= SEQUENCE {
// eContentType ContentType,
// eContent [0] EXPLICIT OCTET STRING OPTIONAL }
//
// ContentType ::= OBJECT IDENTIFIER
//
// SignerInfos ::= SET OF SignerInfo
//
// SignerInfo ::= SEQUENCE {
// version CMSVersion,
// sid SignerIdentifier,
// digestAlgorithm DigestAlgorithmIdentifier,
// signedAttrs [0] IMPLICIT SignedAttributes OPTIONAL,
// signatureAlgorithm SignatureAlgorithmIdentifier,
// signature SignatureValue,
// unsignedAttrs [1] IMPLICIT UnsignedAttributes OPTIONAL }
//
// SignerIdentifier ::= CHOICE {
// issuerAndSerialNumber IssuerAndSerialNumber,
// subjectKeyIdentifier [0] SubjectKeyIdentifier }
//
// SignedAttributes ::= SET SIZE (1..MAX) OF Attribute
// UnsignedAttributes ::= SET SIZE (1..MAX) OF Attribute
// Attribute ::= SEQUENCE {
// attrType OBJECT IDENTIFIER,
// attrValues SET OF AttributeValue }
// AttributeValue ::= ANY
// SignatureValue ::= OCTET STRING
@available(iOS 13, macOS 10.15, *)
class SOD : DataGroup {
public private(set) var pkcs7CertificateData : [UInt8] = []
private var asn1 : ASN1Item!
private var pubKey : OpaquePointer?
required init( _ data : [UInt8] ) throws {
try super.init(data)
self.pkcs7CertificateData = body
datagroupType = .SOD
}
deinit {
if ( pubKey != nil ) {
EVP_PKEY_free(pubKey);
}
}
override func parse(_ data: [UInt8]) throws {
let p = SimpleASN1DumpParser()
asn1 = try p.parse(data: Data(body))
}
/// Returns the public key from the embedded X509 certificate
/// - Returns pointer to the public key
func getPublicKey( ) throws -> OpaquePointer {
if let key = pubKey {
return key
}
let certs = try OpenSSLUtils.getX509CertificatesFromPKCS7(pkcs7Der:Data(pkcs7CertificateData))
if let key = X509_get_pubkey (certs[0].cert) {
pubKey = key
return key
}
throw OpenSSLError.UnableToExtractSignedDataFromPKCS7("Unable to get public key")
}
/// Extracts the encapsulated content section from a SignedData PKCS7 container (if present)
/// - Returns: The encapsulated content from a PKCS7 container if we could read it
/// - Throws: Error if we can't find or read the encapsulated content
func getEncapsulatedContent() throws -> Data {
guard let signedData = asn1.getChild(1)?.getChild(0),
let encContent = signedData.getChild(2)?.getChild(1),
let content = encContent.getChild(0) else {
throw OpenSSLError.UnableToExtractSignedDataFromPKCS7("Data in invalid format")
}
var sigData : Data?
if content.type.hasPrefix("OCTET STRING" ) {
sigData = Data(hexRepToBin( content.value ))
}
guard let ret = sigData else { throw OpenSSLError.UnableToExtractSignedDataFromPKCS7("noDataReturned") }
return ret
}
/// Gets the digest algorithm used to hash the encapsulated content in the signed data section (if present)
/// - Returns: The digest algorithm used to hash the encapsulated content in the signed data section
/// - Throws: Error if we can't find or read the digest algorithm
func getEncapsulatedContentDigestAlgorithm() throws -> String {
guard let signedData = asn1.getChild(1)?.getChild(0),
let digestAlgo = signedData.getChild(1)?.getChild(0)?.getChild(0) else {
throw OpenSSLError.UnableToExtractSignedDataFromPKCS7("Data in invalid format")
}
return String(digestAlgo.value)
}
/// Gets the signed attributes section (if present)
/// - Returns: the signed attributes section
/// - Throws: Error if we can't find or read the signed attributes
func getSignedAttributes( ) throws -> Data {
// Get the SignedAttributes section.
guard let signedData = asn1.getChild(1)?.getChild(0),
let signerInfo = signedData.getChild(4),
let signedAttrs = signerInfo.getChild(0)?.getChild(3) else {
throw OpenSSLError.UnableToExtractSignedDataFromPKCS7("Data in invalid format")
}
var bytes = [UInt8](self.pkcs7CertificateData[signedAttrs.pos ..< signedAttrs.pos + signedAttrs.headerLen + signedAttrs.length])
// The first byte will be 0xA0 -> as its a explicit tag for a contextual item which we need to convert
// for the hash to calculate correctly
// We know that the actual tag is a SET (0x31) - See section 5.4 of https://tools.ietf.org/html/rfc5652
// So we need to change this from 0xA0 to 0x31
if bytes[0] == 0xA0 {
bytes[0] = 0x31
}
let signedAttribs = Data(bytes)
return signedAttribs
}
/// Gets the message digest from the signed attributes section (if present)
/// - Returns: the message digest
/// - Throws: Error if we can't find or read the message digest
func getMessageDigestFromSignedAttributes( ) throws -> Data {
// For the SOD, the SignedAttributes consists of:
// A Content type Object (which has the value of the attributes content type)
// A messageDigest Object which has the message digest as it value
// We want the messageDigest value
guard let signedData = asn1.getChild(1)?.getChild(0),
let signerInfo = signedData.getChild(4),
let signedAttrs = signerInfo.getChild(0)?.getChild(3) else {
throw OpenSSLError.UnableToExtractSignedDataFromPKCS7("Data in invalid format")
}
// Find the messageDigest in the signedAttributes section
var sigData : Data?
for i in 0 ..< signedAttrs.getNumberOfChildren() {
let attrObj = signedAttrs.getChild(i)
if attrObj?.getChild(0)?.value == "messageDigest" {
if let set = attrObj?.getChild(1),
let digestVal = set.getChild(0) {
if digestVal.type.hasPrefix("OCTET STRING" ) {
sigData = Data(hexRepToBin( digestVal.value ) )
}
}
}
}
guard let messageDigest = sigData else { throw OpenSSLError.UnableToExtractSignedDataFromPKCS7("No messageDigest Returned") }
return messageDigest
}
/// Gets the signature data (if present)
/// - Returns: the signature
/// - Throws: Error if we can't find or read the signature
func getSignature( ) throws -> Data {
guard let signedData = asn1.getChild(1)?.getChild(0),
let signerInfo = signedData.getChild(4),
let signature = signerInfo.getChild(0)?.getChild(5) else {
throw OpenSSLError.UnableToExtractSignedDataFromPKCS7("Data in invalid format")
}
var sigData : Data?
if signature.type.hasPrefix("OCTET STRING" ) {
sigData = Data(hexRepToBin( signature.value ))
}
guard let ret = sigData else { throw OpenSSLError.UnableToExtractSignedDataFromPKCS7("noDataReturned") }
return ret
}
/// Gets the signature algorithm used (if present)
/// - Returns: the signature algorithm used
/// - Throws: Error if we can't find or read the signature algorithm
func getSignatureAlgorithm( ) throws -> String {
guard let signedData = asn1.getChild(1)?.getChild(0),
let signerInfo = signedData.getChild(4),
let signatureAlgo = signerInfo.getChild(0)?.getChild(4)?.getChild(0) else {
throw OpenSSLError.UnableToExtractSignedDataFromPKCS7("Data in invalid format")
}
// Vals I've seen are:
// sha1WithRSAEncryption => default pkcs1
// sha256WithRSAEncryption => default pkcs1
// rsassaPss => pss
return signatureAlgo.value
}
}

View File

@@ -1,130 +0,0 @@
import Foundation
import OpenSSL
@available(iOS 13, macOS 10.15,*)
public class SecurityInfo {
// Active Authentication OID
static let ID_AA_OID = "2.23.136.1.1.5"
// Active Authentication Signature Algorithm OIDS
// Specified in BSI TR 03111 Section 5.2.1.
static let ECDSA_PLAIN_SIGNATURES = "0.4.0.127.0.7.1.1.4.1";
static let ECDSA_PLAIN_SHA1_OID = ECDSA_PLAIN_SIGNATURES + ".1"; // 0.4.0.127.0.7.1.1.4.1.1, ecdsa-plain-SHA1
static let ECDSA_PLAIN_SHA224_OID = ECDSA_PLAIN_SIGNATURES + ".2"; // 0.4.0.127.0.7.1.1.4.1.2, ecdsa-plain-SHA224
static let ECDSA_PLAIN_SHA256_OID = ECDSA_PLAIN_SIGNATURES + ".3"; // 0.4.0.127.0.7.1.1.4.1.3, ecdsa-plain-SHA256
static let ECDSA_PLAIN_SHA384_OID = ECDSA_PLAIN_SIGNATURES + ".4"; // 0.4.0.127.0.7.1.1.4.1.4, ecdsa-plain-SHA384
static let ECDSA_PLAIN_SHA512_OID = ECDSA_PLAIN_SIGNATURES + ".5"; // 0.4.0.127.0.7.1.1.4.1.5, ecdsa-plain-SHA512
static let ECDSA_PLAIN_RIPEMD160_OID = ECDSA_PLAIN_SIGNATURES + ".6"; // 0.4.0.127.0.7.1.1.4.1.6, ecdsa-plain-RIPEMD160
// Chip Authentication Public Key OIDS
static let ID_PK_DH_OID = "0.4.0.127.0.7.2.2.1.1"
static let ID_PK_ECDH_OID = "0.4.0.127.0.7.2.2.1.2"
// Chip Authentication OIDS
static let ID_CA_DH_3DES_CBC_CBC_OID = "0.4.0.127.0.7.2.2.3.1.1"
static let ID_CA_ECDH_3DES_CBC_CBC_OID = "0.4.0.127.0.7.2.2.3.2.1"
static let ID_CA_DH_AES_CBC_CMAC_128_OID = "0.4.0.127.0.7.2.2.3.1.2"
static let ID_CA_DH_AES_CBC_CMAC_192_OID = "0.4.0.127.0.7.2.2.3.1.3"
static let ID_CA_DH_AES_CBC_CMAC_256_OID = "0.4.0.127.0.7.2.2.3.1.4"
static let ID_CA_ECDH_AES_CBC_CMAC_128_OID = "0.4.0.127.0.7.2.2.3.2.2"
static let ID_CA_ECDH_AES_CBC_CMAC_192_OID = "0.4.0.127.0.7.2.2.3.2.3"
static let ID_CA_ECDH_AES_CBC_CMAC_256_OID = "0.4.0.127.0.7.2.2.3.2.4"
// PACE OIDS
static let ID_BSI = "0.4.0.127.0.7"
static let ID_PACE = ID_BSI + ".2.2.4"
static let ID_PACE_DH_GM = ID_PACE + ".1"
static let ID_PACE_DH_GM_3DES_CBC_CBC = ID_PACE_DH_GM + ".1"; // 0.4.0.127.0.7.2.2.4.1.1, id-PACE-DH-GM-3DES-CBC-CBC
static let ID_PACE_DH_GM_AES_CBC_CMAC_128 = ID_PACE_DH_GM + ".2"; // 0.4.0.127.0.7.2.2.4.1.2, id-PACE-DH-GM-AES-CBC-CMAC-128
static let ID_PACE_DH_GM_AES_CBC_CMAC_192 = ID_PACE_DH_GM + ".3"; // 0.4.0.127.0.7.2.2.4.1.3, id-PACE-DH-GM-AES-CBC-CMAC-192
static let ID_PACE_DH_GM_AES_CBC_CMAC_256 = ID_PACE_DH_GM + ".4"; // 0.4.0.127.0.7.2.2.4.1.4, id-PACE-DH-GM-AES-CBC-CMAC-256
static let ID_PACE_ECDH_GM = ID_PACE + ".2"
static let ID_PACE_ECDH_GM_3DES_CBC_CBC = ID_PACE_ECDH_GM + ".1"; // 0.4.0.127.0.7.2.2.4.2.1, id-PACE-ECDH-GM-3DES-CBC-CBC
static let ID_PACE_ECDH_GM_AES_CBC_CMAC_128 = ID_PACE_ECDH_GM + ".2"; // 0.4.0.127.0.7.2.2.4.2.2, id-PACE-ECDH-GM-AES-CBC-CMAC-128
static let ID_PACE_ECDH_GM_AES_CBC_CMAC_192 = ID_PACE_ECDH_GM + ".3"; // 0.4.0.127.0.7.2.2.4.2.3, id-PACE-ECDH-GM-AES-CBC-CMAC-192
static let ID_PACE_ECDH_GM_AES_CBC_CMAC_256 = ID_PACE_ECDH_GM + ".4"; // 0.4.0.127.0.7.2.2.4.2.4, id-PACE-ECDH-GM-AES-CBC-CMAC-256
static let ID_PACE_DH_IM = ID_PACE + ".3"
static let ID_PACE_DH_IM_3DES_CBC_CBC = ID_PACE_DH_IM + ".1"; // 0.4.0.127.0.7.2.2.4.3.1, id-PACE-DH-IM-3DES-CBC-CBC
static let ID_PACE_DH_IM_AES_CBC_CMAC_128 = ID_PACE_DH_IM + ".2"; // 0.4.0.127.0.7.2.2.4.3.2, id-PACE-DH-IM-AES-CBC-CMAC-128
static let ID_PACE_DH_IM_AES_CBC_CMAC_192 = ID_PACE_DH_IM + ".3"; // 0.4.0.127.0.7.2.2.4.3.3, id-PACE-DH-IM-AES-CBC-CMAC-192
static let ID_PACE_DH_IM_AES_CBC_CMAC_256 = ID_PACE_DH_IM + ".4"; // 0.4.0.127.0.7.2.2.4.3.4, id-PACE-DH-IM-AES-CBC-CMAC-256
static let ID_PACE_ECDH_IM = ID_PACE + ".4"
static let ID_PACE_ECDH_IM_3DES_CBC_CBC = ID_PACE_ECDH_IM + ".1"; // 0.4.0.127.0.7.2.2.4.4.1, id-PACE-ECDH-IM-3DES-CBC-CBC
static let ID_PACE_ECDH_IM_AES_CBC_CMAC_128 = ID_PACE_ECDH_IM + ".2"; // 0.4.0.127.0.7.2.2.4.4.2, id-PACE-ECDH-IM-AES-CBC-CMAC-128
static let ID_PACE_ECDH_IM_AES_CBC_CMAC_192 = ID_PACE_ECDH_IM + ".3"; // 0.4.0.127.0.7.2.2.4.4.3, id-PACE-ECDH-IM-AES-CBC-CMAC-192
static let ID_PACE_ECDH_IM_AES_CBC_CMAC_256 = ID_PACE_ECDH_IM + ".4"; // 0.4.0.127.0.7.2.2.4.4.4, id-PACE-ECDH-IM-AES-CBC-CMAC-256
static let ID_PACE_ECDH_CAM = ID_PACE + ".6"
static let ID_PACE_ECDH_CAM_AES_CBC_CMAC_128 = ID_PACE_ECDH_CAM + ".2"; // 0.4.0.127.0.7.2.2.4.6.2, id-PACE-ECDH-CAM-AES-CBC-CMAC-128
static let ID_PACE_ECDH_CAM_AES_CBC_CMAC_192 = ID_PACE_ECDH_CAM + ".3"; // 0.4.0.127.0.7.2.2.4.6.3, id-PACE-ECDH-CAM-AES-CBC-CMAC-192
static let ID_PACE_ECDH_CAM_AES_CBC_CMAC_256 = ID_PACE_ECDH_CAM + ".4"; // 0.4.0.127.0.7.2.2.4.6.4, id-PACE-ECDH-CAM-AES-CBC-CMAC-256
public func getObjectIdentifier() -> String {
preconditionFailure("This method must be overridden")
}
public func getProtocolOIDString() -> String {
preconditionFailure("This method must be overridden")
}
static func getInstance( object : ASN1Item, body: [UInt8] ) -> SecurityInfo? {
let oid = object.getChild(0)?.value ?? ""
let requiredData = object.getChild(1)!
var optionalData : ASN1Item? = nil
if (object.getNumberOfChildren() == 3) {
optionalData = object.getChild(2)
}
if ChipAuthenticationPublicKeyInfo.checkRequiredIdentifier(oid) {
let keyData : [UInt8] = [UInt8](body[requiredData.pos ..< requiredData.pos+requiredData.headerLen+requiredData.length])
var subjectPublicKeyInfo : OpaquePointer? = nil
let _ = keyData.withUnsafeBytes { (ptr) in
var newPtr = ptr.baseAddress?.assumingMemoryBound(to: UInt8.self)
subjectPublicKeyInfo = d2i_PUBKEY(nil, &newPtr, keyData.count)
}
if let subjectPublicKeyInfo = subjectPublicKeyInfo {
if optionalData == nil {
return ChipAuthenticationPublicKeyInfo(oid:oid, pubKey:subjectPublicKeyInfo);
} else {
let keyId = Int(optionalData!.value, radix: 16)
return ChipAuthenticationPublicKeyInfo(oid:oid, pubKey:subjectPublicKeyInfo, keyId: keyId);
}
}
} else if ChipAuthenticationInfo.checkRequiredIdentifier(oid) {
let version = Int(requiredData.value) ?? -1
if let optionalData = optionalData {
let keyId = Int(optionalData.value, radix: 16)
return ChipAuthenticationInfo(oid: oid, version: version, keyId: keyId);
} else {
return ChipAuthenticationInfo(oid: oid, version: version);
}
} else if PACEInfo.checkRequiredIdentifier(oid) {
let version = Int(requiredData.value) ?? -1
var parameterId : Int? = nil
if let optionalData = optionalData {
parameterId = Int(optionalData.value, radix:16)
}
return PACEInfo(oid: oid, version: version, parameterId: parameterId);
} else if ActiveAuthenticationInfo.checkRequiredIdentifier(oid) {
let version = Int(requiredData.value) ?? -1
if let optionalData = optionalData {
return ActiveAuthenticationInfo(oid: oid, version: version, signatureAlgorithmOID: optionalData.value)
} else {
return ActiveAuthenticationInfo(oid: oid, version: version)
}
}
return nil
}
}

View File

@@ -1,50 +0,0 @@
import Foundation
@available(iOS 13, macOS 10.15, *)
public enum NFCViewDisplayMessage {
case requestPresentPassport
case authenticatingWithPassport(Int)
case readingDataGroupProgress(DataGroupId, Int)
case error(NFCPassportReaderError)
case successfulRead
}
@available(iOS 13, macOS 10.15, *)
extension NFCViewDisplayMessage {
public var description: String {
switch self {
case .requestPresentPassport:
return "Hold your iPhone near an NFC enabled passport."
case .authenticatingWithPassport(let progress):
let progressString = handleProgress(percentualProgress: progress)
return "Authenticating with passport.....\n\n\(progressString)"
case .readingDataGroupProgress(let dataGroup, let progress):
let progressString = handleProgress(percentualProgress: progress)
return "Reading \(dataGroup).....\n\n\(progressString)"
case .error(let tagError):
switch tagError {
case NFCPassportReaderError.TagNotValid:
return "Tag not valid."
case NFCPassportReaderError.MoreThanOneTagFound:
return "More than 1 tags was found. Please present only 1 tag."
case NFCPassportReaderError.ConnectionError:
return "Connection error. Please try again."
case NFCPassportReaderError.InvalidMRZKey:
return "MRZ Key not valid for this document."
case NFCPassportReaderError.ResponseError(let description, let sw1, let sw2):
return "Sorry, there was a problem reading the passport. \(description) - (0x\(sw1), 0x\(sw2)"
default:
return "Sorry, there was a problem reading the passport. Please try again"
}
case .successfulRead:
return "Passport read successfully"
}
}
func handleProgress(percentualProgress: Int) -> String {
let p = (percentualProgress/20)
let full = String(repeating: "🟢 ", count: p)
let empty = String(repeating: "⚪️ ", count: 5-p)
return "\(full)\(empty)"
}
}

View File

@@ -1,697 +0,0 @@
import Foundation
import OpenSSL
import CryptoTokenKit
@available(iOS 13, macOS 10.15, *)
public class OpenSSLUtils {
private static var loaded = false
/// Returns any OpenSSL Error as a String
public static func getOpenSSLError() -> String {
guard let out = BIO_new(BIO_s_mem()) else { return "Unknown" }
defer { BIO_free(out) }
ERR_print_errors( out )
let str = OpenSSLUtils.bioToString( bio:out )
return str
}
/// Extracts the contents of a BIO object and returns it as a String
/// - Parameter bio: a Pointer to a BIO buffer
/// - Returns: A string containing the contents of the BIO buffer
static func bioToString( bio : OpaquePointer ) -> String {
let len = BIO_ctrl(bio, BIO_CTRL_PENDING, 0, nil)
var buffer = [CChar](repeating: 0, count: len+1)
BIO_read(bio, &buffer, Int32(len))
// Ensure last value is 0 (null terminated) otherwise we get buffer overflow!
buffer[len] = 0
let ret = String(cString:buffer)
return ret
}
static func X509ToPEM( x509: OpaquePointer ) -> String {
let out = BIO_new(BIO_s_mem())!
defer { BIO_free( out) }
PEM_write_bio_X509(out, x509);
let str = OpenSSLUtils.bioToString( bio:out )
return str
}
static func pubKeyToPEM( pubKey: OpaquePointer ) -> String {
let out = BIO_new(BIO_s_mem())!
defer { BIO_free( out) }
PEM_write_bio_PUBKEY(out, pubKey);
let str = OpenSSLUtils.bioToString( bio:out )
return str
}
static func privKeyToPEM( privKey: OpaquePointer ) -> String {
let out = BIO_new(BIO_s_mem())!
defer { BIO_free( out) }
PEM_write_bio_PrivateKey(out, privKey, nil, nil, 0, nil, nil)
let str = OpenSSLUtils.bioToString( bio:out )
return str
}
static func pkcs7DataToPEM( pkcs7: Data ) -> String {
let inf = BIO_new(BIO_s_mem())!
defer { BIO_free( inf) }
let out = BIO_new(BIO_s_mem())!
defer { BIO_free( out) }
let _ = pkcs7.withUnsafeBytes { (ptr) in
BIO_write(inf, ptr.baseAddress?.assumingMemoryBound(to: Int8.self), Int32(pkcs7.count))
}
guard let p7 = d2i_PKCS7_bio(inf, nil) else { return "" }
defer { PKCS7_free(p7) }
PEM_write_bio_PKCS7(out, p7)
let str = OpenSSLUtils.bioToString( bio:out )
return str
}
/// Extracts a X509 certificate in PEM format from a PKCS7 container
/// - Parameter pkcs7Der: The PKCS7 container in DER format
/// - Returns: The PEM formatted X509 certificate
/// - Throws: A OpenSSLError.UnableToGetX509CertificateFromPKCS7 are thrown for any error
static func getX509CertificatesFromPKCS7( pkcs7Der : Data ) throws -> [X509Wrapper] {
guard let inf = BIO_new(BIO_s_mem()) else { throw OpenSSLError.UnableToGetX509CertificateFromPKCS7("Unable to allocate input buffer") }
defer { BIO_free(inf) }
let _ = pkcs7Der.withUnsafeBytes { (ptr) in
BIO_write(inf, ptr.baseAddress?.assumingMemoryBound(to: Int8.self), Int32(pkcs7Der.count))
}
guard let p7 = d2i_PKCS7_bio(inf, nil) else { throw OpenSSLError.UnableToGetX509CertificateFromPKCS7("Unable to read PKCS7 DER data") }
defer { PKCS7_free(p7) }
var certs : OpaquePointer? = nil
let i = OBJ_obj2nid(p7.pointee.type);
switch (i) {
case NID_pkcs7_signed:
if let sign = p7.pointee.d.sign {
certs = sign.pointee.cert
}
break;
case NID_pkcs7_signedAndEnveloped:
if let signed_and_enveloped = p7.pointee.d.signed_and_enveloped {
certs = signed_and_enveloped.pointee.cert
}
break;
default:
break;
}
var ret = [X509Wrapper]()
if let certs = certs {
let certCount = sk_X509_num(certs)
for i in 0 ..< certCount {
let x = sk_X509_value(certs, i);
if let x509 = X509Wrapper(with:x) {
ret.append( x509 )
}
}
}
return ret
}
/// Checks whether a trust chain can be built up to verify a X509 certificate. A CAFile containing a list of trusted certificates (each in PEM format)
/// is used to build the trust chain.
/// The trusted certificates in this use case are typically from a Countries master list (see the scripts for more informaton on how to prepare this)
/// - Parameter x509Cert: The X509 certificate (in PEM format) to verify
/// - Parameter CAFile: The URL path of a file containing the list of certificates used to try to discover and build a trust chain
/// - Returns: either the X509 issue signing certificate that was used to sign the passed in X509 certificate or an error
static func verifyTrustAndGetIssuerCertificate( x509 : X509Wrapper, CAFile : URL ) -> Result<X509Wrapper, OpenSSLError> {
guard let cert_ctx = X509_STORE_new() else { return .failure(OpenSSLError.UnableToVerifyX509CertificateForSOD("Unable to create certificate store")) }
defer { X509_STORE_free(cert_ctx) }
X509_STORE_set_verify_cb(cert_ctx) { (ok, ctx) -> Int32 in
let cert_error = X509_STORE_CTX_get_error(ctx)
if ok == 0 {
let errVal = X509_verify_cert_error_string(Int(cert_error))
let val = errVal!.withMemoryRebound(to: CChar.self, capacity: 1000) { (ptr) in
return String(cString: ptr)
}
Log.error("error \(cert_error) at \(X509_STORE_CTX_get_error_depth(ctx)) depth lookup:\(val)" )
}
return ok;
}
guard let lookup = X509_STORE_add_lookup(cert_ctx, X509_LOOKUP_file()) else { return .failure(OpenSSLError.UnableToVerifyX509CertificateForSOD("Unable to add lookup to store")) }
// Load masterList.pem file
var rc = X509_LOOKUP_ctrl(lookup, X509_L_FILE_LOAD, CAFile.path, Int(X509_FILETYPE_PEM), nil)
guard let store = X509_STORE_CTX_new() else {
return .failure(OpenSSLError.UnableToVerifyX509CertificateForSOD("Unable to create new X509_STORE_CTX"))
}
defer { X509_STORE_CTX_free(store) }
X509_STORE_set_flags(cert_ctx, 0)
rc = X509_STORE_CTX_init(store, cert_ctx, x509.cert, nil)
if rc == 0 {
return .failure(OpenSSLError.UnableToVerifyX509CertificateForSOD("Unable to initialise X509_STORE_CTX"))
}
// discover and verify X509 certificte chain
let i = X509_verify_cert(store);
if i != 1 {
let err = X509_STORE_CTX_get_error(store)
return .failure(OpenSSLError.UnableToVerifyX509CertificateForSOD("Verification of certificate failed - errorCode \(err)"))
}
// Get chain and issue certificate is the last cert in the chain
let chain = X509_STORE_CTX_get1_chain(store);
let nrCertsInChain = sk_X509_num(chain)
if nrCertsInChain > 1 {
let cert = sk_X509_value(chain, nrCertsInChain-1)
if let certWrapper = X509Wrapper(with: cert) {
return .success( certWrapper )
}
}
return .failure(OpenSSLError.UnableToVerifyX509CertificateForSOD("Unable to get issuer certificate - not found"))
}
/// Verifies the signed data section against the stored certificate and extracts the signed data section from a PKCS7 container (if present and valid)
/// - Parameter pkcs7Der: The PKCS7 container in DER format
/// - Returns: The signed data from a PKCS7 container if we could read it
///
/// - Note: To test from the command line using openssl (NOTE NOT THE default mac version as it doesn't currently support CMS):
/// extract the SOD Base64 from an exported passport (you will need to unescape slashes!) - save this to ppt.b64
/// convert to binary (cat ppt.b64 | base64 -D > ppt.bin
/// extract the der file from the SOD (which includes header) - tail -c+5 ppt.bin > aq.der (blindy discards header)
/// convert der to PEM - openssl pkcs7 -in ppt.der --inform der -out ppt.pem -outform pem
/// verify signature data against included document signing cert - openssl cms -verify -in ppt.pem -inform pem -noverify
/// the -noverify is don't verify against the signers certifcate (as we don' thave that!)
///
/// This should return Verification Successful and the signed data
static func verifyAndReturnSODEncapsulatedDataUsingCMS( sod : SOD ) throws -> Data {
guard let inf = BIO_new(BIO_s_mem()) else { throw OpenSSLError.VerifyAndReturnSODEncapsulatedData("CMS - Unable to allocate input buffer") }
defer { BIO_free(inf) }
guard let out = BIO_new(BIO_s_mem()) else { throw OpenSSLError.VerifyAndReturnSODEncapsulatedData("CMS - Unable to allocate output buffer") }
defer { BIO_free(out) }
let _ = sod.body.withUnsafeBytes { (ptr) in
BIO_write(inf, ptr.baseAddress?.assumingMemoryBound(to: UInt8.self), Int32(sod.body.count))
}
guard let cms = d2i_CMS_bio(inf, nil) else {
throw OpenSSLError.VerifyAndReturnSODEncapsulatedData("CMS - Verification of P7 failed - unable to create CMS")
}
defer { CMS_ContentInfo_free(cms) }
let flags : UInt32 = UInt32(CMS_NO_SIGNER_CERT_VERIFY)
if CMS_verify(cms, nil, nil, nil, out, flags) == 0 {
throw OpenSSLError.VerifyAndReturnSODEncapsulatedData("CMS - Verification of P7 failed - unable to verify signature")
}
Log.debug("Verification successful\n");
let len = BIO_ctrl(out, BIO_CTRL_PENDING, 0, nil)
var buffer = [UInt8](repeating: 0, count: len)
BIO_read(out, &buffer, Int32(len))
let sigData = Data(buffer)
return sigData
}
static func verifyAndReturnSODEncapsulatedData( sod : SOD ) throws -> Data {
let encapsulatedContent = try sod.getEncapsulatedContent()
let signedAttribsHashAlgo = try sod.getEncapsulatedContentDigestAlgorithm()
let signedAttributes = try sod.getSignedAttributes()
let messageDigest = try sod.getMessageDigestFromSignedAttributes()
let signature = try sod.getSignature()
let sigType = try sod.getSignatureAlgorithm()
let pubKey = try sod.getPublicKey()
let mdHash : Data = try Data(calcHash(data: [UInt8](encapsulatedContent), hashAlgorithm: signedAttribsHashAlgo))
// Make sure that hash equals the messageDigest
if messageDigest != mdHash {
// Invalid - signed data hash doesn't match message digest hash
throw OpenSSLError.VerifyAndReturnSODEncapsulatedData("messageDigest Hash doesn't hatch that of the signed attributes")
}
// Verify signed attributes
if !verifySignature( data : [UInt8](signedAttributes), signature : [UInt8](signature), pubKey : pubKey, digestType: sigType ) {
throw OpenSSLError.VerifyAndReturnSODEncapsulatedData("Unable to verify signature for signed attributes")
}
return encapsulatedContent
}
/// Parses a signed data structures encoded in ASN1 format and returns the structure in text format
/// - Parameter data: The data to be parsed in ASN1 format
/// - Returns: The parsed data as A String
static func ASN1Parse( data: Data ) throws -> String {
guard let out = BIO_new(BIO_s_mem()) else { throw OpenSSLError.UnableToParseASN1("Unable to allocate output buffer") }
defer { BIO_free(out) }
var parsed : String = ""
let _ = try data.withUnsafeBytes { (ptr) in
let rc = ASN1_parse_dump(out, ptr.baseAddress?.assumingMemoryBound(to: UInt8.self), data.count, 0, 0)
if rc == 0 {
let str = OpenSSLUtils.getOpenSSLError()
Log.debug( "Failed to parse ASN1 Data - \(str)" )
throw OpenSSLError.UnableToParseASN1("Failed to parse ASN1 Data - \(str)")
}
parsed = bioToString(bio: out)
}
return parsed
}
/// Reads an RSA Public Key in DER format and converts it to an OpenSSL EVP_PKEY value for use whilst decrypting or verifying an RSA signature
/// - Parameter data: The RSA key in DER format
/// - Returns: The EVP_PKEY value
/// NOTE THE CALLER IS RESPONSIBLE FOR FREEING THE RETURNED KEY USING
/// EVP_PKEY_free(pemKey);
static func readRSAPublicKey( data : [UInt8] ) throws -> OpaquePointer? {
guard let inf = BIO_new(BIO_s_mem()) else { throw OpenSSLError.UnableToReadECPublicKey("Unable to allocate output buffer") }
defer { BIO_free(inf) }
let _ = data.withUnsafeBytes { (ptr) in
BIO_write(inf, ptr.baseAddress?.assumingMemoryBound(to: UInt8.self), Int32(data.count))
}
guard let rsakey = d2i_RSA_PUBKEY_bio(inf, nil) else { throw OpenSSLError.UnableToReadECPublicKey("Failed to load") }
defer{ RSA_free(rsakey) }
let key = EVP_PKEY_new()
if EVP_PKEY_set1_RSA(key, rsakey) != 1 {
EVP_PKEY_free(key)
throw OpenSSLError.UnableToReadECPublicKey("Failed to load")
}
return key
}
/// This code is taken pretty much from rsautl.c - to decrypt a signature with a public key
/// NOTE: Current no padding is used! - This seems to be the default for Active Authentication RSA signatures (guess)
/// - Parameter signature: The RSA encrypted signature to decrypt
/// - Parameter pubKey: The RSA Public Key
/// - Returns: The decrypted signature data
static func decryptRSASignature( signature : Data, pubKey : OpaquePointer ) throws -> [UInt8] {
let pad = RSA_NO_PADDING
let rsa = EVP_PKEY_get1_RSA( pubKey )
let keysize = RSA_size(rsa);
var outputBuf = [UInt8](repeating: 0, count: Int(keysize))
// Decrypt signature
var outlen : Int32 = 0
let _ = signature.withUnsafeBytes { (sigPtr) in
let _ = outputBuf.withUnsafeMutableBytes { (outPtr) in
outlen = RSA_public_decrypt(Int32(signature.count), sigPtr.baseAddress?.assumingMemoryBound(to: UInt8.self), outPtr.baseAddress?.assumingMemoryBound(to: UInt8.self), rsa, pad)
}
}
if outlen == 0 {
let error = OpenSSLUtils.getOpenSSLError()
throw OpenSSLError.UnableToDecryptRSASignature( "RSA_public_decrypt failed - \(error)" )
}
return outputBuf
}
/// Reads an ECDSA Public Key in DER format and converts it to an OpenSSL EVP_PKEY value for use whilst verifying a ECDSA signature
/// - Parameter data: The ECDSA key in DER forma
/// - Returns: The EVP_PKEY value
/// NOTE THE CALLER IS RESPONSIBLE FOR FREEING THE RETURNED KEY USING
/// EVP_PKEY_free(pemKey);
static func readECPublicKey( data : [UInt8] ) throws -> OpaquePointer? {
guard let inf = BIO_new(BIO_s_mem()) else { throw OpenSSLError.UnableToReadECPublicKey("Unable to allocate output buffer") }
defer { BIO_free(inf) }
let _ = data.withUnsafeBytes { (ptr) in
BIO_write(inf, ptr.baseAddress?.assumingMemoryBound(to: UInt8.self), Int32(data.count))
}
guard let eckey = d2i_EC_PUBKEY_bio(inf, nil) else { throw OpenSSLError.UnableToReadECPublicKey("Failed to load") }
defer{ EC_KEY_free(eckey) }
guard let outf = BIO_new(BIO_s_mem()) else { throw OpenSSLError.UnableToReadECPublicKey("Unable to allocate output buffer") }
defer { BIO_free(outf) }
let _ = PEM_write_bio_EC_PUBKEY(outf, eckey);
let pemKey = PEM_read_bio_PUBKEY(outf, nil, nil, nil)
return pemKey
}
/// Verifies Active Authentication data valid against an ECDSA signature and ECDSA Public Key - used in Active Authentication
/// - Parameter publicKey: The OpenSSL EVP_PKEY ECDSA key
/// - Parameter signature: The ECDSA signature to verify
/// - Parameter data: The data used to generate the signature
/// - Returns: True if the signature was verified
static func verifyECDSASignature( publicKey:OpaquePointer, signature: [UInt8], data: [UInt8], digestType: String = "" ) -> Bool {
// We first need to convert the signature from PLAIN ECDSA to ASN1 DER encoded
let ecsig = ECDSA_SIG_new()
defer { ECDSA_SIG_free(ecsig) }
let sigData = signature
let l = sigData.count / 2
sigData.withUnsafeBufferPointer { (unsafeBufPtr) in
let unsafePointer = unsafeBufPtr.baseAddress!
let r = BN_bin2bn(unsafePointer, Int32(l), nil)
let s = BN_bin2bn((unsafePointer + l), Int32(l), nil)
ECDSA_SIG_set0(ecsig, r, s)
}
let sigSize = i2d_ECDSA_SIG(ecsig, nil)
var derBytes = [UInt8](repeating: 0, count: Int(sigSize))
derBytes.withUnsafeMutableBufferPointer { (unsafeBufPtr) in
var unsafePointer = unsafeBufPtr.baseAddress
let _ = i2d_ECDSA_SIG(ecsig, &unsafePointer)
}
let rc = verifySignature(data: data, signature: derBytes, pubKey: publicKey, digestType: digestType)
return rc
}
/// Verifies that a signature is valid for some data and a Public Key
/// - Parameter data: The data used to generate the signature
/// - Parameter signature: The signature to verify
/// - Parameter publicKey: The OpenSSL EVP_PKEY key
/// - Parameter digestType: the type of hash to use (empty string to use no digest type)
/// - Returns: True if the signature was verified
static func verifySignature( data : [UInt8], signature : [UInt8], pubKey : OpaquePointer, digestType: String ) -> Bool {
var digest = "sha256"
let digestType = digestType.lowercased()
if digestType.contains( "sha1" ) {
digest = "sha1"
} else if digestType.contains( "sha224" ) {
digest = "sha224"
} else if digestType.contains( "sha256" ) || digestType.contains( "rsassapss" ) {
digest = "sha256"
} else if digestType.contains( "sha384" ) {
digest = "sha384"
} else if digestType.contains( "sha512" ) {
digest = "sha512"
}
// Fix for some invalid ECDSA based signatures
// An ECDSA signature comprises of a Sequence of 2 big integers (R & S) and the verification
// is a linear equation of these two integers, the data hash and the public key
// However, in some passports the encoding of the integers is incorrect and has a leading 00
// causing the verification to fail.
// So in this case, we check to see if it is actually a valid BigInteger, and if not, we remove the
// leading prefix, check again and if a valid big integer this time then we use this otherwise
// we keep the original value
// If we change any values then we re-generate the signature and use this
var fixedSignature = signature
if digestType.contains( "ecdsa" ) {
// Decode signature
if let sequence = TKBERTLVRecord(from:Data(signature)),
sequence.tag == 0x30,
var intRecords = TKBERTLVRecord.sequenceOfRecords(from: sequence.value),
intRecords.count == 2 {
var didFix = false
for (idx, rec) in intRecords.enumerated() {
// Only process if the first byte is a 0
if rec.value[0] != 0 {
continue
}
// There is a feature in TKBERTLVRecord.sequenceOfRecords where the 2nd record.data call
// contains the data for the whole data not the actual record
// (reported as FB9077037)
// So for the moment, work aroud this and create a new record
let fixedRec = TKBERTLVRecord( tag: rec.tag, value: rec.value)
let data = [UInt8](fixedRec.data)
// Check to see if a valid Big Integer (we need the whole record including tag and length for the d2i_ASN1_INTEGER call)
data.withUnsafeBufferPointer { (ptr) in
var address = ptr.baseAddress
let v = d2i_ASN1_INTEGER(nil, &address, data.count)
defer { ASN1_INTEGER_free(v) }
if v == nil {
// Not a valid BigInteger, so remove the first value and try again
let newRec = TKBERTLVRecord( tag: rec.tag, value: rec.value[1...])
let data2 = [UInt8](newRec.data)
data2.withUnsafeBufferPointer { (ptr) in
var address = ptr.baseAddress
let v2 = d2i_ASN1_INTEGER(nil, &address, data2.count)
defer { ASN1_INTEGER_free(v2) }
if v2 != nil {
// OK, we have a valid BigInteger this time so replace the original
// record with the new one
intRecords[idx] = newRec
didFix = true
}
}
}
}
}
// We only reencode if we changed any of the integers, otherwise assume they were actually
// correctly encoded
if didFix {
// re-encode
let newSequence = TKBERTLVRecord( tag: sequence.tag, records: intRecords)
fixedSignature = [UInt8](newSequence.data)
}
}
}
let md = EVP_get_digestbyname(digest)
let ctx = EVP_MD_CTX_new()
var pkey_ctx : OpaquePointer?
defer{ EVP_MD_CTX_free( ctx) }
var nRes = EVP_DigestVerifyInit(ctx, &pkey_ctx, md, nil, pubKey)
if ( nRes != 1 ) {
return false;
}
if digestType.contains( "rsassapss" ) {
EVP_PKEY_CTX_ctrl_str(pkey_ctx, "rsa_padding_mode", "pss" )
EVP_PKEY_CTX_ctrl_str(pkey_ctx, "rsa_pss_saltlen", "auto" )
}
nRes = EVP_DigestUpdate(ctx, data, data.count);
if ( nRes != 1 ) {
return false;
}
nRes = EVP_DigestVerifyFinal(ctx, fixedSignature, fixedSignature.count);
if (nRes != 1) {
return false;
}
return true
}
@available(iOS 13, macOS 10.15, *)
static func generateAESCMAC( key: [UInt8], message : [UInt8] ) -> [UInt8] {
let ctx = CMAC_CTX_new();
defer { CMAC_CTX_free(ctx) }
var key = key
var mac = [UInt8](repeating: 0, count: 32)
var maclen : Int = 0
if key.count == 16 {
CMAC_Init(ctx, &key, key.count, EVP_aes_128_cbc(), nil)
} else if key.count == 24 {
CMAC_Init(ctx, &key, key.count, EVP_aes_192_cbc(), nil)
} else if key.count == 32 {
CMAC_Init(ctx, &key, key.count, EVP_aes_256_cbc(), nil)
}
CMAC_Update(ctx, message, message.count);
CMAC_Final(ctx, &mac, &maclen);
Log.verbose( "aesMac - mac - \(binToHexRep(mac))" )
return [UInt8](mac[0..<maclen])
}
@available(iOS 13, macOS 10.15, *)
static func asn1EncodeOID (oid : String) -> [UInt8] {
let obj = OBJ_txt2obj( oid.cString(using: .utf8), 1)
let payloadLen = i2d_ASN1_OBJECT(obj, nil)
var data = [UInt8](repeating: 0, count: Int(payloadLen))
let _ = data.withUnsafeMutableBytes { (ptr) in
var newPtr = ptr.baseAddress?.assumingMemoryBound(to: UInt8.self)
_ = i2d_ASN1_OBJECT(obj, &newPtr)
}
return data
}
@available(iOS 13, macOS 10.15, *)
public static func getPublicKeyData(from key:OpaquePointer) -> [UInt8]? {
var data : [UInt8] = []
// Get Key type
let v = EVP_PKEY_base_id( key )
if v == EVP_PKEY_DH || v == EVP_PKEY_DHX {
guard let dh = EVP_PKEY_get0_DH(key) else {
return nil
}
var dhPubKey : OpaquePointer?
DH_get0_key(dh, &dhPubKey, nil)
let nrBytes = (BN_num_bits(dhPubKey)+7)/8
data = [UInt8](repeating: 0, count: Int(nrBytes))
_ = BN_bn2bin(dhPubKey, &data)
} else if v == EVP_PKEY_EC {
guard let ec = EVP_PKEY_get0_EC_KEY(key),
let ec_pub = EC_KEY_get0_public_key(ec),
let ec_group = EC_KEY_get0_group(ec) else {
return nil
}
let form = EC_KEY_get_conv_form(ec)
let len = EC_POINT_point2oct(ec_group, ec_pub, form, nil, 0, nil)
data = [UInt8](repeating: 0, count: Int(len))
if len == 0 {
return nil
}
_ = EC_POINT_point2oct(ec_group, ec_pub, form, &data, len, nil)
}
return data
}
// Caller is responsible for freeing the key
@available(iOS 13, macOS 10.15, *)
public static func decodePublicKeyFromBytes(pubKeyData: [UInt8], params: OpaquePointer) -> OpaquePointer? {
var pubKey : OpaquePointer?
let keyType = EVP_PKEY_base_id( params )
if keyType == EVP_PKEY_DH || keyType == EVP_PKEY_DHX {
let dhKey = DH_new()
defer{ DH_free(dhKey) }
// We don't free this as its part of the key!
let bn = BN_bin2bn(pubKeyData, Int32(pubKeyData.count), nil)
DH_set0_key(dhKey, bn, nil)
pubKey = EVP_PKEY_new()
guard EVP_PKEY_set1_DH(pubKey, dhKey) == 1 else {
return nil
}
} else {
let ec = EVP_PKEY_get1_EC_KEY(params)
let group = EC_KEY_get0_group(ec);
let ecp = EC_POINT_new(group);
let key = EC_KEY_new();
defer {
EC_KEY_free(ec)
EC_POINT_free(ecp)
EC_KEY_free(key)
}
// Read EC_Point from public key data
guard EC_POINT_oct2point(group, ecp, pubKeyData, pubKeyData.count, nil) == 1,
EC_KEY_set_group(key, group) == 1,
EC_KEY_set_public_key(key, ecp) == 1 else {
return nil
}
pubKey = EVP_PKEY_new()
guard EVP_PKEY_set1_EC_KEY(pubKey, key) == 1 else {
return nil
}
}
return pubKey
}
public static func computeSharedSecret( privateKeyPair: OpaquePointer, publicKey: OpaquePointer ) -> [UInt8] {
// Oddly it seems that we cant use EVP_PKEY stuff for DH as it uses DTX keys which OpenSSL doesn't quite handle right
// OR I'm misunderstanding something (which is more possible)
// Works fine though for ECDH keys
var secret : [UInt8]
let keyType = EVP_PKEY_base_id( privateKeyPair )
if keyType == EVP_PKEY_DH || keyType == EVP_PKEY_DHX {
// Get bn for public key
let dh = EVP_PKEY_get1_DH(privateKeyPair);
let dh_pub = EVP_PKEY_get1_DH(publicKey)
var bn = BN_new()
DH_get0_key( dh_pub, &bn, nil )
secret = [UInt8](repeating: 0, count: Int(DH_size(dh)))
let len = DH_compute_key(&secret, bn, dh);
Log.verbose( "OpenSSLUtils.computeSharedSecret - DH secret len - \(len)" )
} else {
let ctx = EVP_PKEY_CTX_new(privateKeyPair, nil)
defer{ EVP_PKEY_CTX_free(ctx) }
if EVP_PKEY_derive_init(ctx) != 1 {
// error
Log.error( "ERROR - \(OpenSSLUtils.getOpenSSLError())" )
}
// Set the public key
if EVP_PKEY_derive_set_peer( ctx, publicKey ) != 1 {
// error
Log.error( "ERROR - \(OpenSSLUtils.getOpenSSLError())" )
}
// get buffer length needed for shared secret
var keyLen = 0
if EVP_PKEY_derive(ctx, nil, &keyLen) != 1 {
// Error
Log.error( "ERROR - \(OpenSSLUtils.getOpenSSLError())" )
}
// Derive the shared secret
secret = [UInt8](repeating: 0, count: keyLen)
if EVP_PKEY_derive(ctx, &secret, &keyLen) != 1 {
// Error
Log.error( "ERROR - \(OpenSSLUtils.getOpenSSLError())" )
}
}
return secret
}
}

View File

@@ -1,614 +0,0 @@
import Foundation
import OpenSSL
import CryptoTokenKit
#if !os(macOS)
import CoreNFC
import CryptoKit
@available(iOS 15, *)
private enum PACEHandlerError {
case DHKeyAgreementError(String)
case ECDHKeyAgreementError(String)
var value: String {
switch self {
case .DHKeyAgreementError(let errMsg): return errMsg
case .ECDHKeyAgreementError(let errMsg): return errMsg
}
}
}
@available(iOS 15, *)
extension PACEHandlerError: LocalizedError {
public var errorDescription: String? {
return NSLocalizedString(value, comment: "PACEHandlerError")
}
}
@available(iOS 15, *)
public class PACEHandler {
private static let MRZ_PACE_KEY_REFERENCE : UInt8 = 0x01
private static let CAN_PACE_KEY_REFERENCE : UInt8 = 0x02 // Not currently supported
private static let PIN_PACE_KEY_REFERENCE : UInt8 = 0x03 // Not currently supported
private static let CUK_PACE_KEY_REFERENCE : UInt8 = 0x04 // Not currently supported
var tagReader : TagReader
var paceInfo : PACEInfo
var isPACESupported : Bool = false
var paceError : String = ""
// Params used
private var paceKey : [UInt8] = []
private var paceKeyType : UInt8 = 0
private var paceOID : String = ""
private var parameterSpec : Int32 = -1
private var mappingType : PACEMappingType!
private var agreementAlg : String = ""
private var cipherAlg : String = ""
private var digestAlg : String = ""
private var keyLength : Int = -1
public init(cardAccess : CardAccess, tagReader: TagReader) throws {
self.tagReader = tagReader
guard let pi = cardAccess.paceInfo else {
throw NFCPassportReaderError.NotYetSupported( "PACE not supported" )
}
self.paceInfo = pi
isPACESupported = true
}
public func doPACE( mrzKey : String ) async throws {
guard isPACESupported else {
throw NFCPassportReaderError.NotYetSupported( "PACE not supported" )
}
Log.info( "Performing PACE with \(paceInfo.getProtocolOIDString())" )
paceOID = paceInfo.getObjectIdentifier()
parameterSpec = try paceInfo.getParameterSpec()
mappingType = try paceInfo.getMappingType() // Either GM, CAM, or IM.
agreementAlg = try paceInfo.getKeyAgreementAlgorithm() // Either DH or ECDH.
cipherAlg = try paceInfo.getCipherAlgorithm() // Either DESede or AES.
digestAlg = try paceInfo.getDigestAlgorithm() // Either SHA-1 or SHA-256.
keyLength = try paceInfo.getKeyLength() // Get key length the enc cipher. Either 128, 192, or 256.
paceKeyType = PACEHandler.MRZ_PACE_KEY_REFERENCE
paceKey = try createPaceKey( from: mrzKey )
// Temporary logging
Log.verbose("doPace - inpit parameters" )
Log.verbose("paceOID - \(paceOID)" )
Log.verbose("parameterSpec - \(parameterSpec)" )
Log.verbose("mappingType - \(mappingType!)" )
Log.verbose("agreementAlg - \(agreementAlg)" )
Log.verbose("cipherAlg - \(cipherAlg)" )
Log.verbose("digestAlg - \(digestAlg)" )
Log.verbose("keyLength - \(keyLength)" )
Log.verbose("keyLength - \(mrzKey)" )
Log.verbose("paceKey - \(binToHexRep(paceKey, asArray:true))" )
// First start the initial auth call
_ = try await tagReader.sendMSESetATMutualAuth(oid: paceOID, keyType: paceKeyType)
let decryptedNonce = try await self.doStep1()
let ephemeralParams = try await self.doStep2(passportNonce: decryptedNonce)
let (ephemeralKeyPair, passportPublicKey) = try await self.doStep3KeyExchange(ephemeralParams: ephemeralParams)
let (encKey, macKey) = try await self.doStep4KeyAgreement( pcdKeyPair: ephemeralKeyPair, passportPublicKey: passportPublicKey)
try self.paceCompleted( ksEnc: encKey, ksMac: macKey )
Log.debug("PACE SUCCESSFUL" )
}
/// Handles an error during the PACE process
/// Logs and stoes the error and returns false to the caller
/// - Parameters:
/// - stage: Where in the PACE process the error occurred
/// - error: The error message
func handleError( _ stage: String, _ error: String, needToTerminateGA: Bool = false ) {
Log.error( "PACEHandler: \(stage) - \(error)" )
Log.error( " OpenSSLError: \(OpenSSLUtils.getOpenSSLError())" )
self.paceError = "\(stage) - \(error)"
//self.completedHandler?( false )
/*
if needToTerminateGA {
// This is to fix some passports that don't automatically terminate command chaining!
// No idea if this is the correct way to do it but testing.....
let terminateGA = wrapDO(b:0x83, arr:[0x00])
tagReader.sendGeneralAuthenticate(data:terminateGA, isLast:true, completed: { [weak self] response, error in
self?.completedHandler?( false )
})
} else {
self.completedHandler?( false )
}
*/
}
/// Performs PACE Step 1- receives an encrypted nonce from the passport and decypts it with the PACE key - derived from MRZ, CAN (not yet supported)
func doStep1() async throws -> [UInt8] {
Log.debug("Doing PACE Step1...")
let response = try await tagReader.sendGeneralAuthenticate(data: [], isLast: false)
let data = response.data
let encryptedNonce = try unwrapDO(tag: 0x80, wrappedData: data)
Log.verbose( "Encrypted nonce - \(binToHexRep(encryptedNonce, asArray:true))" )
let decryptedNonce: [UInt8]
if self.cipherAlg == "DESede" {
let iv = [UInt8](repeating:0, count: 8)
decryptedNonce = tripleDESDecrypt(key: self.paceKey, message: encryptedNonce, iv: iv)
} else if self.cipherAlg == "AES" {
let iv = [UInt8](repeating:0, count: 16)
decryptedNonce = AESDecrypt(key: self.paceKey, message: encryptedNonce, iv: iv)
} else {
throw NFCPassportReaderError.UnsupportedCipherAlgorithm
}
Log.verbose( "Decrypted nonce - \(binToHexRep(decryptedNonce, asArray:true) )" )
return decryptedNonce
}
/// Performs PACE Step 2 - computes ephemeral parameters by mapping the nonce received from the passport
/// (and if IM used the nonce generated by us)
///
/// Using the supported
/// - Parameters:
/// - passportNonce: The decrypted nonce received from the passport
func doStep2( passportNonce: [UInt8]) async throws -> OpaquePointer {
Log.debug( "Doing PACE Step2...")
switch(mappingType) {
case .CAM, .GM:
Log.debug( " Using General Mapping (GM)...")
return try await doPACEStep2GM(passportNonce: passportNonce)
case .IM:
Log.debug( " Using Integrated Mapping (IM)...")
return try await doPACEStep2IM(passportNonce: passportNonce)
default:
throw NFCPassportReaderError.PACEError( "Step2GM", "Unsupported Mapping Type" )
}
}
/// Performs PACEStep 2 using Generic Mapping
///
/// Using the supported
/// - Parameters:
/// - passportNonce: The decrypted nonce received from the passport
func doPACEStep2GM(passportNonce : [UInt8]) async throws -> OpaquePointer {
let mappingKey : OpaquePointer
mappingKey = try self.paceInfo.createMappingKey( )
guard let pcdMappingEncodedPublicKey = OpenSSLUtils.getPublicKeyData(from: mappingKey) else {
throw NFCPassportReaderError.PACEError( "Step2GM", "Unable to get public key from mapping key")
}
Log.verbose( "public mapping key - \(binToHexRep(pcdMappingEncodedPublicKey, asArray:true))")
Log.debug( "Sending public mapping key to passport..")
let step2Data = wrapDO(b:0x81, arr:pcdMappingEncodedPublicKey)
let response = try await tagReader.sendGeneralAuthenticate(data:step2Data, isLast:false)
let piccMappingEncodedPublicKey = try unwrapDO(tag: 0x82, wrappedData: response.data)
Log.debug( "Received passports public mapping key")
Log.verbose( " public mapping key - \(binToHexRep(piccMappingEncodedPublicKey, asArray: true))")
// Do mapping agreement
// First, Convert nonce to BIGNUM
guard let bn_nonce = BN_bin2bn(passportNonce, Int32(passportNonce.count), nil) else {
throw NFCPassportReaderError.PACEError( "Step2GM", "Unable to convert picc nonce to bignum" )
}
defer { BN_free(bn_nonce) }
// ephmeralParams are free'd in stage 3
let ephemeralParams : OpaquePointer
if self.agreementAlg == "DH" {
Log.debug( "Doing DH Mapping agreement")
ephemeralParams = try self.doDHMappingAgreement(mappingKey: mappingKey, passportPublicKeyData: piccMappingEncodedPublicKey, nonce: bn_nonce )
} else if self.agreementAlg == "ECDH" {
Log.debug( "Doing ECDH Mapping agreement")
ephemeralParams = try self.doECDHMappingAgreement(mappingKey: mappingKey, passportPublicKeyData: piccMappingEncodedPublicKey, nonce: bn_nonce )
} else {
throw NFCPassportReaderError.PACEError( "Step2GM", "Unsupported agreement algorithm" )
}
// Need to free the mapping key we created now
EVP_PKEY_free(mappingKey)
return ephemeralParams
}
func doPACEStep2IM( passportNonce: [UInt8] ) async throws -> OpaquePointer {
// Not implemented yet
throw NFCPassportReaderError.PACEError( "Step2IM", "IM not yet implemented" )
}
/// Generates an ephemeral public/private key pair based on mapping parameters from step 2, and then sends
/// the public key to the passport and receives its ephmeral public key in exchange
/// - Parameters:
/// - ephemeralParams: The ehpemeral mapping keys generated by step2
/// - Returns:
/// - Tuple of Generated Ephemeral KeyPair and the Passport's public key
func doStep3KeyExchange(ephemeralParams: OpaquePointer) async throws -> (OpaquePointer, OpaquePointer) {
Log.debug( "Doing PACE Step3 - Key Exchange")
// Generate ephemeral keypair from ephemeralParams
var ephKeyPair : OpaquePointer? = nil
let pctx = EVP_PKEY_CTX_new(ephemeralParams, nil)
EVP_PKEY_keygen_init(pctx)
EVP_PKEY_keygen(pctx, &ephKeyPair)
EVP_PKEY_CTX_free(pctx)
guard let ephemeralKeyPair = ephKeyPair else {
throw NFCPassportReaderError.PACEError( "Step3 KeyEx", "Unable to get create ephermeral key pair" )
}
Log.debug( "Generated Ephemeral key pair")
// We've finished with the ephemeralParams now - we can now free it
EVP_PKEY_free( ephemeralParams )
guard let publicKey = OpenSSLUtils.getPublicKeyData( from: ephemeralKeyPair ) else {
throw NFCPassportReaderError.PACEError( "Step3 KeyEx", "Unable to get public key from ephermeral key pair" )
}
Log.verbose( "Ephemeral public key - \(binToHexRep(publicKey, asArray: true))")
// exchange public keys
Log.debug( "Sending ephemeral public key to passport")
let step3Data = wrapDO(b:0x83, arr:publicKey)
let response = try await tagReader.sendGeneralAuthenticate(data:step3Data, isLast:false)
let passportEncodedPublicKey = try? unwrapDO(tag: 0x84, wrappedData: response.data)
guard let passportPublicKey = OpenSSLUtils.decodePublicKeyFromBytes(pubKeyData: passportEncodedPublicKey!, params: ephemeralKeyPair) else {
throw NFCPassportReaderError.PACEError( "Step3 KeyEx", "Unable to decode passports ephemeral key" )
}
Log.verbose( "Received passports ephemeral public key - \(binToHexRep(passportEncodedPublicKey!, asArray: true))" )
return (ephemeralKeyPair, passportPublicKey)
}
/// This performs PACE Step 4 - Key Agreement.
/// Here the shared secret is computed from our ephemeral private key and the passports ephemeral public key
/// The new secure messaging (ksEnc and ksMac) keys are computed from the shared secret
/// An authentication token is generated from the passports public key and the computed ksMac key
/// Then, the authetication token is send to the passport, it returns its own computed authentication token
/// We then compute an expected authentication token from the ksMac key and our ephemeral public key
/// Finally we compare the recieved auth token to the expected token and if they are the same then PACE has succeeded!
/// - Parameters:
/// - pcdKeyPair: our ephemeral key pair
/// - passportPublicKey: passports ephemeral public key
/// - Returns:
/// - Tuple of KSEnc KSMac
func doStep4KeyAgreement( pcdKeyPair: OpaquePointer, passportPublicKey: OpaquePointer) async throws -> ([UInt8], [UInt8]) {
Log.debug( "Doing PACE Step4 Key Agreement...")
Log.debug( "Computing shared secret...")
let sharedSecret = OpenSSLUtils.computeSharedSecret(privateKeyPair: pcdKeyPair, publicKey: passportPublicKey)
Log.verbose( "Shared secret - \(binToHexRep(sharedSecret, asArray:true))")
Log.debug( "Deriving ksEnc and ksMac keys from shared secret")
let gen = SecureMessagingSessionKeyGenerator()
let encKey = try! gen.deriveKey(keySeed: sharedSecret, cipherAlgName: cipherAlg, keyLength: keyLength, mode: .ENC_MODE)
let macKey = try! gen.deriveKey(keySeed: sharedSecret, cipherAlgName: cipherAlg, keyLength: keyLength, mode: .MAC_MODE)
Log.verbose( "encKey - \(binToHexRep(encKey, asArray:true))")
Log.verbose( "macKey - \(binToHexRep(macKey, asArray:true))")
// Step 4 - generate authentication token
Log.debug( "Generating authentication token")
guard let pcdAuthToken = try? generateAuthenticationToken( publicKey: passportPublicKey, macKey: macKey) else {
throw NFCPassportReaderError.PACEError( "Step3 KeyAgreement", "Unable to generate authentication token using passports public key" )
}
Log.verbose( "authentication token - \(pcdAuthToken)")
Log.debug( "Sending auth token to passport")
let step4Data = wrapDO(b:0x85, arr:pcdAuthToken)
let response = try await tagReader.sendGeneralAuthenticate(data:step4Data, isLast:true)
let tvlResp = TKBERTLVRecord.sequenceOfRecords(from: Data(response.data))!
if tvlResp[0].tag != 0x86 {
Log.warning("Was expecting tag 0x86, found: \(binToHex(UInt8(tvlResp[0].tag)))")
}
// Calculate expected authentication token
let expectedPICCToken = try self.generateAuthenticationToken( publicKey: pcdKeyPair, macKey: macKey)
Log.verbose( "Expecting authentication token from passport - \(expectedPICCToken)")
let piccToken = [UInt8](tvlResp[0].value)
Log.verbose( "Received authentication token from passport - \(piccToken)")
guard piccToken == expectedPICCToken else {
Log.error( "Error PICC Token mismatch!\npicToken - \(piccToken)\nexpectedPICCToken - \(expectedPICCToken)" )
throw NFCPassportReaderError.PACEError( "Step3 KeyAgreement", "Error PICC Token mismatch!\npicToken - \(piccToken)\nexpectedPICCToken - \(expectedPICCToken)" )
}
Log.debug( "Auth token from passport matches expected token!" )
// This will be added for CAM when supported
// var encryptedChipAuthenticationData : [UInt8]? = nil
// if (sself.mappingType == PACEMappingType.CAM) {
// if tvlResp[1].tag != 0x8A {
// Log.warning("CAM: Was expecting tag 0x86, found: \(binToHex(UInt8(tvlResp[1].tag)))")
// }
// encryptedChipAuthenticationData = [UInt8](tvlResp[1].value)
// }
// We're done!
return (encKey, macKey)
}
/// Called once PACE has completed with the newly generated ksEnc and ksMac keys for restarting secure messaging
/// - Parameters:
/// - ksEnc: the computed encryption key derived from the key agreement
/// - ksMac: the computed mac key derived from the key agreement
func paceCompleted( ksEnc: [UInt8], ksMac: [UInt8] ) throws {
// Restart secure messaging
let ssc = withUnsafeBytes(of: 0.bigEndian, Array.init)
if (cipherAlg.hasPrefix("DESede")) {
Log.info( "Restarting secure messaging using DESede encryption")
let sm = SecureMessaging(encryptionAlgorithm: .DES, ksenc: ksEnc, ksmac: ksMac, ssc: ssc)
tagReader.secureMessaging = sm
} else if (cipherAlg.hasPrefix("AES")) {
Log.info( "Restarting secure messaging using AES encryption")
let sm = SecureMessaging(encryptionAlgorithm: .AES, ksenc: ksEnc, ksmac: ksMac, ssc: ssc)
tagReader.secureMessaging = sm
} else {
throw NFCPassportReaderError.PACEError( "PACECompleted", "Not restarting secure messaging as unsupported cipher algorithm requested - \(cipherAlg)" )
}
}
}
// MARK - PACEHandler Utility functions
@available(iOS 15, *)
extension PACEHandler {
/// Does the DH key Mapping agreement
/// - Parameter mappingKey - Pointer to an EVP_PKEY structure containing the mapping key
/// - Parameter passportPublicKeyData - byte array containing the publick key read from the passport
/// - Parameter nonce - Pointer to an BIGNUM structure containing the unencrypted nonce
/// - Returns the EVP_PKEY containing the mapped ephemeral parameters
func doDHMappingAgreement( mappingKey : OpaquePointer, passportPublicKeyData: [UInt8], nonce: OpaquePointer ) throws -> OpaquePointer {
guard let dh_mapping_key = EVP_PKEY_get1_DH(mappingKey) else {
// Error
throw PACEHandlerError.DHKeyAgreementError( "Unable to get DH mapping key" )
}
// Compute the shared secret using the mapping key and the passports public mapping key
let bn = BN_bin2bn(passportPublicKeyData, Int32(passportPublicKeyData.count), nil)
defer { BN_free( bn ) }
var secret = [UInt8](repeating: 0, count: Int(DH_size(dh_mapping_key)))
DH_compute_key( &secret, bn, dh_mapping_key)
// Convert the secret to a bignum
let bn_h = BN_bin2bn(secret, Int32(secret.count), nil)
defer { BN_clear_free(bn_h) }
// Initialize ephemeral parameters with parameters from the mapping key
guard let ephemeral_key = DHparams_dup(dh_mapping_key) else {
// Error
throw PACEHandlerError.DHKeyAgreementError("Unable to get initialise ephemeral parameters from DH mapping key")
}
defer{ DH_free(ephemeral_key) }
var p : OpaquePointer? = nil
var q : OpaquePointer? = nil
var g : OpaquePointer? = nil
DH_get0_pqg(dh_mapping_key, &p, &q, &g)
// map to new generator
guard let bn_g = BN_new() else {
throw PACEHandlerError.DHKeyAgreementError( "Unable to create bn_g" )
}
defer{ BN_free(bn_g) }
guard let new_g = BN_new() else {
throw PACEHandlerError.DHKeyAgreementError( "Unable to create new_g" )
}
defer{ BN_free(new_g) }
// bn_g = g^nonce mod p
// ephemeral_key->g = bn_g mod p * h => (g^nonce mod p) * h mod p
let bn_ctx = BN_CTX_new()
guard BN_mod_exp(bn_g, g, nonce, p, bn_ctx) == 1,
BN_mod_mul(new_g, bn_g, bn_h, p, bn_ctx) == 1 else {
// Error
throw PACEHandlerError.DHKeyAgreementError( "Failed to generate new parameters" )
}
guard DH_set0_pqg(ephemeral_key, BN_dup(p), BN_dup(q), BN_dup(new_g)) == 1 else {
// Error
throw PACEHandlerError.DHKeyAgreementError( "Unable to set DH pqg paramerters" )
}
// Set the ephemeral params
guard let ephemeralParams = EVP_PKEY_new() else {
throw PACEHandlerError.ECDHKeyAgreementError( "Unable to create ephemeral params" )
}
guard EVP_PKEY_set1_DH(ephemeralParams, ephemeral_key) == 1 else {
// Error
EVP_PKEY_free( ephemeralParams )
throw PACEHandlerError.DHKeyAgreementError( "Unable to set ephemeral parameters" )
}
return ephemeralParams
}
/// Does the ECDH key Mapping agreement
/// - Parameter mappingKey - Pointer to an EVP_PKEY structure containing the mapping key
/// - Parameter passportPublicKeyData - byte array containing the publick key read from the passport
/// - Parameter nonce - Pointer to an BIGNUM structure containing the unencrypted nonce
/// - Returns the EVP_PKEY containing the mapped ephemeral parameters
func doECDHMappingAgreement( mappingKey : OpaquePointer, passportPublicKeyData: [UInt8], nonce: OpaquePointer ) throws -> OpaquePointer {
let ec_mapping_key = EVP_PKEY_get1_EC_KEY(mappingKey)
guard let group = EC_GROUP_dup(EC_KEY_get0_group(ec_mapping_key)) else {
// Error
throw PACEHandlerError.ECDHKeyAgreementError( "Unable to get EC group" )
}
defer { EC_GROUP_free(group) }
guard let order = BN_new() else {
// Error
throw PACEHandlerError.ECDHKeyAgreementError( "Unable to create order bignum" )
}
defer { BN_free( order ) }
guard let cofactor = BN_new() else {
// error
throw PACEHandlerError.ECDHKeyAgreementError( "Unable to create cofactor bignum" )
}
defer { BN_free( cofactor ) }
guard EC_GROUP_get_order(group, order, nil) == 1 ||
EC_GROUP_get_cofactor(group, cofactor, nil) == 1 else {
// Handle error
throw PACEHandlerError.ECDHKeyAgreementError( "Unable to get order or cofactor from group" )
}
// Create the shared secret in the form of a ECPoint
// Ideally I'd use OpenSSLUtls.computeSharedSecret for this but for reasons as yet unknown, it only returns the first 32 bytes
// NOT the full 64 bytes (would then convert to 65 with e header of 4 for uncompressed)
guard let sharedSecretMappingPoint = self.computeECDHMappingKeyPoint(privateKey: mappingKey, inputKey: passportPublicKeyData) else {
// Error
throw PACEHandlerError.ECDHKeyAgreementError( "Failed to compute new shared secret mapping point from mapping key and passport public mapping key" )
}
defer { EC_POINT_free( sharedSecretMappingPoint ) }
// Map the nonce using Generic mapping to get the new parameters (inc a new generator)
guard let newGenerater = EC_POINT_new(group) else {
throw PACEHandlerError.ECDHKeyAgreementError( "Unable to create new mapping generator point" )
}
defer{ EC_POINT_free(newGenerater) }
// g = (generator * nonce) + (sharedSecretMappingPoint * 1)
guard EC_POINT_mul(group, newGenerater, nonce, sharedSecretMappingPoint, BN_value_one(), nil) == 1 else {
throw PACEHandlerError.ECDHKeyAgreementError( "Failed to map nonce to get new generator params" )
}
// Initialize ephemeral parameters with parameters from the mapping key
guard let ephemeralParams = EVP_PKEY_new() else {
throw PACEHandlerError.ECDHKeyAgreementError( "Unable to create ephemeral params" )
}
let ephemeral_key = EC_KEY_dup(ec_mapping_key)
defer{ EC_KEY_free(ephemeral_key) }
// configure the new EC_KEY
guard EVP_PKEY_set1_EC_KEY(ephemeralParams, ephemeral_key) == 1,
EC_GROUP_set_generator(group, newGenerater, order, cofactor) == 1,
EC_GROUP_check(group, nil) == 1,
EC_KEY_set_group(ephemeral_key, group) == 1 else {
// Error
EVP_PKEY_free( ephemeralParams )
throw PACEHandlerError.ECDHKeyAgreementError( "Unable to configure new ephemeral params" )
}
return ephemeralParams
}
/// Generate Authentication token from a publicKey and and a mac key
/// - Parameters:
/// - publicKey: An EVP_PKEY structure containing a public key data which will be used to generate the auth code
/// - macKey: The mac key derived from the key agreement
/// - Throws: An error if we are unable to encode the public key data
/// - Returns: The authentication token (8 bytes)
func generateAuthenticationToken( publicKey: OpaquePointer, macKey: [UInt8] ) throws -> [UInt8] {
var encodedPublicKeyData = try encodePublicKey(oid:self.paceOID, key:publicKey)
if cipherAlg == "DESede" {
// If DESede (3DES), we need to pad the data
encodedPublicKeyData = pad(encodedPublicKeyData, blockSize: 8)
}
Log.verbose( "Generating Authentication Token" )
Log.verbose( "EncodedPubKey = \(binToHexRep(encodedPublicKeyData, asArray: true))" )
Log.verbose( "macKey = \(binToHexRep(macKey, asArray: true))" )
let maccedPublicKeyDataObject = mac(algoName: cipherAlg == "DESede" ? .DES : .AES, key: macKey, msg: encodedPublicKeyData)
// Take 8 bytes for auth token
let authToken = [UInt8](maccedPublicKeyDataObject[0..<8])
Log.verbose( "Generated authToken = \(binToHexRep(authToken, asArray: true))" )
return authToken
}
/// Encodes a PublicKey as an TLV strucuture based on TR-SAC 1.01 4.5.1 and 4.5.2
/// - Parameters:
/// - oid: The object identifier specifying the key type
/// - key: The ECP_PKEY public key to encode
/// - Throws: Error if unable to encode
/// - Returns: the encoded public key in tlv format
func encodePublicKey( oid : String, key : OpaquePointer ) throws -> [UInt8] {
let encodedOid = oidToBytes(oid:oid, replaceTag: false)
guard let pubKeyData = OpenSSLUtils.getPublicKeyData(from: key) else {
Log.error( "PACEHandler: encodePublicKey() - Unable to get public key data" )
throw NFCPassportReaderError.InvalidDataPassed("Unable to get public key data")
}
let keyType = EVP_PKEY_base_id( key )
let tag : TKTLVTag
if keyType == EVP_PKEY_DH || keyType == EVP_PKEY_DHX {
tag = 0x84
} else {
tag = 0x86
}
guard let encOid = TKBERTLVRecord(from: Data(encodedOid)) else {
throw NFCPassportReaderError.InvalidASN1Value
}
let encPub = TKBERTLVRecord(tag:tag, value: Data(pubKeyData))
let record = TKBERTLVRecord(tag: 0x7F49, records:[encOid, encPub])
let data = record.data
return [UInt8](data)
}
/// Computes a key seed based on an MRZ key
/// - Parameter the mrz key
/// - Returns a encoded key based on the mrz key that can be used for PACE
func createPaceKey( from mrzKey: String ) throws -> [UInt8] {
let buf: [UInt8] = Array(mrzKey.utf8)
let hash = calcSHA1Hash(buf)
let smskg = SecureMessagingSessionKeyGenerator()
let key = try smskg.deriveKey(keySeed: hash, cipherAlgName: cipherAlg, keyLength: keyLength, nonce: nil, mode: .PACE_MODE, paceKeyReference: paceKeyType)
return key
}
/// Performs the ECDH PACE GM key agreement protocol by multiplying a private key with a public key
/// - Parameters:
/// - key: an EVP_PKEY structure containng a ECDH private key
/// - inputKey: a public key
/// - Returns: a new EC_POINT
func computeECDHMappingKeyPoint( privateKey : OpaquePointer, inputKey : [UInt8] ) -> OpaquePointer? {
let ecdh = EVP_PKEY_get1_EC_KEY(privateKey)
defer { EC_KEY_free(ecdh) }
let privateECKey = EC_KEY_get0_private_key(ecdh) // BIGNUM
// decode public key
guard let group = EC_KEY_get0_group(ecdh) else{ return nil }
guard let ecp = EC_POINT_new(group) else { return nil }
defer { EC_POINT_free(ecp) }
guard EC_POINT_oct2point(group, ecp, inputKey, inputKey.count,nil) != 0 else { return nil }
// create our output point
let output = EC_POINT_new(group)
// Multiply our private key with the passports public key to get a new point
EC_POINT_mul(group, output, nil, ecp, privateECKey, nil)
return output
}
}
#endif

View File

@@ -1,405 +0,0 @@
import Foundation
#if !os(macOS)
import UIKit
import CoreNFC
@available(iOS 15, *)
public class PassportReader : NSObject {
private typealias NFCCheckedContinuation = CheckedContinuation<NFCPassportModel, Error>
private var nfcContinuation: NFCCheckedContinuation?
private var passport : NFCPassportModel = NFCPassportModel()
private var readerSession: NFCTagReaderSession?
private var currentlyReadingDataGroup : DataGroupId?
private var dataGroupsToRead : [DataGroupId] = []
private var readAllDatagroups = false
private var skipSecureElements = true
private var skipCA = false
private var skipPACE = false
private var bacHandler : BACHandler?
private var caHandler : ChipAuthenticationHandler?
private var paceHandler : PACEHandler?
private var mrzKey : String = ""
private var dataAmountToReadOverride : Int? = nil
private var scanCompletedHandler: ((NFCPassportModel?, NFCPassportReaderError?)->())!
private var nfcViewDisplayMessageHandler: ((NFCViewDisplayMessage) -> String?)?
private var masterListURL : URL?
private var shouldNotReportNextReaderSessionInvalidationErrorUserCanceled : Bool = false
// By default, Passive Authentication uses the new RFS5652 method to verify the SOD, but can be switched to use
// the previous OpenSSL CMS verification if necessary
public var passiveAuthenticationUsesOpenSSL : Bool = false
public init( logLevel: LogLevel = .info, masterListURL: URL? = nil ) {
super.init()
Log.logLevel = logLevel
self.masterListURL = masterListURL
}
public func setMasterListURL( _ masterListURL : URL ) {
self.masterListURL = masterListURL
}
// This function allows you to override the amount of data the TagReader tries to read from the NFC
// chip. NOTE - this really shouldn't be used for production but is useful for testing as different
// passports support different data amounts.
// It appears that the most reliable is 0xA0 (160 chars) but some will support arbitary reads (0xFF or 256)
public func overrideNFCDataAmountToRead( amount: Int ) {
dataAmountToReadOverride = amount
}
public func readPassport( mrzKey : String, tags : [DataGroupId] = [], skipSecureElements : Bool = true, skipCA : Bool = false, skipPACE : Bool = false, customDisplayMessage : ((NFCViewDisplayMessage) -> String?)? = nil) async throws -> NFCPassportModel {
self.passport = NFCPassportModel()
self.mrzKey = mrzKey
self.skipCA = skipCA
self.skipPACE = skipPACE
self.dataGroupsToRead.removeAll()
self.dataGroupsToRead.append( contentsOf:tags)
self.nfcViewDisplayMessageHandler = customDisplayMessage
self.skipSecureElements = skipSecureElements
self.currentlyReadingDataGroup = nil
self.bacHandler = nil
self.caHandler = nil
self.paceHandler = nil
// If no tags specified, read all
if self.dataGroupsToRead.count == 0 {
// Start off with .COM, will always read (and .SOD but we'll add that after), and then add the others from the COM
self.dataGroupsToRead.append(contentsOf:[.COM, .SOD] )
self.readAllDatagroups = true
} else {
// We are reading specific datagroups
self.readAllDatagroups = false
}
guard NFCNDEFReaderSession.readingAvailable else {
throw NFCPassportReaderError.NFCNotSupported
}
if NFCTagReaderSession.readingAvailable {
readerSession = NFCTagReaderSession(pollingOption: [.iso14443], delegate: self, queue: nil)
self.updateReaderSessionMessage( alertMessage: NFCViewDisplayMessage.requestPresentPassport )
readerSession?.begin()
}
return try await withCheckedThrowingContinuation({ (continuation: NFCCheckedContinuation) in
self.nfcContinuation = continuation
})
}
}
@available(iOS 15, *)
extension PassportReader : NFCTagReaderSessionDelegate {
// MARK: - NFCTagReaderSessionDelegate
public func tagReaderSessionDidBecomeActive(_ session: NFCTagReaderSession) {
// If necessary, you may perform additional operations on session start.
// At this point RF polling is enabled.
Log.debug( "tagReaderSessionDidBecomeActive" )
}
public func tagReaderSession(_ session: NFCTagReaderSession, didInvalidateWithError error: Error) {
// If necessary, you may handle the error. Note session is no longer valid.
// You must create a new session to restart RF polling.
Log.debug( "tagReaderSession:didInvalidateWithError - \(error.localizedDescription)" )
self.readerSession?.invalidate()
self.readerSession = nil
if let readerError = error as? NFCReaderError, readerError.code == NFCReaderError.readerSessionInvalidationErrorUserCanceled
&& self.shouldNotReportNextReaderSessionInvalidationErrorUserCanceled {
self.shouldNotReportNextReaderSessionInvalidationErrorUserCanceled = false
} else {
var userError = NFCPassportReaderError.UnexpectedError
if let readerError = error as? NFCReaderError {
Log.error( "tagReaderSession:didInvalidateWithError - Got NFCReaderError - \(readerError.localizedDescription)" )
switch (readerError.code) {
case NFCReaderError.readerSessionInvalidationErrorUserCanceled:
Log.error( " - User cancelled session" )
userError = NFCPassportReaderError.UserCanceled
default:
Log.error( " - some other error - \(readerError.localizedDescription)" )
userError = NFCPassportReaderError.UnexpectedError
}
} else {
Log.error( "tagReaderSession:didInvalidateWithError - Received error - \(error.localizedDescription)" )
}
nfcContinuation?.resume(throwing: userError)
nfcContinuation = nil
}
}
public func tagReaderSession(_ session: NFCTagReaderSession, didDetect tags: [NFCTag]) {
Log.debug( "tagReaderSession:didDetect - \(tags[0])" )
if tags.count > 1 {
Log.debug( "tagReaderSession:more than 1 tag detected! - \(tags)" )
let errorMessage = NFCViewDisplayMessage.error(.MoreThanOneTagFound)
self.invalidateSession(errorMessage: errorMessage, error: NFCPassportReaderError.MoreThanOneTagFound)
return
}
let tag = tags.first!
var passportTag: NFCISO7816Tag
switch tags.first! {
case let .iso7816(tag):
passportTag = tag
default:
Log.debug( "tagReaderSession:invalid tag detected!!!" )
let errorMessage = NFCViewDisplayMessage.error(NFCPassportReaderError.TagNotValid)
self.invalidateSession(errorMessage:errorMessage, error: NFCPassportReaderError.TagNotValid)
return
}
Task { [passportTag] in
do {
try await session.connect(to: tag)
Log.debug( "tagReaderSession:connected to tag - starting authentication" )
self.updateReaderSessionMessage( alertMessage: NFCViewDisplayMessage.authenticatingWithPassport(0) )
let tagReader = TagReader(tag:passportTag)
if let newAmount = self.dataAmountToReadOverride {
tagReader.overrideDataAmountToRead(newAmount: newAmount)
}
tagReader.progress = { [unowned self] (progress) in
if let dgId = self.currentlyReadingDataGroup {
self.updateReaderSessionMessage( alertMessage: NFCViewDisplayMessage.readingDataGroupProgress(dgId, progress) )
} else {
self.updateReaderSessionMessage( alertMessage: NFCViewDisplayMessage.authenticatingWithPassport(progress) )
}
}
let passportModel = try await self.startReading( tagReader : tagReader)
nfcContinuation?.resume(returning: passportModel)
nfcContinuation = nil
} catch let error as NFCPassportReaderError {
let errorMessage = NFCViewDisplayMessage.error(error)
self.invalidateSession(errorMessage: errorMessage, error: error)
} catch let error {
nfcContinuation?.resume(throwing: error)
nfcContinuation = nil
Log.debug( "tagReaderSession:failed to connect to tag - \(error.localizedDescription)" )
let errorMessage = NFCViewDisplayMessage.error(NFCPassportReaderError.ConnectionError)
self.invalidateSession(errorMessage: errorMessage, error: NFCPassportReaderError.ConnectionError)
}
}
}
func updateReaderSessionMessage(alertMessage: NFCViewDisplayMessage ) {
self.readerSession?.alertMessage = self.nfcViewDisplayMessageHandler?(alertMessage) ?? alertMessage.description
}
}
@available(iOS 15, *)
extension PassportReader {
func startReading(tagReader : TagReader) async throws -> NFCPassportModel {
if !skipPACE {
do {
let data = try await tagReader.readCardAccess()
Log.verbose( "Read CardAccess - data \(binToHexRep(data))" )
let cardAccess = try CardAccess(data)
passport.cardAccess = cardAccess
Log.info( "Starting Password Authenticated Connection Establishment (PACE)" )
let paceHandler = try PACEHandler( cardAccess: cardAccess, tagReader: tagReader )
try await paceHandler.doPACE(mrzKey: mrzKey )
passport.PACEStatus = .success
Log.debug( "PACE Succeeded" )
} catch {
passport.PACEStatus = .failed
Log.error( "PACE Failed - falling back to BAC" )
}
_ = try await tagReader.selectPassportApplication()
}
// If either PACE isn't supported, we failed whilst doing PACE or we didn't even attempt it, then fall back to BAC
if passport.PACEStatus != .success {
try await doBACAuthentication(tagReader : tagReader)
}
// Now to read the datagroups
try await readDataGroups(tagReader: tagReader)
self.updateReaderSessionMessage(alertMessage: NFCViewDisplayMessage.successfulRead)
try await doActiveAuthenticationIfNeccessary(tagReader : tagReader)
self.shouldNotReportNextReaderSessionInvalidationErrorUserCanceled = true
self.readerSession?.invalidate()
// If we have a masterlist url set then use that and verify the passport now
self.passport.verifyPassport(masterListURL: self.masterListURL, useCMSVerification: self.passiveAuthenticationUsesOpenSSL)
return self.passport
}
func doActiveAuthenticationIfNeccessary( tagReader : TagReader) async throws {
guard self.passport.activeAuthenticationSupported else {
return
}
Log.info( "Performing Active Authentication" )
let challenge = generateRandomUInt8Array(8)
Log.verbose( "Generated Active Authentication challange - \(binToHexRep(challenge))")
let response = try await tagReader.doInternalAuthentication(challenge: challenge)
self.passport.verifyActiveAuthentication( challenge:challenge, signature:response.data )
}
func doBACAuthentication(tagReader : TagReader) async throws {
self.currentlyReadingDataGroup = nil
Log.info( "Starting Basic Access Control (BAC)" )
self.passport.BACStatus = .failed
self.bacHandler = BACHandler( tagReader: tagReader )
try await bacHandler?.performBACAndGetSessionKeys( mrzKey: mrzKey )
Log.info( "Basic Access Control (BAC) - SUCCESS!" )
self.passport.BACStatus = .success
}
func readDataGroups( tagReader: TagReader ) async throws {
// Read COM
var DGsToRead = [DataGroupId]()
self.updateReaderSessionMessage( alertMessage: NFCViewDisplayMessage.readingDataGroupProgress(.COM, 0) )
if let com = try await readDataGroup(tagReader:tagReader, dgId:.COM) as? COM {
self.passport.addDataGroup( .COM, dataGroup:com )
// SOD and COM shouldn't be present in the DG list but just in case (worst case here we read the sod twice)
DGsToRead = [.SOD] + com.dataGroupsPresent.map { DataGroupId.getIDFromName(name:$0) }
DGsToRead.removeAll { $0 == .COM }
}
if DGsToRead.contains( .DG14 ) {
DGsToRead.removeAll { $0 == .DG14 }
if !skipCA {
// Do Chip Authentication
if let dg14 = try await readDataGroup(tagReader:tagReader, dgId:.DG14) as? DataGroup14 {
self.passport.addDataGroup( .DG14, dataGroup:dg14 )
let caHandler = ChipAuthenticationHandler(dg14: dg14, tagReader: tagReader)
if caHandler.isChipAuthenticationSupported {
do {
// Do Chip authentication and then continue reading datagroups
try await caHandler.doChipAuthentication()
self.passport.chipAuthenticationStatus = .success
} catch {
Log.info( "Chip Authentication failed - re-establishing BAC")
self.passport.chipAuthenticationStatus = .failed
// Failed Chip Auth, need to re-establish BAC
try await doBACAuthentication(tagReader: tagReader)
}
}
}
}
}
// If we are skipping secure elements then remove .DG3 and .DG4
if self.skipSecureElements {
DGsToRead = DGsToRead.filter { $0 != .DG3 && $0 != .DG4 }
}
if self.readAllDatagroups != true {
DGsToRead = DGsToRead.filter { dataGroupsToRead.contains($0) }
}
for dgId in DGsToRead {
self.updateReaderSessionMessage( alertMessage: NFCViewDisplayMessage.readingDataGroupProgress(dgId, 0) )
if let dg = try await readDataGroup(tagReader:tagReader, dgId:dgId) {
self.passport.addDataGroup( dgId, dataGroup:dg )
}
}
}
func readDataGroup( tagReader : TagReader, dgId : DataGroupId ) async throws -> DataGroup? {
self.currentlyReadingDataGroup = dgId
Log.info( "Reading tag - \(dgId)" )
var readAttempts = 0
self.updateReaderSessionMessage( alertMessage: NFCViewDisplayMessage.readingDataGroupProgress(dgId, 0) )
repeat {
do {
let response = try await tagReader.readDataGroup(dataGroup:dgId)
let dg = try DataGroupParser().parseDG(data: response)
return dg
} catch let error as NFCPassportReaderError {
Log.error( "TagError reading tag - \(error)" )
// OK we had an error - depending on what happened, we may want to try to re-read this
// E.g. we failed to read the last Datagroup because its protected and we can't
let errMsg = error.value
Log.error( "ERROR - \(errMsg)" )
var redoBAC = false
if errMsg == "Session invalidated" || errMsg == "Class not supported" || errMsg == "Tag connection lost" {
// Check if we have done Chip Authentication, if so, set it to nil and try to redo BAC
if self.caHandler != nil {
self.caHandler = nil
redoBAC = true
} else {
// Can't go any more!
throw error
}
} else if errMsg == "Security status not satisfied" || errMsg == "File not found" {
// Can't read this element as we aren't allowed - remove it and return out so we re-do BAC
self.dataGroupsToRead.removeFirst()
redoBAC = true
} else if errMsg == "SM data objects incorrect" || errMsg == "Class not supported" {
// Can't read this element security objects now invalid - and return out so we re-do BAC
redoBAC = true
} else if errMsg.hasPrefix( "Wrong length" ) || errMsg.hasPrefix( "End of file" ) { // Should now handle errors 0x6C xx, and 0x67 0x00
// OK passport can't handle max length so drop it down
tagReader.reduceDataReadingAmount()
redoBAC = true
}
if redoBAC {
// Redo BAC and try again
try await doBACAuthentication(tagReader : tagReader)
} else {
// Some other error lets have another try
}
}
readAttempts += 1
} while ( readAttempts < 2 )
return nil
}
func invalidateSession(errorMessage: NFCViewDisplayMessage, error: NFCPassportReaderError) {
// Mark the next 'invalid session' error as not reportable (we're about to cause it by invalidating the
// session). The real error is reported back with the call to the completed handler
self.shouldNotReportNextReaderSessionInvalidationErrorUserCanceled = true
self.readerSession?.invalidate(errorMessage: self.nfcViewDisplayMessageHandler?(errorMessage) ?? errorMessage.description)
nfcContinuation?.resume(throwing: error)
nfcContinuation = nil
}
}
#endif

View File

@@ -1,17 +0,0 @@
#if !os(macOS)
@available(iOS 13, *)
public struct ResponseAPDU {
public var data : [UInt8]
public var sw1 : UInt8
public var sw2 : UInt8
public init(data: [UInt8], sw1: UInt8, sw2: UInt8) {
self.data = data
self.sw1 = sw1
self.sw2 = sw2
}
}
#endif

View File

@@ -1,292 +0,0 @@
import Foundation
public enum SecureMessagingSupportedAlgorithms {
case DES
case AES
}
#if !os(macOS)
import CoreNFC
/// This class implements the secure messaging protocol.
/// The class is a new layer that comes between the reader and the iso7816.
/// It gives a new transmit method that takes an APDU object formed by the iso7816 layer,
/// ciphers it following the doc9303 specification, sends the ciphered APDU to the reader
/// layer and returns the unciphered APDU.
@available(iOS 13, *)
public class SecureMessaging {
private var ksenc : [UInt8]
private var ksmac : [UInt8]
private var ssc : [UInt8]
private let algoName : SecureMessagingSupportedAlgorithms
private let padLength : Int
public init( encryptionAlgorithm : SecureMessagingSupportedAlgorithms = .DES, ksenc : [UInt8], ksmac : [UInt8], ssc : [UInt8]) {
self.ksenc = ksenc
self.ksmac = ksmac
self.ssc = ssc
self.algoName = encryptionAlgorithm
self.padLength = algoName == .DES ? 8 : 16
}
/// Protect the apdu following the doc9303 specification
func protect(apdu : NFCISO7816APDU ) throws -> NFCISO7816APDU {
Log.verbose("\t\tSSC: " + binToHexRep(self.ssc))
self.ssc = self.incSSC()
let paddedSSC = algoName == .DES ? self.ssc : [UInt8](repeating: 0, count: 8) + ssc
Log.verbose("\tIncrement SSC with 1")
Log.verbose("\t\tSSC: " + binToHexRep(self.ssc))
let cmdHeader = self.maskClassAndPad(apdu: apdu)
var do87 : [UInt8] = []
var do97 : [UInt8] = []
var tmp = "Concatenate CmdHeader"
if apdu.data != nil {
tmp += " and DO87"
do87 = try self.buildD087(apdu: apdu)
}
let isMSE = apdu.instructionCode == 0x22
if apdu.expectedResponseLength > 0 && (isMSE ? apdu.expectedResponseLength < 256 : true) {
tmp += " and DO97"
do97 = try self.buildD097(apdu: apdu)
}
let M = cmdHeader + do87 + do97
Log.verbose(tmp)
Log.verbose("\tM: " + binToHexRep(M))
Log.verbose("Compute MAC of M")
let N = pad(paddedSSC + M, blockSize:padLength)
Log.verbose("\tConcatenate SSC and M and add padding")
Log.verbose("\t\tN: " + binToHexRep(N))
var CC = mac(algoName: algoName, key: self.ksmac, msg: N)
if CC.count > 8 {
CC = [UInt8](CC[0..<8])
}
Log.verbose("\tCompute MAC over N with KSmac")
Log.verbose("\t\tCC: " + binToHexRep(CC))
let do8e = self.buildD08E(mac: CC)
// If dataSize > 255 then it will be encoded in 3 bytes with the first byte being 0x00
// otherwise its a single byte of size
let size = do87.count + do97.count + do8e.count
var dataSize: [UInt8]
if size > 255 {
dataSize = [0x00] + intToBin(size, pad: 4)
} else {
dataSize = intToBin(size)
}
var protectedAPDU = [UInt8](cmdHeader[0..<4]) + dataSize
protectedAPDU += do87 + do97 + do8e
// If the data is more that 255, specify the we are using extended length (0x00, 0x00)
// Thanks to @filom for the fix!
if size > 255 {
protectedAPDU += [0x00,0x00]
} else {
protectedAPDU += [0x00]
}
Log.verbose("Construct and send protected APDU")
Log.verbose("\tProtectedAPDU: " + binToHexRep(protectedAPDU))
let newAPDU = NFCISO7816APDU(data:Data(protectedAPDU))!
return newAPDU
}
/// Unprotect the APDU following the iso7816 specification
func unprotect(rapdu : ResponseAPDU ) throws -> ResponseAPDU {
var needCC = false
var do87 : [UInt8] = []
var do87Data : [UInt8] = []
var do99 : [UInt8] = []
//var do8e : [UInt8] = []
var offset = 0
self.ssc = self.incSSC()
let paddedSSC = algoName == .DES ? self.ssc : [UInt8](repeating: 0, count: 8) + ssc
Log.verbose("\tIncrement SSC with 1")
Log.verbose("\t\tSSC: " + binToHexRep(self.ssc))
// Check for a SM error
if(rapdu.sw1 != 0x90 || rapdu.sw2 != 0x00) {
return rapdu
}
let rapduBin = rapdu.data + [rapdu.sw1, rapdu.sw2]
Log.verbose("Receive response APDU of MRTD's chip")
Log.verbose("\tRAPDU: " + binToHexRep(rapduBin))
// DO'87'
// Mandatory if data is returned, otherwise absent
if rapduBin[0] == 0x87 {
let (encDataLength, o) = try asn1Length([UInt8](rapduBin[1...]))
offset = 1 + o
if rapduBin[offset] != 0x1 {
throw NFCPassportReaderError.D087Malformed
}
do87 = [UInt8](rapduBin[0 ..< offset + Int(encDataLength)])
do87Data = [UInt8](rapduBin[offset+1 ..< offset + Int(encDataLength)])
offset += Int(encDataLength)
needCC = true
}
//DO'99'
// Mandatory, only absent if SM error occurs
guard rapduBin.count >= offset + 5 else {
print("size error")
let returnSw1 = (rapduBin.count >= offset+3) ? rapduBin[offset+2] : 0;
let returnSw2 = (rapduBin.count >= offset+4) ? rapduBin[offset+3] : 0;
return ResponseAPDU(data: [], sw1: returnSw1, sw2: returnSw2);
}
do99 = [UInt8](rapduBin[offset..<offset+4])
let sw1 = rapduBin[offset+2]
let sw2 = rapduBin[offset+3]
offset += 4
needCC = true
if do99[0] != 0x99 && do99[1] != 0x02 {
//SM error, return the error code
return ResponseAPDU(data: [], sw1: sw1, sw2: sw2)
}
// DO'8E'
//Mandatory if DO'87' and/or DO'99' is present
if rapduBin[offset] == 0x8E {
let ccLength : Int = Int(binToHex(rapduBin[offset+1]))
let CC = [UInt8](rapduBin[offset+2 ..< offset+2+ccLength])
// do8e = [UInt8](rapduBin[offset ..< offset+2+ccLength])
// CheckCC
var tmp = ""
if do87.count > 0 {
tmp += " DO'87"
}
if do99.count > 0 {
tmp += " DO'99"
}
Log.verbose("Verify RAPDU CC by computing MAC of" + tmp)
let K = pad(paddedSSC + do87 + do99, blockSize:padLength)
Log.verbose("\tConcatenate SSC and" + tmp + " and add padding")
Log.verbose("\t\tK: " + binToHexRep(K))
Log.verbose("\tCompute MAC with KSmac")
var CCb = mac(algoName: algoName, key: self.ksmac, msg: K)
if CCb.count > 8 {
CCb = [UInt8](CC[0..<8])
}
Log.verbose("\t\tCC: " + binToHexRep(CCb))
let res = (CC == CCb)
Log.verbose("\tCompare CC with data of DO'8E of RAPDU")
Log.verbose("\t\t\(binToHexRep(CC)) == \(binToHexRep(CCb)) ? \(res)")
if !res {
throw NFCPassportReaderError.InvalidResponseChecksum
}
}
else if needCC {
throw NFCPassportReaderError.MissingMandatoryFields
}
var data : [UInt8] = []
if do87Data.count > 0 {
let dec : [UInt8]
if algoName == .DES {
dec = tripleDESDecrypt(key: self.ksenc, message: do87Data, iv: [0,0,0,0,0,0,0,0])
} else {
// for AES the IV is the ssc with AES/EBC/NOPADDING
let paddedssc = [UInt8](repeating: 0, count: 8) + ssc
let iv = AESECBEncrypt(key: ksenc, message: paddedssc)
dec = AESDecrypt(key: self.ksenc, message: do87Data, iv: iv)
}
// There is a payload
data = unpad(dec)
Log.verbose("Decrypt data of DO'87 with KSenc")
Log.verbose("\tDecryptedData: " + binToHexRep(data))
}
Log.verbose("Unprotected APDU: [\(binToHexRep(data))] \(binToHexRep(sw1)) \(binToHexRep(sw2))" )
return ResponseAPDU(data: data, sw1: sw1, sw2: sw2)
}
func maskClassAndPad(apdu : NFCISO7816APDU ) -> [UInt8] {
Log.verbose("Mask class byte and pad command header")
let res = pad([0x0c, apdu.instructionCode, apdu.p1Parameter, apdu.p2Parameter], blockSize: padLength)
Log.verbose("\tCmdHeader: " + binToHexRep(res))
return res
}
func buildD087(apdu : NFCISO7816APDU) throws -> [UInt8] {
let cipher = [0x01] + self.padAndEncryptData(apdu)
let res = try [0x87] + toAsn1Length(cipher.count) + cipher
Log.verbose("Build DO'87")
Log.verbose("\tDO87: " + binToHexRep(res))
return res
}
func padAndEncryptData(_ apdu : NFCISO7816APDU) -> [UInt8] {
// Pad the data, encrypt data with KSenc and build DO'87
let data = [UInt8](apdu.data!)
let paddedData = pad( data, blockSize: padLength )
let enc : [UInt8]
if algoName == .DES {
enc = tripleDESEncrypt(key: self.ksenc, message: paddedData, iv: [0,0,0,0,0,0,0,0])
} else {
// for AES the IV is the ssc with AES/EBC/NOPADDING
let paddedssc = [UInt8](repeating: 0, count: 8) + ssc
let iv = AESECBEncrypt(key: ksenc, message: paddedssc)
enc = AESEncrypt(key: self.ksenc, message: paddedData, iv: iv)
}
Log.verbose("Pad data")
Log.verbose("\tData: " + binToHexRep(paddedData))
Log.verbose("Encrypt data with KSenc")
Log.verbose("\tEncryptedData: " + binToHexRep(enc))
return enc
}
func incSSC() -> [UInt8] {
let val = binToHex(self.ssc) + 1
// This needs to be fully zero padded - to 8 bytes = i.e. if SSC is 1 it should return [0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1]
// NOT [0x1]
return withUnsafeBytes(of: val.bigEndian, Array.init)
}
func buildD08E(mac : [UInt8]) -> [UInt8] {
let res : [UInt8] = [0x8E, UInt8(mac.count)] + mac
Log.verbose("Build DO'8E")
Log.verbose("\tDO8E: \(binToHexRep(res))" )
return res
}
func buildD097(apdu : NFCISO7816APDU) throws -> [UInt8] {
let le = apdu.expectedResponseLength
var binLe = intToBin(le)
if (le == 256 || le == 65536) {
binLe = [0x00] + (le > 256 ? [0x00] : [])
}
let res : [UInt8] = try [0x97] + toAsn1Length(binLe.count) + binLe
Log.verbose("Build DO'97")
Log.verbose("\tDO97: \(res)")
return res
}
}
#endif

View File

@@ -1,149 +0,0 @@
import Foundation
import CryptoKit
@available(iOS 13, macOS 10.15, *)
class SecureMessagingSessionKeyGenerator {
static let NO_PACE_KEY_REFERENCE : UInt8 = 0x00
enum SMSMode : UInt8 {
case ENC_MODE = 0x1;
case MAC_MODE = 0x2;
case PACE_MODE = 0x3;
}
/// Derives the ENC or MAC key for BAC from the keySeed.
/// - Parameter keySeed the key seed.
/// - Parameter mode either <code>ENC_MODE</code> or <code>MAC_MODE</code>
/// - Returns the key.
/// - Throws InvalidDataPassed on data error
func deriveKey( keySeed : [UInt8], mode : SMSMode) throws -> [UInt8] {
return try deriveKey(keySeed: keySeed, cipherAlgName: "DESede", keyLength: 128, mode: mode);
}
/// Derives the ENC or MAC key for BAC or PACE or CA.
/// - Parameter keySeed the key seed.
/// - Parameter cipherAlgName either AES or DESede
/// - Parameter keyLength key length in bits
/// - Parameter mode either {@code ENC_MODE}, {@code MAC_MODE}, or {@code PACE_MODE}
/// - Returns the key.
/// - Throws InvalidDataPassed on data error
func deriveKey(keySeed : [UInt8], cipherAlgName :String, keyLength : Int, mode : SMSMode) throws -> [UInt8] {
return try deriveKey(keySeed: keySeed, cipherAlgName: cipherAlgName, keyLength: keyLength, nonce: nil, mode: mode);
}
/// Derives the ENC or MAC key for BAC or PACE or CA.
/// - Parameter keySeed the shared secret, as octets
/// - Parameter cipherAlg in Java mnemonic notation (for example "DESede", "AES")
/// - Parameter keyLength length in bits
/// - Parameter nonce optional nonce or <code>nil</code>
/// - Parameter mode the mode either {@code ENC}, {@code MAC}, or {@code PACE} mode
/// - Returns the key.
/// - Throws InvalidDataPassed on data error
func deriveKey(keySeed : [UInt8], cipherAlgName :String, keyLength : Int, nonce : [UInt8]? = nil, mode : SMSMode) throws -> [UInt8] {
return try deriveKey(keySeed: keySeed, cipherAlgName: cipherAlgName, keyLength: keyLength, nonce: nonce, mode: mode, paceKeyReference: SecureMessagingSessionKeyGenerator.NO_PACE_KEY_REFERENCE);
}
/// Derives the ENC or MAC key for BAC or PACE or CA.
/// - Parameter keySeed the shared secret, as octets
/// - Parameter cipherAlg in Java mnemonic notation (for example "DESede", "AES")
/// - Parameter keyLength length in bits
/// - Parameter nonce optional nonce or <code>null</code>
/// - Parameter mode the mode either {@code ENC}, {@code MAC}, or {@code PACE} mode
/// - Parameter paceKeyReference Key Reference For Pace Protocol
/// - Returns the key.
/// - Throws InvalidDataPassed on data error
func deriveKey(keySeed : [UInt8], cipherAlgName :String, keyLength : Int, nonce : [UInt8]?, mode : SMSMode, paceKeyReference : UInt8) throws -> [UInt8] {
let digestAlgo = try inferDigestAlgorithmFromCipherAlgorithmForKeyDerivation(cipherAlg: cipherAlgName, keyLength: keyLength);
let modeArr : [UInt8] = [0x00, 0x00, 0x00, mode.rawValue]
var dataEls = [Data(keySeed)]
if let nonce = nonce {
dataEls.append( Data(nonce) )
}
dataEls.append( Data(modeArr) )
let hashResult = try getHash(algo: digestAlgo, dataElements: dataEls)
var keyBytes : [UInt8]
if cipherAlgName == "DESede" || cipherAlgName == "3DES" {
// TR-SAC 1.01, 4.2.1.
switch(keyLength) {
case 112, 128:
// Copy E (Octects 1 to 8), D (Octects 9 to 16), E (again Octects 1 to 8), 112-bit 3DES key
keyBytes = [UInt8](hashResult[0..<16] + hashResult[0..<8])
break;
default:
throw NFCPassportReaderError.InvalidDataPassed("Can only use DESede with 128-but key length")
}
} else if cipherAlgName.lowercased() == "aes" || cipherAlgName.lowercased().hasPrefix("aes") {
// TR-SAC 1.01, 4.2.2.
switch(keyLength) {
case 128:
keyBytes = [UInt8](hashResult[0..<16]) // NOTE: 128 = 16 * 8
case 192:
keyBytes = [UInt8](hashResult[0..<24]) // NOTE: 192 = 24 * 8
case 256:
keyBytes = [UInt8](hashResult[0..<32]) // NOTE: 256 = 32 * 8
default:
throw NFCPassportReaderError.InvalidDataPassed("Can only use AES with 128-bit, 192-bit key or 256-bit length")
}
} else {
throw NFCPassportReaderError.InvalidDataPassed( "Unsupported cipher algorithm used" )
}
return keyBytes
}
func inferDigestAlgorithmFromCipherAlgorithmForKeyDerivation( cipherAlg : String, keyLength : Int) throws -> String {
if cipherAlg == "DESede" || cipherAlg == "AES-128" {
return "SHA1";
}
if cipherAlg == "AES" && keyLength == 128 {
return "SHA1";
}
if cipherAlg == "AES-256" || cipherAlg == "AES-192" {
return "SHA256";
}
if cipherAlg == "AES" && (keyLength == 192 || keyLength == 256) {
return "SHA256";
}
throw NFCPassportReaderError.InvalidDataPassed("Unsupported cipher algorithm or key length")
}
/// This generates a SHA-X hash based on the passed in algo.
/// There must be a more generic way to do this?
func getHash(algo: String, dataElements:[Data] ) throws -> [UInt8] {
var hash : [UInt8]
let algo = algo.lowercased()
if algo == "sha1" {
var hasher = Insecure.SHA1()
for d in dataElements {
hasher.update( data:d )
}
hash = Array(hasher.finalize())
} else if algo == "sha256" {
var hasher = SHA256()
for d in dataElements {
hasher.update( data:d )
}
hash = Array(hasher.finalize())
} else if algo == "sha384" {
var hasher = SHA384()
for d in dataElements {
hasher.update( data:d )
}
hash = Array(hasher.finalize())
} else if algo == "sha512" {
var hasher = SHA512()
for d in dataElements {
hasher.update( data:d )
}
hash = Array(hasher.finalize())
} else {
throw NFCPassportReaderError.InvalidHashAlgorithmSpecified
}
return hash
}
}

View File

@@ -1,166 +0,0 @@
import Foundation
import OpenSSL
@available(iOS 13, macOS 10.15, *)
public class ASN1Item : CustomDebugStringConvertible {
var pos : Int = -1
var depth : Int = -1
var headerLen : Int = -1
var length : Int = -1
var itemType : String = "" // Primative or Constructed (prim or cons)
var type : String = "" // Actual type of the value ( object, set, etc)
var value : String = ""
var line : String = ""
var parent : ASN1Item? = nil
private var children = [ASN1Item] ()
public init( line : String) {
self.line = line
let scanner = Scanner(string: line)
let space = CharacterSet(charactersIn: " =:")
let equals = CharacterSet(charactersIn: "= ")
let colon = CharacterSet(charactersIn: ":")
let end = CharacterSet(charactersIn: "\n")
scanner.charactersToBeSkipped = space
self.pos = scanner.scanInt() ?? -1
_ = scanner.scanUpToCharacters(from: equals)
self.depth = scanner.scanInt() ?? -1
_ = scanner.scanUpToCharacters(from: equals)
self.headerLen = scanner.scanInt() ?? -1
_ = scanner.scanUpToCharacters(from: equals)
self.length = scanner.scanInt() ?? -1
self.itemType = scanner.scanUpToCharacters(from: colon) ?? ""
let rest = scanner.scanUpToCharacters(from: end)?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
if itemType == "cons" {
type = rest
} else {
let items = rest.components(separatedBy: ":" ).filter{ !$0.isEmpty }
self.type = items[0].trimmingCharacters(in: .whitespacesAndNewlines)
if ( items.count > 1 ) {
self.value = items[1].trimmingCharacters(in: .whitespacesAndNewlines)
}
}
}
func addChild( _ child : ASN1Item ) {
child.parent = self
self.children.append( child )
}
public func getChild( _ child : Int ) -> ASN1Item? {
if ( child < children.count ) {
return children[child]
} else {
return nil
}
}
public func getNumberOfChildren() -> Int {
return children.count
}
public var debugDescription: String {
var ret = "pos:\(pos), d=\(depth), hl=\(headerLen), l=\(length): \(itemType): \(type) \(value)\n"
children.forEach { ret += $0.debugDescription }
return ret
}
}
/// Very very basic ASN1 parser class - uses OpenSSL to dump an ASN1 structure to a string, and then parses that out into
/// a tree based hieracy of ASN1Item structures - depth based
@available(iOS 13, macOS 10.15, *)
public class SimpleASN1DumpParser {
public init() {
}
public func parse( data: Data ) throws -> ASN1Item {
var parsed : String = ""
let _ = try data.withUnsafeBytes { (ptr) in
guard let out = BIO_new(BIO_s_mem()) else { throw OpenSSLError.UnableToParseASN1("Unable to allocate output buffer") }
defer { BIO_free(out) }
let rc = ASN1_parse_dump(out, ptr.baseAddress?.assumingMemoryBound(to: UInt8.self), data.count, 0, 0)
if rc == 0 {
throw OpenSSLError.UnableToParseASN1("Failed to parse ASN1 Data")
}
parsed = OpenSSLUtils.bioToString(bio: out)
}
let lines = parsed.components(separatedBy: "\n")
let topItem : ASN1Item? = parseLines( lines:lines)
guard let ret = topItem else {
throw OpenSSLError.UnableToParseASN1("Failed to format ASN1 Data")
}
return ret
}
func parseLines( lines : [String] ) -> ASN1Item? {
var topItem : ASN1Item?
var currentParent : ASN1Item?
for line in lines {
if line.trimmingCharacters(in: .whitespacesAndNewlines) == "" {
continue
}
let item = ASN1Item(line: line)
if item.depth == 0 {
topItem = item
} else if item.depth == currentParent!.depth {
currentParent!.parent!.addChild( item )
} else if item.depth > currentParent!.depth {
currentParent!.addChild( item )
} else {
repeat {
currentParent = currentParent!.parent
} while currentParent!.depth > item.depth-1 && currentParent!.depth != 0
if currentParent!.depth == item.depth-1 {
currentParent!.addChild( item )
}
}
currentParent = item
}
return topItem
}
public func test() {
let lines = [
" 0:d=0 hl=4 l= 758 cons: SET ",
" 662:d=1 hl=2 l= 18 cons: SEQUENCE ",
" 664:d=2 hl=2 l= 10 prim: OBJECT :0.4.0.127.0.7.2.2.3.2.4",
" 676:d=2 hl=2 l= 1 prim: INTEGER :01",
" 679:d=2 hl=2 l= 1 prim: INTEGER :01",
" 682:d=1 hl=2 l= 18 cons: SEQUENCE ",
" 684:d=2 hl=2 l= 10 prim: OBJECT :0.4.0.127.0.7.2.2.3.2.1",
" 696:d=2 hl=2 l= 1 prim: INTEGER :01",
" 699:d=2 hl=2 l= 1 prim: INTEGER :02",
" 702:d=1 hl=2 l= 13 cons: SEQUENCE ",
" 704:d=2 hl=2 l= 8 prim: OBJECT :0.4.0.127.0.7.2.2.2",
" 714:d=2 hl=2 l= 1 prim: INTEGER :01",
" 717:d=1 hl=2 l= 18 cons: SEQUENCE ",
" 719:d=2 hl=2 l= 10 prim: OBJECT :0.4.0.127.0.7.2.2.4.2.4",
" 731:d=2 hl=2 l= 1 prim: INTEGER :02",
" 734:d=2 hl=2 l= 1 prim: INTEGER :0D",
" 737:d=1 hl=2 l= 23 cons: SEQUENCE ",
" 739:d=2 hl=2 l= 6 prim: OBJECT :2.23.136.1.1.5",
" 747:d=2 hl=2 l= 1 prim: INTEGER :01",
" 750:d=2 hl=2 l= 10 prim: OBJECT :0.4.0.127.0.7.1.1.4.1.3",
""
]
let topItem = parseLines( lines:lines )
print( topItem?.debugDescription ?? "" )
}
}

View File

@@ -1,349 +0,0 @@
import Foundation
#if !os(macOS)
import CoreNFC
@available(iOS 15, *)
public class TagReader {
var tag : NFCISO7816Tag
var secureMessaging : SecureMessaging?
var maxDataLengthToRead : Int = 0xA0 // Should be able to use 256 to read arbitrary amounts of data at full speed BUT this isn't supported across all passports so for reliability just use the smaller amount.
var progress : ((Int)->())?
init( tag: NFCISO7816Tag ) {
self.tag = tag
}
func overrideDataAmountToRead( newAmount : Int ) {
maxDataLengthToRead = newAmount
}
func reduceDataReadingAmount() {
if maxDataLengthToRead > 0xA0 {
maxDataLengthToRead = 0xA0
}
}
func readDataGroup( dataGroup: DataGroupId ) async throws -> [UInt8] {
guard let tag = dataGroup.getFileIDTag() else {
throw NFCPassportReaderError.UnsupportedDataGroup
}
return try await selectFileAndRead(tag: tag )
}
func getChallenge() async throws -> ResponseAPDU{
let cmd : NFCISO7816APDU = NFCISO7816APDU(instructionClass: 00, instructionCode: 0x84, p1Parameter: 0, p2Parameter: 0, data: Data(), expectedResponseLength: 8)
return try await send( cmd: cmd )
}
func doInternalAuthentication( challenge: [UInt8] ) async throws -> ResponseAPDU {
let randNonce = Data(challenge)
let cmd = NFCISO7816APDU(instructionClass: 00, instructionCode: 0x88, p1Parameter: 0, p2Parameter: 0, data: randNonce, expectedResponseLength: 256)
return try await send( cmd: cmd )
}
func doMutualAuthentication( cmdData : Data ) async throws -> ResponseAPDU{
let cmd : NFCISO7816APDU = NFCISO7816APDU(instructionClass: 00, instructionCode: 0x82, p1Parameter: 0, p2Parameter: 0, data: cmdData, expectedResponseLength: 256)
return try await send( cmd: cmd )
}
/// The MSE KAT APDU, see EAC 1.11 spec, Section B.1.
/// This command is sent in the "DESede" case.
/// - Parameter keyData key data object (tag 0x91)
/// - Parameter idData key id data object (tag 0x84), can be null
/// - Parameter completed the complete handler - returns the success response or an error
func sendMSEKAT( keyData : Data, idData: Data? ) async throws -> ResponseAPDU {
var data = keyData
if let idData = idData {
data += idData
}
let cmd : NFCISO7816APDU = NFCISO7816APDU(instructionClass: 00, instructionCode: 0x22, p1Parameter: 0x41, p2Parameter: 0xA6, data: data, expectedResponseLength: 256)
return try await send( cmd: cmd )
}
/// The MSE Set AT for Chip Authentication.
/// This command is the first command that is sent in the "AES" case.
/// For Chip Authentication. We prefix 0x80 for OID and 0x84 for keyId.
///
/// NOTE THIS IS CURRENTLY UNTESTED
/// - Parameter oid the OID
/// - Parameter keyId the keyId or {@code null}
/// - Parameter completed the complete handler - returns the success response or an error
func sendMSESetATIntAuth( oid: String, keyId: Int? ) async throws -> ResponseAPDU {
let cmd : NFCISO7816APDU
let oidBytes = oidToBytes(oid: oid, replaceTag: true)
if let keyId = keyId, keyId != 0 {
let keyIdBytes = wrapDO(b:0x84, arr:intToBytes(val:keyId, removePadding: true))
let data = oidBytes + keyIdBytes
cmd = NFCISO7816APDU(instructionClass: 00, instructionCode: 0x22, p1Parameter: 0x41, p2Parameter: 0xA4, data: Data(data), expectedResponseLength: 256)
} else {
cmd = NFCISO7816APDU(instructionClass: 00, instructionCode: 0x22, p1Parameter: 0x41, p2Parameter: 0xA4, data: Data(oidBytes), expectedResponseLength: 256)
}
return try await send( cmd: cmd )
}
func sendMSESetATMutualAuth( oid: String, keyType: UInt8 ) async throws -> ResponseAPDU {
let oidBytes = oidToBytes(oid: oid, replaceTag: true)
let keyTypeBytes = wrapDO( b: 0x83, arr:[keyType])
let data = oidBytes + keyTypeBytes
let cmd = NFCISO7816APDU(instructionClass: 00, instructionCode: 0x22, p1Parameter: 0xC1, p2Parameter: 0xA4, data: Data(data), expectedResponseLength: -1)
return try await send( cmd: cmd )
}
/// Sends a General Authenticate command.
/// This command is the second command that is sent in the "AES" case.
/// - Parameter data data to be sent, without the {@code 0x7C} prefix (this method will add it)
/// - Parameter lengthExpected the expected length defaults to 256
/// - Parameter isLast indicates whether this is the last command in the chain
/// - Parameter completed the complete handler - returns the dynamic authentication data without the {@code 0x7C} prefix (this method will remove it) or an error
func sendGeneralAuthenticate( data : [UInt8], lengthExpected : Int = 256, isLast: Bool) async throws -> ResponseAPDU {
let wrappedData = wrapDO(b:0x7C, arr:data)
let commandData = Data(wrappedData)
// NOTE: Support of Protocol Response Data is CONDITIONAL:
// It MUST be provided for version 2 but MUST NOT be provided for version 1.
// So, we are expecting 0x7C (= tag), 0x00 (= length) here.
// 0x10 is class command chaining
let instructionClass : UInt8 = isLast ? 0x00 : 0x10
let INS_BSI_GENERAL_AUTHENTICATE : UInt8 = 0x86
let cmd : NFCISO7816APDU = NFCISO7816APDU(instructionClass: instructionClass, instructionCode: INS_BSI_GENERAL_AUTHENTICATE, p1Parameter: 0x00, p2Parameter: 0x00, data: commandData, expectedResponseLength: lengthExpected)
var response : ResponseAPDU
do {
response = try await send( cmd: cmd )
response.data = try unwrapDO( tag:0x7c, wrappedData:response.data)
} catch {
// If wrong length error
if case NFCPassportReaderError.ResponseError(_, let sw1, let sw2) = error,
sw1 == 0x67, sw2 == 0x00 {
// Resend
let cmd : NFCISO7816APDU = NFCISO7816APDU(instructionClass: instructionClass, instructionCode: INS_BSI_GENERAL_AUTHENTICATE, p1Parameter: 0x00, p2Parameter: 0x00, data: commandData, expectedResponseLength: 256)
response = try await send( cmd: cmd )
response.data = try unwrapDO( tag:0x7c, wrappedData:response.data)
} else {
throw error
}
}
return response
}
func selectFileAndRead( tag: [UInt8]) async throws -> [UInt8] {
var resp = try await selectFile(tag: tag )
// Read first 4 bytes of header to see how big the data structure is
guard let readHeaderCmd = NFCISO7816APDU(data:Data([0x00, 0xB0, 0x00, 0x00, 0x00, 0x00,0x04])) else {
throw NFCPassportReaderError.UnexpectedError
}
resp = try await self.send( cmd: readHeaderCmd )
// Header looks like: <tag><length of data><nextTag> e.g.60145F01 -
// the total length is the 2nd value plus the two header 2 bytes
// We've read 4 bytes so we now need to read the remaining bytes from offset 4
let (len, o) = try! asn1Length([UInt8](resp.data[1..<4]))
var remaining = Int(len)
var amountRead = o + 1
var data = [UInt8](resp.data[..<amountRead])
Log.debug( "TagReader - Number of data bytes to read - \(remaining)" )
var readAmount : Int = maxDataLengthToRead
while remaining > 0 {
if maxDataLengthToRead != 256 && remaining < maxDataLengthToRead {
readAmount = remaining
}
self.progress?( Int(Float(amountRead) / Float(remaining+amountRead ) * 100))
let offset = intToBin(amountRead, pad:4)
Log.verbose( "TagReader - data bytes remaining: \(remaining), will read : \(readAmount)" )
let cmd = NFCISO7816APDU(
instructionClass: 00,
instructionCode: 0xB0,
p1Parameter: offset[0],
p2Parameter: offset[1],
data: Data(),
expectedResponseLength: readAmount
)
resp = try await self.send( cmd: cmd )
Log.verbose( "TagReader - got resp - \(binToHexRep(resp.data, asArray: true)), sw1 : \(resp.sw1), sw2 : \(resp.sw2)" )
data += resp.data
remaining -= resp.data.count
amountRead += resp.data.count
Log.verbose( "TagReader - Amount of data left to read - \(remaining)" )
}
return data
}
func readCardAccess() async throws -> [UInt8]{
// Info provided by @smulu
// By default NFCISO7816Tag requirers a list of ISO/IEC 7816 applets (AIDs). Upon discovery of NFC tag the first found applet from this list is automatically selected (and you have no way of changing this).
// This is a problem for PACE protocol becaues it requires reading parameters from file EF.CardAccess which lies outside of eMRTD applet (AID: A0000002471001) in the master file.
// Now, the ICAO 9303 standard does specify command for selecting master file by sending SELECT APDU with P1=0x00, P2=0x0C and empty data field (see part 10 page 8). But after some testing I found out this command doesn't work on some passports (European passports) and although receiving success (sw=9000) from passport the master file is not selected.
// After a bit of researching standard ISO/IEC 7816 I found there is an alternative SELECT command for selecting master file. The command doesn't differ much from the command specified in ICAO 9303 doc with only difference that data field is set to: 0x3F00. See section 6.11.3 of ISO/IEC 7816-4.
// By executing above SELECT command (with data=0x3F00) master file should be selected and you should be able to read EF.CardAccess from passport.
// First select master file
let cmd : NFCISO7816APDU = NFCISO7816APDU(instructionClass: 0x00, instructionCode: 0xA4, p1Parameter: 0x00, p2Parameter: 0x0C, data: Data([0x3f,0x00]), expectedResponseLength: -1)
_ = try await send( cmd: cmd)
// Now read EC.CardAccess
let data = try await self.selectFileAndRead(tag: [0x01,0x1C])
return data
}
func selectPassportApplication() async throws -> ResponseAPDU {
// Finally reselect the eMRTD application so the rest of the reading works as normal
Log.debug( "Re-selecting eMRTD Application" )
let cmd : NFCISO7816APDU = NFCISO7816APDU(instructionClass: 0x00, instructionCode: 0xA4, p1Parameter: 0x04, p2Parameter: 0x0C, data: Data([0xA0, 0x00, 0x00, 0x02, 0x47, 0x10, 0x01]), expectedResponseLength: -1)
let response = try await self.send( cmd: cmd)
return response
}
func selectFile( tag: [UInt8] ) async throws -> ResponseAPDU {
let data : [UInt8] = [0x00, 0xA4, 0x02, 0x0C, 0x02] + tag
let cmd = NFCISO7816APDU(data:Data(data))!
return try await send( cmd: cmd )
}
func send( cmd: NFCISO7816APDU ) async throws -> ResponseAPDU {
Log.verbose( "TagReader - sending \(cmd)" )
var toSend = cmd
if let sm = secureMessaging {
toSend = try sm.protect(apdu:cmd)
Log.verbose("TagReader - [SM] \(toSend)" )
}
let (data, sw1, sw2) = try await tag.sendCommand(apdu: toSend)
Log.verbose( "TagReader - Received response" )
var rep = ResponseAPDU(data: [UInt8](data), sw1: sw1, sw2: sw2)
if let sm = self.secureMessaging {
rep = try sm.unprotect(rapdu:rep)
Log.verbose(String(format:"TagReader [SM - unprotected] \(binToHexRep(rep.data, asArray:true)), sw1:0x%02x sw2:0x%02x", rep.sw1, rep.sw2) )
} else {
Log.verbose(String(format:"TagReader [unprotected] \(binToHexRep(rep.data, asArray:true)), sw1:0x%02x sw2:0x%02x", rep.sw1, rep.sw2) )
}
if rep.sw1 != 0x90 && rep.sw2 != 0x00 {
Log.error( "Error reading tag: sw1 - 0x\(binToHexRep(sw1)), sw2 - 0x\(binToHexRep(sw2))" )
let tagError: NFCPassportReaderError
if (rep.sw1 == 0x63 && rep.sw2 == 0x00) {
tagError = NFCPassportReaderError.InvalidMRZKey
} else {
let errorMsg = self.decodeError(sw1: rep.sw1, sw2: rep.sw2)
Log.error( "reason: \(errorMsg)" )
tagError = NFCPassportReaderError.ResponseError( errorMsg, sw1, sw2 )
}
throw tagError
}
return rep
}
private func decodeError( sw1: UInt8, sw2:UInt8 ) -> String {
let errors : [UInt8 : [UInt8:String]] = [
0x62: [0x00:"No information given",
0x81:"Part of returned data may be corrupted",
0x82:"End of file/record reached before reading Le bytes",
0x83:"Selected file invalidated",
0x84:"FCI not formatted according to ISO7816-4 section 5.1.5"],
0x63: [0x81:"File filled up by the last write",
0x82:"Card Key not supported",
0x83:"Reader Key not supported",
0x84:"Plain transmission not supported",
0x85:"Secured Transmission not supported",
0x86:"Volatile memory not available",
0x87:"Non Volatile memory not available",
0x88:"Key number not valid",
0x89:"Key length is not correct",
0xC:"Counter provided by X (valued from 0 to 15) (exact meaning depending on the command)"],
0x65: [0x00:"No information given",
0x81:"Memory failure"],
0x67: [0x00:"Wrong length"],
0x68: [0x00:"No information given",
0x81:"Logical channel not supported",
0x82:"Secure messaging not supported",
0x83:"Last command of the chain expected",
0x84:"Command chaining not supported"],
0x69: [0x00:"No information given",
0x81:"Command incompatible with file structure",
0x82:"Security status not satisfied",
0x83:"Authentication method blocked",
0x84:"Referenced data invalidated",
0x85:"Conditions of use not satisfied",
0x86:"Command not allowed (no current EF)",
0x87:"Expected SM data objects missing",
0x88:"SM data objects incorrect"],
0x6A: [0x00:"No information given",
0x80:"Incorrect parameters in the data field",
0x81:"Function not supported",
0x82:"File not found",
0x83:"Record not found",
0x84:"Not enough memory space in the file",
0x85:"Lc inconsistent with TLV structure",
0x86:"Incorrect parameters P1-P2",
0x87:"Lc inconsistent with P1-P2",
0x88:"Referenced data not found"],
0x6B: [0x00:"Wrong parameter(s) P1-P2]"],
0x6D: [0x00:"Instruction code not supported or invalid"],
0x6E: [0x00:"Class not supported"],
0x6F: [0x00:"No precise diagnosis"],
0x90: [0x00:"Success"] //No further qualification
]
// Special cases - where sw2 isn't an error but contains a value
if sw1 == 0x61 {
return "SW2 indicates the number of response bytes still available - (\(sw2) bytes still available)"
} else if sw1 == 0x64 {
return "State of non-volatile memory unchanged (SW2=00, other values are RFU)"
} else if sw1 == 0x6C {
return "Wrong length Le: SW2 indicates the exact length - (exact length :\(sw2))"
}
if let dict = errors[sw1], let errorMsg = dict[sw2] {
return errorMsg
}
return "Unknown error - sw1: 0x\(binToHexRep(sw1)), sw2 - 0x\(binToHexRep(sw2)) "
}
}
#endif

View File

@@ -1,415 +0,0 @@
import Foundation
import CommonCrypto
import CryptoTokenKit
#if canImport(CryptoKit)
import CryptoKit
#endif
private extension UInt8 {
var hexString: String {
let string = String(self, radix: 16)
return (self < 16 ? "0" + string : string)
}
}
extension FileManager {
static var documentDir : URL {
return FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
}
}
extension StringProtocol {
subscript(bounds: CountableClosedRange<Int>) -> SubSequence {
let start = index(startIndex, offsetBy: bounds.lowerBound)
let end = index(start, offsetBy: bounds.count)
return self[start..<end]
}
subscript(bounds: CountableRange<Int>) -> SubSequence {
let start = index(startIndex, offsetBy: bounds.lowerBound)
let end = index(start, offsetBy: bounds.count)
return self[start..<end]
}
func index(of string: Self, options: String.CompareOptions = []) -> Index? {
return range(of: string, options: options)?.lowerBound
}
}
public func binToHexRep( _ val : [UInt8], asArray : Bool = false ) -> String {
var string = asArray ? "[" : ""
for x in val {
if asArray {
string += String(format:"0x%02x, ", x )
} else {
string += String(format:"%02x", x )
}
}
string += asArray ? "]" : ""
return asArray ? string : string.uppercased()
}
public func binToHexRep( _ val : UInt8 ) -> String {
let string = String(format:"%02x", val ).uppercased()
return string
}
public func binToHex( _ val: UInt8 ) -> Int {
let hexRep = String(format:"%02X", val)
return Int(hexRep, radix:16)!
}
public func binToHex( _ val: [UInt8] ) -> UInt64 {
let hexVal = UInt64(binToHexRep(val), radix:16)!
return hexVal
}
public func binToHex( _ val: ArraySlice<UInt8> ) -> UInt64 {
return binToHex( [UInt8](val) )
}
public func hexToBin( _ val : UInt64 ) -> [UInt8] {
let hexRep = String(format:"%lx", val)
return hexRepToBin( hexRep)
}
public func binToInt( _ val: ArraySlice<UInt8> ) -> Int {
let hexVal = binToInt( [UInt8](val) )
return hexVal
}
public func binToInt( _ val: [UInt8] ) -> Int {
let hexVal = Int(binToHexRep(val), radix:16)!
return hexVal
}
public func intToBin(_ data : Int, pad : Int = 2) -> [UInt8] {
if pad == 2 {
let hex = String(format:"%02x", data)
return hexRepToBin(hex)
} else {
let hex = String(format:"%04x", data)
return hexRepToBin(hex)
}
}
/// 'AABB' --> \xaa\xbb'"""
public func hexRepToBin(_ val : String) -> [UInt8] {
var output : [UInt8] = []
var x = 0
while x < val.count {
if x+2 <= val.count {
output.append( UInt8(val[x ..< x + 2], radix:16)! )
} else {
output.append( UInt8(val[x ..< x+1], radix:16)! )
}
x += 2
}
return output
}
public func xor(_ kifd : [UInt8], _ response_kicc : [UInt8] ) -> [UInt8] {
var kseed = [UInt8]()
for i in 0 ..< kifd.count {
kseed.append( kifd[i] ^ response_kicc[i] )
}
return kseed
}
public func generateRandomUInt8Array( _ size: Int ) -> [UInt8] {
var ret : [UInt8] = []
for _ in 0 ..< size {
ret.append( UInt8(arc4random_uniform(UInt32(UInt8.max) + 1)) )
}
return ret
}
public func pad(_ toPad : [UInt8], blockSize : Int) -> [UInt8] {
var ret = toPad + [0x80]
while ret.count % blockSize != 0 {
ret.append(0x00)
}
return ret
}
public func unpad( _ tounpad : [UInt8]) -> [UInt8] {
var i = tounpad.count-1
while tounpad[i] == 0x00 {
i -= 1
}
if tounpad[i] == 0x80 {
return [UInt8](tounpad[0..<i])
} else {
// no padding
return tounpad
}
}
@available(iOS 13, macOS 10.15, *)
public func mac(algoName: SecureMessagingSupportedAlgorithms, key : [UInt8], msg : [UInt8]) -> [UInt8] {
if algoName == .DES {
return desMAC(key: key, msg: msg)
} else {
return aesMAC(key: key, msg: msg)
}
}
@available(iOS 13, macOS 10.15, *)
public func desMAC(key : [UInt8], msg : [UInt8]) -> [UInt8]{
let size = msg.count / 8
var y : [UInt8] = [0,0,0,0,0,0,0,0]
Log.verbose("Calc mac" )
for i in 0 ..< size {
let tmp = [UInt8](msg[i*8 ..< i*8+8])
Log.verbose("x\(i): \(binToHexRep(tmp))" )
y = DESEncrypt(key: [UInt8](key[0..<8]), message: tmp, iv: y)
Log.verbose("y\(i): \(binToHexRep(y))" )
}
Log.verbose("y: \(binToHexRep(y))" )
Log.verbose("bkey: \(binToHexRep([UInt8](key[8..<16])))" )
Log.verbose("akey: \(binToHexRep([UInt8](key[0..<8])))" )
let iv : [UInt8] = [0,0,0,0,0,0,0,0]
let b = DESDecrypt(key: [UInt8](key[8..<16]), message: y, iv: iv, options:UInt32(kCCOptionECBMode))
Log.verbose( "b: \(binToHexRep(b))" )
let a = DESEncrypt(key: [UInt8](key[0..<8]), message: b, iv: iv, options:UInt32(kCCOptionECBMode))
Log.verbose( "a: \(binToHexRep(a))" )
return a
}
@available(iOS 13, macOS 10.15, *)
public func aesMAC( key: [UInt8], msg : [UInt8] ) -> [UInt8] {
let mac = OpenSSLUtils.generateAESCMAC( key: key, message:msg )
return mac
}
@available(iOS 13, macOS 10.15, *)
public func wrapDO( b : UInt8, arr : [UInt8] ) -> [UInt8] {
let tag = TKBERTLVRecord(tag: TKTLVTag(b), value: Data(arr))
let result = [UInt8](tag.data)
return result;
}
@available(iOS 13, macOS 10.15, *)
public func unwrapDO( tag : UInt8, wrappedData : [UInt8]) throws -> [UInt8] {
guard let rec = TKBERTLVRecord(from: Data(wrappedData)),
rec.tag == tag else {
throw NFCPassportReaderError.InvalidASN1Value
}
return [UInt8](rec.value);
}
public func intToBytes( val: Int, removePadding:Bool) -> [UInt8] {
if val == 0 {
return [0]
}
var data = withUnsafeBytes(of: val.bigEndian, Array.init)
if removePadding {
// Remove initial 0 bytes
for i in 0 ..< data.count {
if data[i] != 0 {
data = [UInt8](data[i...])
break
}
}
}
return data
}
@available(iOS 13, macOS 10.15, *)
public func oidToBytes(oid : String, replaceTag : Bool) -> [UInt8] {
var encOID = OpenSSLUtils.asn1EncodeOID(oid: oid)
if replaceTag {
// Replace tag (0x06) with 0x80
encOID[0] = 0x80
}
return encOID
}
/// Take an asn.1 length, and return a couple with the decoded length in hexa and the total length of the encoding (1,2 or 3 bytes)
///
/// Using Basic Encoding Rules (BER):
/// If the first byte is <= 0x7F (0-127), then this is the total length of the data
/// If the first byte is 0x81 then the length is the value of the next byte
/// If the first byte is 0x82 then the length is the value of the next two bytes
/// If the first byte is 0x80 then the length is indefinite (never seen this and not sure exactle what it means)
/// e.g.
/// if the data was 0x02, 0x11, 0x12, then the amount of data we have to read is two bytes, and the actual data is [0x11, 0x12]
/// If the length was 0x81,0x80,....... then we know that the data length is contained in the next byte - 0x80 (128), so the amount of data to read is 128 bytes
/// If the length was 0x82,0x01,0x01,....... then we know that the data length is contained in the next 2 bytes - 0x01, 0x01 (257) so the amount of data to read is 257 bytes
///
/// @param data: A length value encoded in the asn.1 format.
/// @type data: A binary string.
/// @return: A tuple with the decoded hexa length and the length of the asn.1 encoded value.
/// @raise asn1Exception: If the parameter does not follow the asn.1 notation.
@available(iOS 13, macOS 10.15, *)
public func asn1Length( _ data: ArraySlice<UInt8> ) throws -> (Int, Int) {
return try asn1Length( Array(data) )
}
@available(iOS 13, macOS 10.15, *)
public func asn1Length(_ data : [UInt8]) throws -> (Int, Int) {
if data[0] < 0x80 {
return (Int(binToHex(data[0])), 1)
}
if data[0] == 0x81 {
return (Int(binToHex(data[1])), 2)
}
if data[0] == 0x82 {
let val = binToHex([UInt8](data[1..<3]))
return (Int(val), 3)
}
throw NFCPassportReaderError.CannotDecodeASN1Length
}
/// Convert a length to asn.1 format
/// @param data: The value to encode in asn.1
/// @type data: An integer (hexa)
/// @return: The asn.1 encoded value
/// @rtype: A binary string
/// @raise asn1Exception: If the parameter is too big, must be >= 0 and <= FFFF
@available(iOS 13, macOS 10.15, *)
public func toAsn1Length(_ data : Int) throws -> [UInt8] {
if data < 0x80 {
return hexRepToBin(String(format:"%02x", data))
}
if data >= 0x80 && data <= 0xFF {
return [0x81] + hexRepToBin( String(format:"%02x",data))
}
if data >= 0x0100 && data <= 0xFFFF {
return [0x82] + hexRepToBin( String(format:"%04x",data))
}
throw NFCPassportReaderError.InvalidASN1Value
}
/// This function calculates a Hash of the input data based on the input algorithm
/// @param data: a byte array of data
/// @param hashAlgorithm: the hash algorithm to be used - supported ones are SHA1, SHA224, SHA256, SHA384 and SHA512
/// Currently specifying any others return empty array
/// @return: A hash of the data
@available(iOS 13, macOS 10.15, *)
public func calcHash( data: [UInt8], hashAlgorithm: String ) throws -> [UInt8] {
var ret : [UInt8] = []
let hashAlgorithm = hashAlgorithm.lowercased()
if hashAlgorithm == "sha1" {
ret = calcSHA1Hash(data)
} else if hashAlgorithm == "sha224" {
ret = calcSHA224Hash(data)
} else if hashAlgorithm == "sha256" {
ret = calcSHA256Hash(data)
} else if hashAlgorithm == "sha384" {
ret = calcSHA384Hash(data)
} else if hashAlgorithm == "sha512" {
ret = calcSHA512Hash(data)
} else {
throw NFCPassportReaderError.InvalidHashAlgorithmSpecified
}
return ret
}
/// This function calculates a SHA1 Hash of the input data
/// @param data: a byte array of data
/// @return: A SHA1 hash of the data
@available(iOS 13, macOS 10.15, *)
public func calcSHA1Hash( _ data: [UInt8] ) -> [UInt8] {
#if canImport(CryptoKit)
var sha1 = Insecure.SHA1()
sha1.update(data: data)
let hash = sha1.finalize()
return Array(hash)
#else
fatalError("Couldn't import CryptoKit")
#endif
}
/// This function calculates a SHA224 Hash of the input data
/// @param data: a byte array of data
/// @return: A SHA224 hash of the data
@available(iOS 13, macOS 10.15, *)
public func calcSHA224Hash( _ data: [UInt8] ) -> [UInt8] {
var digest = [UInt8](repeating: 0, count:Int(CC_SHA224_DIGEST_LENGTH))
data.withUnsafeBytes {
_ = CC_SHA224($0.baseAddress, CC_LONG(data.count), &digest)
}
return digest
}
/// This function calculates a SHA256 Hash of the input data
/// @param data: a byte array of data
/// @return: A SHA256 hash of the data
@available(iOS 13, macOS 10.15, *)
public func calcSHA256Hash( _ data: [UInt8] ) -> [UInt8] {
#if canImport(CryptoKit)
var sha256 = SHA256()
sha256.update(data: data)
let hash = sha256.finalize()
return Array(hash)
#else
fatalError("Couldn't import CryptoKit")
#endif
}
/// This function calculates a SHA512 Hash of the input data
/// @param data: a byte array of data
/// @return: A SHA512 hash of the data
@available(iOS 13, macOS 10.15, *)
public func calcSHA512Hash( _ data: [UInt8] ) -> [UInt8] {
#if canImport(CryptoKit)
var sha512 = SHA512()
sha512.update(data: data)
let hash = sha512.finalize()
return Array(hash)
#else
fatalError("Couldn't import CryptoKit")
#endif
}
/// This function calculates a SHA384 Hash of the input data
/// @param data: a byte array of data
/// @return: A SHA384 hash of the data
@available(iOS 13, macOS 10.15, *)
public func calcSHA384Hash( _ data: [UInt8] ) -> [UInt8] {
#if canImport(CryptoKit)
var sha384 = SHA384()
sha384.update(data: data)
let hash = sha384.finalize()
return Array(hash)
#else
fatalError("Couldn't import CryptoKit")
#endif
}

View File

@@ -1,161 +0,0 @@
import OpenSSL
@available(iOS 13, macOS 10.15, *)
public enum CertificateType {
case documentSigningCertificate
case issuerSigningCertificate
}
@available(iOS 13, macOS 10.15, *)
public enum CertificateItem : String {
case fingerprint = "Certificate fingerprint"
case issuerName = "Issuer"
case subjectName = "Subject"
case serialNumber = "Serial number"
case signatureAlgorithm = "Signature algorithm"
case publicKeyAlgorithm = "Public key algorithm"
case notBefore = "Valid from"
case notAfter = "Valid to"
}
@available(iOS 13, macOS 10.15, *)
public class X509Wrapper {
public let cert : OpaquePointer
public init?( with cert: OpaquePointer? ) {
guard let cert = cert else { return nil }
self.cert = X509_dup(cert)
}
public func getItemsAsDict() -> [CertificateItem:String] {
var item = [CertificateItem:String]()
if let fingerprint = self.getFingerprint() {
item[.fingerprint] = fingerprint
}
if let issuerName = self.getIssuerName() {
item[.issuerName] = issuerName
}
if let subjectName = self.getSubjectName() {
item[.subjectName] = subjectName
}
if let serialNr = self.getSerialNumber() {
item[.serialNumber] = serialNr
}
if let signatureAlgorithm = self.getSignatureAlgorithm() {
item[.signatureAlgorithm] = signatureAlgorithm
}
if let publicKeyAlgorithm = self.getPublicKeyAlgorithm() {
item[.publicKeyAlgorithm] = publicKeyAlgorithm
}
if let notBefore = self.getNotBeforeDate() {
item[.notBefore] = notBefore
}
if let notAfter = self.getNotAfterDate() {
item[.notAfter] = notAfter
}
return item
}
public func certToPEM() -> String {
return OpenSSLUtils.X509ToPEM( x509:cert )
}
public func getFingerprint( ) -> String? {
let fdig = EVP_sha1();
var n : UInt32 = 0
let md = UnsafeMutablePointer<UInt8>.allocate(capacity: Int(EVP_MAX_MD_SIZE))
defer { md.deinitialize(count: Int(EVP_MAX_MD_SIZE)); md.deallocate() }
X509_digest(cert, fdig, md, &n)
let arr = UnsafeMutableBufferPointer(start: md, count: Int(n)).map({ binToHexRep($0) }).joined(separator: ":")
return arr
}
public func getNotBeforeDate() -> String? {
var notBefore : String?
if let val = X509_get0_notBefore(cert) {
notBefore = ASN1TimeToString( val )
}
return notBefore
}
public func getNotAfterDate() -> String? {
var notAfter : String?
if let val = X509_get0_notAfter(cert) {
notAfter = ASN1TimeToString( val )
}
return notAfter
}
public func getSerialNumber() -> String? {
let serialNr = String( ASN1_INTEGER_get(X509_get_serialNumber(cert)), radix:16, uppercase: true )
return serialNr
}
public func getSignatureAlgorithm() -> String? {
let algor = X509_get0_tbs_sigalg(cert);
let algo = getAlgorithm( algor?.pointee.algorithm )
return algo
}
public func getPublicKeyAlgorithm() -> String? {
let pubKey = X509_get_X509_PUBKEY(cert)
var ptr : OpaquePointer?
X509_PUBKEY_get0_param(&ptr, nil, nil, nil, pubKey)
let algo = getAlgorithm(ptr)
return algo
}
public func getIssuerName() -> String? {
return getName(for: X509_get_issuer_name(cert))
}
public func getSubjectName() -> String? {
return getName(for: X509_get_subject_name(cert))
}
private func getName( for name: OpaquePointer? ) -> String? {
guard let name = name else { return nil }
var issuer: String = ""
guard let out = BIO_new( BIO_s_mem()) else { return nil }
defer { BIO_free(out) }
X509_NAME_print_ex(out, name, 0, UInt(ASN1_STRFLGS_ESC_2253 |
ASN1_STRFLGS_ESC_CTRL |
ASN1_STRFLGS_ESC_MSB |
ASN1_STRFLGS_UTF8_CONVERT |
ASN1_STRFLGS_DUMP_UNKNOWN |
ASN1_STRFLGS_DUMP_DER | XN_FLAG_SEP_COMMA_PLUS |
XN_FLAG_DN_REV |
XN_FLAG_FN_SN |
XN_FLAG_DUMP_UNKNOWN_FIELDS))
issuer = OpenSSLUtils.bioToString(bio: out)
return issuer
}
private func getAlgorithm( _ algo: OpaquePointer? ) -> String? {
guard let algo = algo else { return nil }
let len = OBJ_obj2nid(algo)
var algoString : String? = nil
if let sa = OBJ_nid2ln(len) {
algoString = String(cString: sa )
}
return algoString
}
private func ASN1TimeToString( _ date: UnsafePointer<ASN1_TIME> ) -> String? {
guard let b = BIO_new(BIO_s_mem()) else { return nil }
defer { BIO_free(b) }
ASN1_TIME_print(b, date)
return OpenSSLUtils.bioToString(bio: b)
}
}

View File

@@ -1,32 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIFdDCCA1wCCQDFcc+Un66UsDANBgkqhkiG9w0BAQsFADB8MQswCQYDVQQGEwJH
QjESMBAGA1UECAwJU29tZXdoZXJlMRIwEAYDVQQHDAlUaGlzIENpdHkxCzAJBgNV
BAoMAk5BMQswCQYDVQQLDAJOQTENMAsGA1UEAwwEdGVzdDEcMBoGCSqGSIb3DQEJ
ARYNdGVzdEB0ZXN0LmNvbTAeFw0xOTA2MjgxMzAyNTlaFw0yMDA2MjcxMzAyNTla
MHwxCzAJBgNVBAYTAkdCMRIwEAYDVQQIDAlTb21ld2hlcmUxEjAQBgNVBAcMCVRo
aXMgQ2l0eTELMAkGA1UECgwCTkExCzAJBgNVBAsMAk5BMQ0wCwYDVQQDDAR0ZXN0
MRwwGgYJKoZIhvcNAQkBFg10ZXN0QHRlc3QuY29tMIICIjANBgkqhkiG9w0BAQEF
AAOCAg8AMIICCgKCAgEA4sTb0Xd20ATso1VffMGK/N2TuEcsZvQkbhr75dd6f77y
3h5r5ikFCcppGwlgEy6/wn0iEiv4iGDm7cxvVSfwZ+Bm7nbYZVHl8G7vYUZRdLnh
tcUpfa+jPyrQ+XXfTygjoE2shAs8Wv+uB/O0oLbltdx5HMcitHj6MkIz3bs6hY4Z
2kSy6JB1Iw0GwB6vfHbD8MUkn0Aqxwdrd5ks61lVpYx3JyvhUXVjC/9rE+dk6+cN
16WvRhCEH6aClgWcfkSfDtZ85ltcyL3glAqX1YMLl4CFRKZ3NLVgXjHj/QYvAXJq
yWoNbAQvCFv5WK8/JjtAzFYKwjGGUhG5tL6etLrfDF/RudrLDIS2Js9cTMbbB10O
UehVksJvYQBaj8VRSsI7GrGydecfBixwqYD5DxcPzZsGcsfzuQYkotG+poDhjRE4
Ow9zqIG20hgnBn3VTbP21yc4woYLzIK7M7MdWDiDqqcrLwtxolIk6R6lc/3wVR8i
19F5HFwKM0RW3cWZhXqCHrpYBzj8gx7hJAKSFZWDygLFH8h1LtMx93kstxPR7FA6
99737YU1HF0j7kjKJAIQqoJ43ftUN9q8sDN/rRpEgKnRUuFrvcaTdTZCJ7NT3u6p
edC+8rdbKQO6TX0JC8PEl1FjTiVnb3wpMpWPYaI3hspSN/CMUunypoKRJ5IgSK8C
AwEAATANBgkqhkiG9w0BAQsFAAOCAgEAJtrliSVzukKjUJR3AnZrgcnWZ8kTGH9E
TSfpmc9IkMvwWvBs/vgpKMm3XVzTM1RcI/pQYR9Y4beXf4wgTQIg1I2kWLVcRMzU
rwFWl2/h6uA6G+tJpaVVy30rPcYGqLmBqzCCRb3sf/H77/6nknafA4jMASlIWnTE
R7HBd0cJa50jaTgFaIe734oIMqaAZvFFKK5KDnxSBMHwUf7ej1ibT/rB5Ry80qpJ
FsKlxpjaeUDWsnnjR4Z98rjDsWOSI8tUTStdnPYTw/6VFsf+zPKwE2u2Qe7SXg3S
eUoCQqWYaSMqmAfTVJx7zIDIvxa8+LmtijeqYUZcfnos0xxTEeJ6EH65rAPOi23x
1pg8Z28NAAinqFAo74Hr2hTKESmq0mykF/XP4wLhhaxYHy+FFwm984IIpqbKJmuY
/LapNMi6hZXRXft5PQYe57ZXD9/Lp8pn+19nX0nyv5agius0/R4M3CvuPbdose64
jm+2325MQAhihrHmcU0YIt1kwSMuiYOqwD5plx+yenCIhdR6Oa5WX7Xf/hiKZult
5UVNVxfG1tZbsk/u2iwd2fDMBGQ3/D36REA5el6GUPpBOaa6gVmpS38qRCTVTBqH
NY8Qy6N+AHsahMtMakwo/EWsDsGTajcrvXX1psLgpRdMNeXgU3nNzJ+wPMUPCTFB
ZhNynFjgmgY=
-----END CERTIFICATE-----

View File

@@ -1,66 +0,0 @@
#import <UIKit/UIKit.h>
#import <XCTest/XCTest.h>
#import <React/RCTLog.h>
#import <React/RCTRootView.h>
#define TIMEOUT_SECONDS 600
#define TEXT_TO_LOOK_FOR @"Welcome to React"
@interface AwesomeProjectTests : XCTestCase
@end
@implementation AwesomeProjectTests
- (BOOL)findSubviewInView:(UIView *)view matching:(BOOL (^)(UIView *view))test
{
if (test(view)) {
return YES;
}
for (UIView *subview in [view subviews]) {
if ([self findSubviewInView:subview matching:test]) {
return YES;
}
}
return NO;
}
- (void)testRendersWelcomeScreen
{
UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController];
NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS];
BOOL foundElement = NO;
__block NSString *redboxError = nil;
#ifdef DEBUG
RCTSetLogFunction(
^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) {
if (level >= RCTLogLevelError) {
redboxError = message;
}
});
#endif
while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) {
[[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
[[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
foundElement = [self findSubviewInView:vc.view
matching:^BOOL(UIView *view) {
if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) {
return YES;
}
return NO;
}];
}
#ifdef DEBUG
RCTSetLogFunction(RCTDefaultLogFunction);
#endif
XCTAssertNil(redboxError, @"RedBox error: %@", redboxError);
XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS);
}
@end

52
app/ios/MoproKit/.gitignore vendored Normal file
View File

@@ -0,0 +1,52 @@
# macOS
.DS_Store
# Xcode
build/
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata/
*.xccheckout
*.moved-aside
DerivedData
*.hmap
*.ipa
*.xcuserstate
# Bundler
.bundle
# Add this line if you want to avoid checking in source code from Carthage dependencies.
# Carthage/Checkouts
Carthage/Build
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
#
# Note: if you ignore the Pods directory, make sure to uncomment
# `pod install` in .travis.yml
#
# Pods/
# Xcode
#
/.build
/Packages
/*.xcodeproj
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
# CocoaPods
Podfile.lock
Pods
*.xcworkspace
# Ignore static libs
*.a

View File

@@ -0,0 +1,14 @@
# references:
# * https://www.objc.io/issues/6-build-tools/travis-ci/
# * https://github.com/supermarin/xcpretty#usage
osx_image: xcode7.3
language: objective-c
# cache: cocoapods
# podfile: Example/Podfile
# before_install:
# - gem install cocoapods # Since Travis is not always on latest version
# - pod install --project-directory=Example
script:
- set -o pipefail && xcodebuild test -enableCodeCoverage YES -workspace Example/MoproKit.xcworkspace -scheme MoproKit-Example -sdk iphonesimulator9.3 ONLY_ACTIVE_ARCH=NO | xcpretty
- pod lib lint

View File

@@ -0,0 +1,805 @@
// This file was autogenerated by some hot garbage in the `uniffi` crate.
// Trust me, you don't want to mess with it!
import Foundation
// Depending on the consumer's build setup, the low-level FFI code
// might be in a separate module, or it might be compiled inline into
// this module. This is a bit of light hackery to work with both.
#if canImport(moproFFI)
import moproFFI
#endif
fileprivate extension RustBuffer {
// Allocate a new buffer, copying the contents of a `UInt8` array.
init(bytes: [UInt8]) {
let rbuf = bytes.withUnsafeBufferPointer { ptr in
RustBuffer.from(ptr)
}
self.init(capacity: rbuf.capacity, len: rbuf.len, data: rbuf.data)
}
static func from(_ ptr: UnsafeBufferPointer<UInt8>) -> RustBuffer {
try! rustCall { ffi_mopro_ffi_rustbuffer_from_bytes(ForeignBytes(bufferPointer: ptr), $0) }
}
// Frees the buffer in place.
// The buffer must not be used after this is called.
func deallocate() {
try! rustCall { ffi_mopro_ffi_rustbuffer_free(self, $0) }
}
}
fileprivate extension ForeignBytes {
init(bufferPointer: UnsafeBufferPointer<UInt8>) {
self.init(len: Int32(bufferPointer.count), data: bufferPointer.baseAddress)
}
}
// For every type used in the interface, we provide helper methods for conveniently
// lifting and lowering that type from C-compatible data, and for reading and writing
// values of that type in a buffer.
// Helper classes/extensions that don't change.
// Someday, this will be in a library of its own.
fileprivate extension Data {
init(rustBuffer: RustBuffer) {
// TODO: This copies the buffer. Can we read directly from a
// Rust buffer?
self.init(bytes: rustBuffer.data!, count: Int(rustBuffer.len))
}
}
// Define reader functionality. Normally this would be defined in a class or
// struct, but we use standalone functions instead in order to make external
// types work.
//
// With external types, one swift source file needs to be able to call the read
// method on another source file's FfiConverter, but then what visibility
// should Reader have?
// - If Reader is fileprivate, then this means the read() must also
// be fileprivate, which doesn't work with external types.
// - If Reader is internal/public, we'll get compile errors since both source
// files will try define the same type.
//
// Instead, the read() method and these helper functions input a tuple of data
fileprivate func createReader(data: Data) -> (data: Data, offset: Data.Index) {
(data: data, offset: 0)
}
// Reads an integer at the current offset, in big-endian order, and advances
// the offset on success. Throws if reading the integer would move the
// offset past the end of the buffer.
fileprivate func readInt<T: FixedWidthInteger>(_ reader: inout (data: Data, offset: Data.Index)) throws -> T {
let range = reader.offset..<reader.offset + MemoryLayout<T>.size
guard reader.data.count >= range.upperBound else {
throw UniffiInternalError.bufferOverflow
}
if T.self == UInt8.self {
let value = reader.data[reader.offset]
reader.offset += 1
return value as! T
}
var value: T = 0
let _ = withUnsafeMutableBytes(of: &value, { reader.data.copyBytes(to: $0, from: range)})
reader.offset = range.upperBound
return value.bigEndian
}
// Reads an arbitrary number of bytes, to be used to read
// raw bytes, this is useful when lifting strings
fileprivate func readBytes(_ reader: inout (data: Data, offset: Data.Index), count: Int) throws -> Array<UInt8> {
let range = reader.offset..<(reader.offset+count)
guard reader.data.count >= range.upperBound else {
throw UniffiInternalError.bufferOverflow
}
var value = [UInt8](repeating: 0, count: count)
value.withUnsafeMutableBufferPointer({ buffer in
reader.data.copyBytes(to: buffer, from: range)
})
reader.offset = range.upperBound
return value
}
// Reads a float at the current offset.
fileprivate func readFloat(_ reader: inout (data: Data, offset: Data.Index)) throws -> Float {
return Float(bitPattern: try readInt(&reader))
}
// Reads a float at the current offset.
fileprivate func readDouble(_ reader: inout (data: Data, offset: Data.Index)) throws -> Double {
return Double(bitPattern: try readInt(&reader))
}
// Indicates if the offset has reached the end of the buffer.
fileprivate func hasRemaining(_ reader: (data: Data, offset: Data.Index)) -> Bool {
return reader.offset < reader.data.count
}
// Define writer functionality. Normally this would be defined in a class or
// struct, but we use standalone functions instead in order to make external
// types work. See the above discussion on Readers for details.
fileprivate func createWriter() -> [UInt8] {
return []
}
fileprivate func writeBytes<S>(_ writer: inout [UInt8], _ byteArr: S) where S: Sequence, S.Element == UInt8 {
writer.append(contentsOf: byteArr)
}
// Writes an integer in big-endian order.
//
// Warning: make sure what you are trying to write
// is in the correct type!
fileprivate func writeInt<T: FixedWidthInteger>(_ writer: inout [UInt8], _ value: T) {
var value = value.bigEndian
withUnsafeBytes(of: &value) { writer.append(contentsOf: $0) }
}
fileprivate func writeFloat(_ writer: inout [UInt8], _ value: Float) {
writeInt(&writer, value.bitPattern)
}
fileprivate func writeDouble(_ writer: inout [UInt8], _ value: Double) {
writeInt(&writer, value.bitPattern)
}
// Protocol for types that transfer other types across the FFI. This is
// analogous go the Rust trait of the same name.
fileprivate protocol FfiConverter {
associatedtype FfiType
associatedtype SwiftType
static func lift(_ value: FfiType) throws -> SwiftType
static func lower(_ value: SwiftType) -> FfiType
static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType
static func write(_ value: SwiftType, into buf: inout [UInt8])
}
// Types conforming to `Primitive` pass themselves directly over the FFI.
fileprivate protocol FfiConverterPrimitive: FfiConverter where FfiType == SwiftType { }
extension FfiConverterPrimitive {
public static func lift(_ value: FfiType) throws -> SwiftType {
return value
}
public static func lower(_ value: SwiftType) -> FfiType {
return value
}
}
// Types conforming to `FfiConverterRustBuffer` lift and lower into a `RustBuffer`.
// Used for complex types where it's hard to write a custom lift/lower.
fileprivate protocol FfiConverterRustBuffer: FfiConverter where FfiType == RustBuffer {}
extension FfiConverterRustBuffer {
public static func lift(_ buf: RustBuffer) throws -> SwiftType {
var reader = createReader(data: Data(rustBuffer: buf))
let value = try read(from: &reader)
if hasRemaining(reader) {
throw UniffiInternalError.incompleteData
}
buf.deallocate()
return value
}
public static func lower(_ value: SwiftType) -> RustBuffer {
var writer = createWriter()
write(value, into: &writer)
return RustBuffer(bytes: writer)
}
}
// An error type for FFI errors. These errors occur at the UniFFI level, not
// the library level.
fileprivate enum UniffiInternalError: LocalizedError {
case bufferOverflow
case incompleteData
case unexpectedOptionalTag
case unexpectedEnumCase
case unexpectedNullPointer
case unexpectedRustCallStatusCode
case unexpectedRustCallError
case unexpectedStaleHandle
case rustPanic(_ message: String)
public var errorDescription: String? {
switch self {
case .bufferOverflow: return "Reading the requested value would read past the end of the buffer"
case .incompleteData: return "The buffer still has data after lifting its containing value"
case .unexpectedOptionalTag: return "Unexpected optional tag; should be 0 or 1"
case .unexpectedEnumCase: return "Raw enum value doesn't match any cases"
case .unexpectedNullPointer: return "Raw pointer value was null"
case .unexpectedRustCallStatusCode: return "Unexpected RustCallStatus code"
case .unexpectedRustCallError: return "CALL_ERROR but no errorClass specified"
case .unexpectedStaleHandle: return "The object in the handle map has been dropped already"
case let .rustPanic(message): return message
}
}
}
fileprivate let CALL_SUCCESS: Int8 = 0
fileprivate let CALL_ERROR: Int8 = 1
fileprivate let CALL_PANIC: Int8 = 2
fileprivate let CALL_CANCELLED: Int8 = 3
fileprivate extension RustCallStatus {
init() {
self.init(
code: CALL_SUCCESS,
errorBuf: RustBuffer.init(
capacity: 0,
len: 0,
data: nil
)
)
}
}
private func rustCall<T>(_ callback: (UnsafeMutablePointer<RustCallStatus>) -> T) throws -> T {
try makeRustCall(callback, errorHandler: nil)
}
private func rustCallWithError<T>(
_ errorHandler: @escaping (RustBuffer) throws -> Error,
_ callback: (UnsafeMutablePointer<RustCallStatus>) -> T) throws -> T {
try makeRustCall(callback, errorHandler: errorHandler)
}
private func makeRustCall<T>(
_ callback: (UnsafeMutablePointer<RustCallStatus>) -> T,
errorHandler: ((RustBuffer) throws -> Error)?
) throws -> T {
uniffiEnsureInitialized()
var callStatus = RustCallStatus.init()
let returnedVal = callback(&callStatus)
try uniffiCheckCallStatus(callStatus: callStatus, errorHandler: errorHandler)
return returnedVal
}
private func uniffiCheckCallStatus(
callStatus: RustCallStatus,
errorHandler: ((RustBuffer) throws -> Error)?
) throws {
switch callStatus.code {
case CALL_SUCCESS:
return
case CALL_ERROR:
if let errorHandler = errorHandler {
throw try errorHandler(callStatus.errorBuf)
} else {
callStatus.errorBuf.deallocate()
throw UniffiInternalError.unexpectedRustCallError
}
case CALL_PANIC:
// When the rust code sees a panic, it tries to construct a RustBuffer
// with the message. But if that code panics, then it just sends back
// an empty buffer.
if callStatus.errorBuf.len > 0 {
throw UniffiInternalError.rustPanic(try FfiConverterString.lift(callStatus.errorBuf))
} else {
callStatus.errorBuf.deallocate()
throw UniffiInternalError.rustPanic("Rust panic")
}
case CALL_CANCELLED:
throw CancellationError()
default:
throw UniffiInternalError.unexpectedRustCallStatusCode
}
}
// Public interface members begin here.
fileprivate struct FfiConverterUInt32: FfiConverterPrimitive {
typealias FfiType = UInt32
typealias SwiftType = UInt32
public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UInt32 {
return try lift(readInt(&buf))
}
public static func write(_ value: SwiftType, into buf: inout [UInt8]) {
writeInt(&buf, lower(value))
}
}
fileprivate struct FfiConverterBool : FfiConverter {
typealias FfiType = Int8
typealias SwiftType = Bool
public static func lift(_ value: Int8) throws -> Bool {
return value != 0
}
public static func lower(_ value: Bool) -> Int8 {
return value ? 1 : 0
}
public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Bool {
return try lift(readInt(&buf))
}
public static func write(_ value: Bool, into buf: inout [UInt8]) {
writeInt(&buf, lower(value))
}
}
fileprivate struct FfiConverterString: FfiConverter {
typealias SwiftType = String
typealias FfiType = RustBuffer
public static func lift(_ value: RustBuffer) throws -> String {
defer {
value.deallocate()
}
if value.data == nil {
return String()
}
let bytes = UnsafeBufferPointer<UInt8>(start: value.data!, count: Int(value.len))
return String(bytes: bytes, encoding: String.Encoding.utf8)!
}
public static func lower(_ value: String) -> RustBuffer {
return value.utf8CString.withUnsafeBufferPointer { ptr in
// The swift string gives us int8_t, we want uint8_t.
ptr.withMemoryRebound(to: UInt8.self) { ptr in
// The swift string gives us a trailing null byte, we don't want it.
let buf = UnsafeBufferPointer(rebasing: ptr.prefix(upTo: ptr.count - 1))
return RustBuffer.from(buf)
}
}
}
public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> String {
let len: Int32 = try readInt(&buf)
return String(bytes: try readBytes(&buf, count: Int(len)), encoding: String.Encoding.utf8)!
}
public static func write(_ value: String, into buf: inout [UInt8]) {
let len = Int32(value.utf8.count)
writeInt(&buf, len)
writeBytes(&buf, value.utf8)
}
}
fileprivate struct FfiConverterData: FfiConverterRustBuffer {
typealias SwiftType = Data
public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Data {
let len: Int32 = try readInt(&buf)
return Data(try readBytes(&buf, count: Int(len)))
}
public static func write(_ value: Data, into buf: inout [UInt8]) {
let len = Int32(value.count)
writeInt(&buf, len)
writeBytes(&buf, value)
}
}
public protocol MoproCircomProtocol {
func generateProof(circuitInputs: [String: [String]]) throws -> GenerateProofResult
func setup(wasmPath: String, r1csPath: String) throws -> SetupResult
func verifyProof(proof: Data, publicInput: Data) throws -> Bool
}
public class MoproCircom: MoproCircomProtocol {
fileprivate let pointer: UnsafeMutableRawPointer
// TODO: We'd like this to be `private` but for Swifty reasons,
// we can't implement `FfiConverter` without making this `required` and we can't
// make it `required` without making it `public`.
required init(unsafeFromRawPointer pointer: UnsafeMutableRawPointer) {
self.pointer = pointer
}
public convenience init() {
self.init(unsafeFromRawPointer: try! rustCall() {
uniffi_mopro_ffi_fn_constructor_moprocircom_new($0)
})
}
deinit {
try! rustCall { uniffi_mopro_ffi_fn_free_moprocircom(pointer, $0) }
}
public func generateProof(circuitInputs: [String: [String]]) throws -> GenerateProofResult {
return try FfiConverterTypeGenerateProofResult.lift(
try
rustCallWithError(FfiConverterTypeMoproError.lift) {
uniffi_mopro_ffi_fn_method_moprocircom_generate_proof(self.pointer,
FfiConverterDictionaryStringSequenceString.lower(circuitInputs),$0
)
}
)
}
public func setup(wasmPath: String, r1csPath: String) throws -> SetupResult {
return try FfiConverterTypeSetupResult.lift(
try
rustCallWithError(FfiConverterTypeMoproError.lift) {
uniffi_mopro_ffi_fn_method_moprocircom_setup(self.pointer,
FfiConverterString.lower(wasmPath),
FfiConverterString.lower(r1csPath),$0
)
}
)
}
public func verifyProof(proof: Data, publicInput: Data) throws -> Bool {
return try FfiConverterBool.lift(
try
rustCallWithError(FfiConverterTypeMoproError.lift) {
uniffi_mopro_ffi_fn_method_moprocircom_verify_proof(self.pointer,
FfiConverterData.lower(proof),
FfiConverterData.lower(publicInput),$0
)
}
)
}
}
public struct FfiConverterTypeMoproCircom: FfiConverter {
typealias FfiType = UnsafeMutableRawPointer
typealias SwiftType = MoproCircom
public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> MoproCircom {
let v: UInt64 = try readInt(&buf)
// The Rust code won't compile if a pointer won't fit in a UInt64.
// We have to go via `UInt` because that's the thing that's the size of a pointer.
let ptr = UnsafeMutableRawPointer(bitPattern: UInt(truncatingIfNeeded: v))
if (ptr == nil) {
throw UniffiInternalError.unexpectedNullPointer
}
return try lift(ptr!)
}
public static func write(_ value: MoproCircom, into buf: inout [UInt8]) {
// This fiddling is because `Int` is the thing that's the same size as a pointer.
// The Rust code won't compile if a pointer won't fit in a `UInt64`.
writeInt(&buf, UInt64(bitPattern: Int64(Int(bitPattern: lower(value)))))
}
public static func lift(_ pointer: UnsafeMutableRawPointer) throws -> MoproCircom {
return MoproCircom(unsafeFromRawPointer: pointer)
}
public static func lower(_ value: MoproCircom) -> UnsafeMutableRawPointer {
return value.pointer
}
}
public func FfiConverterTypeMoproCircom_lift(_ pointer: UnsafeMutableRawPointer) throws -> MoproCircom {
return try FfiConverterTypeMoproCircom.lift(pointer)
}
public func FfiConverterTypeMoproCircom_lower(_ value: MoproCircom) -> UnsafeMutableRawPointer {
return FfiConverterTypeMoproCircom.lower(value)
}
public struct GenerateProofResult {
public var proof: Data
public var inputs: Data
// Default memberwise initializers are never public by default, so we
// declare one manually.
public init(proof: Data, inputs: Data) {
self.proof = proof
self.inputs = inputs
}
}
extension GenerateProofResult: Equatable, Hashable {
public static func ==(lhs: GenerateProofResult, rhs: GenerateProofResult) -> Bool {
if lhs.proof != rhs.proof {
return false
}
if lhs.inputs != rhs.inputs {
return false
}
return true
}
public func hash(into hasher: inout Hasher) {
hasher.combine(proof)
hasher.combine(inputs)
}
}
public struct FfiConverterTypeGenerateProofResult: FfiConverterRustBuffer {
public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> GenerateProofResult {
return try GenerateProofResult(
proof: FfiConverterData.read(from: &buf),
inputs: FfiConverterData.read(from: &buf)
)
}
public static func write(_ value: GenerateProofResult, into buf: inout [UInt8]) {
FfiConverterData.write(value.proof, into: &buf)
FfiConverterData.write(value.inputs, into: &buf)
}
}
public func FfiConverterTypeGenerateProofResult_lift(_ buf: RustBuffer) throws -> GenerateProofResult {
return try FfiConverterTypeGenerateProofResult.lift(buf)
}
public func FfiConverterTypeGenerateProofResult_lower(_ value: GenerateProofResult) -> RustBuffer {
return FfiConverterTypeGenerateProofResult.lower(value)
}
public struct SetupResult {
public var provingKey: Data
// Default memberwise initializers are never public by default, so we
// declare one manually.
public init(provingKey: Data) {
self.provingKey = provingKey
}
}
extension SetupResult: Equatable, Hashable {
public static func ==(lhs: SetupResult, rhs: SetupResult) -> Bool {
if lhs.provingKey != rhs.provingKey {
return false
}
return true
}
public func hash(into hasher: inout Hasher) {
hasher.combine(provingKey)
}
}
public struct FfiConverterTypeSetupResult: FfiConverterRustBuffer {
public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SetupResult {
return try SetupResult(
provingKey: FfiConverterData.read(from: &buf)
)
}
public static func write(_ value: SetupResult, into buf: inout [UInt8]) {
FfiConverterData.write(value.provingKey, into: &buf)
}
}
public func FfiConverterTypeSetupResult_lift(_ buf: RustBuffer) throws -> SetupResult {
return try FfiConverterTypeSetupResult.lift(buf)
}
public func FfiConverterTypeSetupResult_lower(_ value: SetupResult) -> RustBuffer {
return FfiConverterTypeSetupResult.lower(value)
}
public enum MoproError {
// Simple error enums only carry a message
case CircomError(message: String)
fileprivate static func uniffiErrorHandler(_ error: RustBuffer) throws -> Error {
return try FfiConverterTypeMoproError.lift(error)
}
}
public struct FfiConverterTypeMoproError: FfiConverterRustBuffer {
typealias SwiftType = MoproError
public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> MoproError {
let variant: Int32 = try readInt(&buf)
switch variant {
case 1: return .CircomError(
message: try FfiConverterString.read(from: &buf)
)
default: throw UniffiInternalError.unexpectedEnumCase
}
}
public static func write(_ value: MoproError, into buf: inout [UInt8]) {
switch value {
case .CircomError(_ /* message is ignored*/):
writeInt(&buf, Int32(1))
}
}
}
extension MoproError: Equatable, Hashable {}
extension MoproError: Error { }
fileprivate struct FfiConverterSequenceString: FfiConverterRustBuffer {
typealias SwiftType = [String]
public static func write(_ value: [String], into buf: inout [UInt8]) {
let len = Int32(value.count)
writeInt(&buf, len)
for item in value {
FfiConverterString.write(item, into: &buf)
}
}
public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> [String] {
let len: Int32 = try readInt(&buf)
var seq = [String]()
seq.reserveCapacity(Int(len))
for _ in 0 ..< len {
seq.append(try FfiConverterString.read(from: &buf))
}
return seq
}
}
fileprivate struct FfiConverterDictionaryStringSequenceString: FfiConverterRustBuffer {
public static func write(_ value: [String: [String]], into buf: inout [UInt8]) {
let len = Int32(value.count)
writeInt(&buf, len)
for (key, value) in value {
FfiConverterString.write(key, into: &buf)
FfiConverterSequenceString.write(value, into: &buf)
}
}
public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> [String: [String]] {
let len: Int32 = try readInt(&buf)
var dict = [String: [String]]()
dict.reserveCapacity(Int(len))
for _ in 0..<len {
let key = try FfiConverterString.read(from: &buf)
let value = try FfiConverterSequenceString.read(from: &buf)
dict[key] = value
}
return dict
}
}
public func add(a: UInt32, b: UInt32) -> UInt32 {
return try! FfiConverterUInt32.lift(
try! rustCall() {
uniffi_mopro_ffi_fn_func_add(
FfiConverterUInt32.lower(a),
FfiConverterUInt32.lower(b),$0)
}
)
}
public func generateProof2(circuitInputs: [String: [String]]) throws -> GenerateProofResult {
return try FfiConverterTypeGenerateProofResult.lift(
try rustCallWithError(FfiConverterTypeMoproError.lift) {
uniffi_mopro_ffi_fn_func_generate_proof2(
FfiConverterDictionaryStringSequenceString.lower(circuitInputs),$0)
}
)
}
public func hello() -> String {
return try! FfiConverterString.lift(
try! rustCall() {
uniffi_mopro_ffi_fn_func_hello($0)
}
)
}
public func initializeMopro() throws {
try rustCallWithError(FfiConverterTypeMoproError.lift) {
uniffi_mopro_ffi_fn_func_initialize_mopro($0)
}
}
public func initializeMoproDylib(dylibPath: String) throws {
try rustCallWithError(FfiConverterTypeMoproError.lift) {
uniffi_mopro_ffi_fn_func_initialize_mopro_dylib(
FfiConverterString.lower(dylibPath),$0)
}
}
public func verifyProof2(proof: Data, publicInput: Data) throws -> Bool {
return try FfiConverterBool.lift(
try rustCallWithError(FfiConverterTypeMoproError.lift) {
uniffi_mopro_ffi_fn_func_verify_proof2(
FfiConverterData.lower(proof),
FfiConverterData.lower(publicInput),$0)
}
)
}
private enum InitializationResult {
case ok
case contractVersionMismatch
case apiChecksumMismatch
}
// Use a global variables to perform the versioning checks. Swift ensures that
// the code inside is only computed once.
private var initializationResult: InitializationResult {
// Get the bindings contract version from our ComponentInterface
let bindings_contract_version = 24
// Get the scaffolding contract version by calling the into the dylib
let scaffolding_contract_version = ffi_mopro_ffi_uniffi_contract_version()
if bindings_contract_version != scaffolding_contract_version {
return InitializationResult.contractVersionMismatch
}
if (uniffi_mopro_ffi_checksum_func_add() != 8411) {
return InitializationResult.apiChecksumMismatch
}
if (uniffi_mopro_ffi_checksum_func_generate_proof2() != 40187) {
return InitializationResult.apiChecksumMismatch
}
if (uniffi_mopro_ffi_checksum_func_hello() != 46136) {
return InitializationResult.apiChecksumMismatch
}
if (uniffi_mopro_ffi_checksum_func_initialize_mopro() != 17540) {
return InitializationResult.apiChecksumMismatch
}
if (uniffi_mopro_ffi_checksum_func_initialize_mopro_dylib() != 64476) {
return InitializationResult.apiChecksumMismatch
}
if (uniffi_mopro_ffi_checksum_func_verify_proof2() != 37192) {
return InitializationResult.apiChecksumMismatch
}
if (uniffi_mopro_ffi_checksum_method_moprocircom_generate_proof() != 64602) {
return InitializationResult.apiChecksumMismatch
}
if (uniffi_mopro_ffi_checksum_method_moprocircom_setup() != 57700) {
return InitializationResult.apiChecksumMismatch
}
if (uniffi_mopro_ffi_checksum_method_moprocircom_verify_proof() != 61522) {
return InitializationResult.apiChecksumMismatch
}
if (uniffi_mopro_ffi_checksum_constructor_moprocircom_new() != 42205) {
return InitializationResult.apiChecksumMismatch
}
return InitializationResult.ok
}
private func uniffiEnsureInitialized() {
switch initializationResult {
case .ok:
break
case .contractVersionMismatch:
fatalError("UniFFI contract version mismatch: try cleaning and rebuilding your project")
case .apiChecksumMismatch:
fatalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
}
}

View File

@@ -0,0 +1,670 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
2A418AB02AF4B1200004B747 /* CircomUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A418AAF2AF4B1200004B747 /* CircomUITests.swift */; };
2A6E5BAF2AF499460052A601 /* CircomTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A6E5BAE2AF499460052A601 /* CircomTests.swift */; };
4384FD09A96F702A375841EE /* Pods_MoproKit_Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 78B0F9CBE5DD22576996A993 /* Pods_MoproKit_Tests.framework */; };
607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD51AFB9204008FA782 /* AppDelegate.swift */; };
607FACD81AFB9204008FA782 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD71AFB9204008FA782 /* ViewController.swift */; };
607FACDB1AFB9204008FA782 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 607FACD91AFB9204008FA782 /* Main.storyboard */; };
607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDC1AFB9204008FA782 /* Images.xcassets */; };
607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */; };
7A38AEC233A3880A843B0133 /* Pods_MoproKit_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BE5A40EAC7D019B9C7566CA /* Pods_MoproKit_Example.framework */; };
CE2C1B8C2AFFCC5E002AF8BC /* main.wasm in Resources */ = {isa = PBXBuildFile; fileRef = CE2C1B8B2AFFCC5E002AF8BC /* main.wasm */; };
CE2C1B8D2AFFCC5E002AF8BC /* main.wasm in Resources */ = {isa = PBXBuildFile; fileRef = CE2C1B8B2AFFCC5E002AF8BC /* main.wasm */; };
CE5A5C062AD43A790074539D /* keccak256_256_test.wasm in Resources */ = {isa = PBXBuildFile; fileRef = CE5A5C052AD43A790074539D /* keccak256_256_test.wasm */; };
CE5A5C072AD43A790074539D /* keccak256_256_test.wasm in Resources */ = {isa = PBXBuildFile; fileRef = CE5A5C052AD43A790074539D /* keccak256_256_test.wasm */; };
CE5A5C092AD43A860074539D /* keccak256_256_test.r1cs in Resources */ = {isa = PBXBuildFile; fileRef = CE5A5C082AD43A860074539D /* keccak256_256_test.r1cs */; };
CE5A5C0A2AD43A860074539D /* keccak256_256_test.r1cs in Resources */ = {isa = PBXBuildFile; fileRef = CE5A5C082AD43A860074539D /* keccak256_256_test.r1cs */; };
CEA2D12F2AB96A7A00F292D2 /* multiplier2.wasm in Resources */ = {isa = PBXBuildFile; fileRef = CEA2D12E2AB96A7A00F292D2 /* multiplier2.wasm */; };
CEA2D1302AB96A7A00F292D2 /* multiplier2.wasm in Resources */ = {isa = PBXBuildFile; fileRef = CEA2D12E2AB96A7A00F292D2 /* multiplier2.wasm */; };
CEA2D1322AB96AB500F292D2 /* multiplier2.r1cs in Resources */ = {isa = PBXBuildFile; fileRef = CEA2D1312AB96AB500F292D2 /* multiplier2.r1cs */; };
CEA2D1332AB96AB500F292D2 /* multiplier2.r1cs in Resources */ = {isa = PBXBuildFile; fileRef = CEA2D1312AB96AB500F292D2 /* multiplier2.r1cs */; };
CEB804502AFF81960063F091 /* KeccakSetupViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEB8044F2AFF81960063F091 /* KeccakSetupViewController.swift */; };
CEB804562AFF81AF0063F091 /* KeccakZkeyViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEB804552AFF81AF0063F091 /* KeccakZkeyViewController.swift */; };
CEB804582AFF81BF0063F091 /* RSAViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEB804572AFF81BF0063F091 /* RSAViewController.swift */; };
E69338642AFFDB1A00B80312 /* AnonAadhaarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E69338632AFFDB1A00B80312 /* AnonAadhaarViewController.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
607FACE61AFB9204008FA782 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 607FACC81AFB9204008FA782 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 607FACCF1AFB9204008FA782;
remoteInfo = MoproKit;
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
1E5E014D70B48C9A59F14658 /* Pods-MoproKit_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MoproKit_Example.release.xcconfig"; path = "Target Support Files/Pods-MoproKit_Example/Pods-MoproKit_Example.release.xcconfig"; sourceTree = "<group>"; };
2A418AAF2AF4B1200004B747 /* CircomUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircomUITests.swift; sourceTree = "<group>"; };
2A6E5BAE2AF499460052A601 /* CircomTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircomTests.swift; sourceTree = "<group>"; };
47F8ADB0AC4168C6E874818D /* MoproKit.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = MoproKit.podspec; path = ../MoproKit.podspec; sourceTree = "<group>"; };
5DAF212A114DFA0C9F4282B2 /* Pods-MoproKit_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MoproKit_Tests.debug.xcconfig"; path = "Target Support Files/Pods-MoproKit_Tests/Pods-MoproKit_Tests.debug.xcconfig"; sourceTree = "<group>"; };
607FACD01AFB9204008FA782 /* MoproKit_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MoproKit_Example.app; sourceTree = BUILT_PRODUCTS_DIR; };
607FACD41AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
607FACD51AFB9204008FA782 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
607FACD71AFB9204008FA782 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
607FACDA1AFB9204008FA782 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
607FACDC1AFB9204008FA782 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
607FACDF1AFB9204008FA782 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = "<group>"; };
607FACE51AFB9204008FA782 /* MoproKit_Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MoproKit_Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
607FACEA1AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
78B0F9CBE5DD22576996A993 /* Pods_MoproKit_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_MoproKit_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
8BE5A40EAC7D019B9C7566CA /* Pods_MoproKit_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_MoproKit_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; };
90EAF1BEF8AD3C193665DBED /* Pods-MoproKit_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MoproKit_Tests.release.xcconfig"; path = "Target Support Files/Pods-MoproKit_Tests/Pods-MoproKit_Tests.release.xcconfig"; sourceTree = "<group>"; };
92580ABC3B6DBAD1A9544456 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = "<group>"; };
B4016A34BBB20BC381CCFC2D /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = "<group>"; };
C61628A63419B5C140B24AF7 /* Pods-MoproKit_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MoproKit_Example.debug.xcconfig"; path = "Target Support Files/Pods-MoproKit_Example/Pods-MoproKit_Example.debug.xcconfig"; sourceTree = "<group>"; };
CE2C1B8B2AFFCC5E002AF8BC /* main.wasm */ = {isa = PBXFileReference; lastKnownFileType = file; name = main.wasm; path = "../../../../../mopro-core/examples/circom/rsa/target/main_js/main.wasm"; sourceTree = "<group>"; };
CE5A5C052AD43A790074539D /* keccak256_256_test.wasm */ = {isa = PBXFileReference; lastKnownFileType = file; name = keccak256_256_test.wasm; path = "../../../../../mopro-core/examples/circom/keccak256/target/keccak256_256_test_js/keccak256_256_test.wasm"; sourceTree = "<group>"; };
CE5A5C082AD43A860074539D /* keccak256_256_test.r1cs */ = {isa = PBXFileReference; lastKnownFileType = file; name = keccak256_256_test.r1cs; path = "../../../../../mopro-core/examples/circom/keccak256/target/keccak256_256_test.r1cs"; sourceTree = "<group>"; };
CEA2D12E2AB96A7A00F292D2 /* multiplier2.wasm */ = {isa = PBXFileReference; lastKnownFileType = file; name = multiplier2.wasm; path = "../../../../../mopro-core/examples/circom/multiplier2/target/multiplier2_js/multiplier2.wasm"; sourceTree = "<group>"; };
CEA2D1312AB96AB500F292D2 /* multiplier2.r1cs */ = {isa = PBXFileReference; lastKnownFileType = file; name = multiplier2.r1cs; path = "../../../../../mopro-core/examples/circom/multiplier2/target/multiplier2.r1cs"; sourceTree = "<group>"; };
CEB8044F2AFF81960063F091 /* KeccakSetupViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeccakSetupViewController.swift; sourceTree = "<group>"; };
CEB804552AFF81AF0063F091 /* KeccakZkeyViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeccakZkeyViewController.swift; sourceTree = "<group>"; };
CEB804572AFF81BF0063F091 /* RSAViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RSAViewController.swift; sourceTree = "<group>"; };
E69338632AFFDB1A00B80312 /* AnonAadhaarViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnonAadhaarViewController.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
607FACCD1AFB9204008FA782 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
7A38AEC233A3880A843B0133 /* Pods_MoproKit_Example.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
607FACE21AFB9204008FA782 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
4384FD09A96F702A375841EE /* Pods_MoproKit_Tests.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
164B820D208F5EFE221D6A86 /* Frameworks */ = {
isa = PBXGroup;
children = (
8BE5A40EAC7D019B9C7566CA /* Pods_MoproKit_Example.framework */,
78B0F9CBE5DD22576996A993 /* Pods_MoproKit_Tests.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
607FACC71AFB9204008FA782 = {
isa = PBXGroup;
children = (
607FACF51AFB993E008FA782 /* Podspec Metadata */,
607FACD21AFB9204008FA782 /* Example for MoproKit */,
607FACE81AFB9204008FA782 /* Tests */,
607FACD11AFB9204008FA782 /* Products */,
EEB809FA0FFBEC7559BC7169 /* Pods */,
164B820D208F5EFE221D6A86 /* Frameworks */,
);
sourceTree = "<group>";
};
607FACD11AFB9204008FA782 /* Products */ = {
isa = PBXGroup;
children = (
607FACD01AFB9204008FA782 /* MoproKit_Example.app */,
607FACE51AFB9204008FA782 /* MoproKit_Tests.xctest */,
);
name = Products;
sourceTree = "<group>";
};
607FACD21AFB9204008FA782 /* Example for MoproKit */ = {
isa = PBXGroup;
children = (
CEB804572AFF81BF0063F091 /* RSAViewController.swift */,
E69338632AFFDB1A00B80312 /* AnonAadhaarViewController.swift */,
CEB804552AFF81AF0063F091 /* KeccakZkeyViewController.swift */,
CEB8044F2AFF81960063F091 /* KeccakSetupViewController.swift */,
CEA2D1282AB9681E00F292D2 /* Resources */,
607FACD51AFB9204008FA782 /* AppDelegate.swift */,
607FACD71AFB9204008FA782 /* ViewController.swift */,
607FACD91AFB9204008FA782 /* Main.storyboard */,
607FACDC1AFB9204008FA782 /* Images.xcassets */,
607FACDE1AFB9204008FA782 /* LaunchScreen.xib */,
607FACD31AFB9204008FA782 /* Supporting Files */,
);
name = "Example for MoproKit";
path = MoproKit;
sourceTree = "<group>";
};
607FACD31AFB9204008FA782 /* Supporting Files */ = {
isa = PBXGroup;
children = (
607FACD41AFB9204008FA782 /* Info.plist */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
607FACE81AFB9204008FA782 /* Tests */ = {
isa = PBXGroup;
children = (
2A418AAF2AF4B1200004B747 /* CircomUITests.swift */,
607FACE91AFB9204008FA782 /* Supporting Files */,
2A6E5BAE2AF499460052A601 /* CircomTests.swift */,
);
path = Tests;
sourceTree = "<group>";
};
607FACE91AFB9204008FA782 /* Supporting Files */ = {
isa = PBXGroup;
children = (
607FACEA1AFB9204008FA782 /* Info.plist */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
607FACF51AFB993E008FA782 /* Podspec Metadata */ = {
isa = PBXGroup;
children = (
47F8ADB0AC4168C6E874818D /* MoproKit.podspec */,
B4016A34BBB20BC381CCFC2D /* README.md */,
92580ABC3B6DBAD1A9544456 /* LICENSE */,
);
name = "Podspec Metadata";
sourceTree = "<group>";
};
CEA2D1282AB9681E00F292D2 /* Resources */ = {
isa = PBXGroup;
children = (
CE2C1B8B2AFFCC5E002AF8BC /* main.wasm */,
CE5A5C082AD43A860074539D /* keccak256_256_test.r1cs */,
CE5A5C052AD43A790074539D /* keccak256_256_test.wasm */,
CEA2D12E2AB96A7A00F292D2 /* multiplier2.wasm */,
CEA2D1312AB96AB500F292D2 /* multiplier2.r1cs */,
);
path = Resources;
sourceTree = "<group>";
};
EEB809FA0FFBEC7559BC7169 /* Pods */ = {
isa = PBXGroup;
children = (
C61628A63419B5C140B24AF7 /* Pods-MoproKit_Example.debug.xcconfig */,
1E5E014D70B48C9A59F14658 /* Pods-MoproKit_Example.release.xcconfig */,
5DAF212A114DFA0C9F4282B2 /* Pods-MoproKit_Tests.debug.xcconfig */,
90EAF1BEF8AD3C193665DBED /* Pods-MoproKit_Tests.release.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
607FACCF1AFB9204008FA782 /* MoproKit_Example */ = {
isa = PBXNativeTarget;
buildConfigurationList = 607FACEF1AFB9204008FA782 /* Build configuration list for PBXNativeTarget "MoproKit_Example" */;
buildPhases = (
C880BC635097821F74482E9F /* [CP] Check Pods Manifest.lock */,
607FACCC1AFB9204008FA782 /* Sources */,
607FACCD1AFB9204008FA782 /* Frameworks */,
607FACCE1AFB9204008FA782 /* Resources */,
AE90EE4FFCA95237FBC047A3 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = MoproKit_Example;
productName = MoproKit;
productReference = 607FACD01AFB9204008FA782 /* MoproKit_Example.app */;
productType = "com.apple.product-type.application";
};
607FACE41AFB9204008FA782 /* MoproKit_Tests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 607FACF21AFB9204008FA782 /* Build configuration list for PBXNativeTarget "MoproKit_Tests" */;
buildPhases = (
2A673A4155C4EBC1B609897B /* [CP] Check Pods Manifest.lock */,
607FACE11AFB9204008FA782 /* Sources */,
607FACE21AFB9204008FA782 /* Frameworks */,
607FACE31AFB9204008FA782 /* Resources */,
2B1BB55A83D0E5B7D03D5DA0 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
dependencies = (
607FACE71AFB9204008FA782 /* PBXTargetDependency */,
);
name = MoproKit_Tests;
productName = Tests;
productReference = 607FACE51AFB9204008FA782 /* MoproKit_Tests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
607FACC81AFB9204008FA782 /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0830;
LastUpgradeCheck = 0830;
ORGANIZATIONNAME = CocoaPods;
TargetAttributes = {
607FACCF1AFB9204008FA782 = {
CreatedOnToolsVersion = 6.3.1;
DevelopmentTeam = 5B29R5LYHQ;
LastSwiftMigration = 0900;
};
607FACE41AFB9204008FA782 = {
CreatedOnToolsVersion = 6.3.1;
DevelopmentTeam = 5B29R5LYHQ;
LastSwiftMigration = 0900;
TestTargetID = 607FACCF1AFB9204008FA782;
};
};
};
buildConfigurationList = 607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "MoproKit" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
English,
en,
Base,
);
mainGroup = 607FACC71AFB9204008FA782;
productRefGroup = 607FACD11AFB9204008FA782 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
607FACCF1AFB9204008FA782 /* MoproKit_Example */,
607FACE41AFB9204008FA782 /* MoproKit_Tests */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
607FACCE1AFB9204008FA782 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
CEA2D1322AB96AB500F292D2 /* multiplier2.r1cs in Resources */,
607FACDB1AFB9204008FA782 /* Main.storyboard in Resources */,
607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */,
CE5A5C062AD43A790074539D /* keccak256_256_test.wasm in Resources */,
CE2C1B8C2AFFCC5E002AF8BC /* main.wasm in Resources */,
607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */,
CEA2D12F2AB96A7A00F292D2 /* multiplier2.wasm in Resources */,
CE5A5C092AD43A860074539D /* keccak256_256_test.r1cs in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
607FACE31AFB9204008FA782 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
CE5A5C0A2AD43A860074539D /* keccak256_256_test.r1cs in Resources */,
CE5A5C072AD43A790074539D /* keccak256_256_test.wasm in Resources */,
CE2C1B8D2AFFCC5E002AF8BC /* main.wasm in Resources */,
CEA2D1332AB96AB500F292D2 /* multiplier2.r1cs in Resources */,
CEA2D1302AB96A7A00F292D2 /* multiplier2.wasm in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
2A673A4155C4EBC1B609897B /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-MoproKit_Tests-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
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;
};
2B1BB55A83D0E5B7D03D5DA0 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-MoproKit_Tests/Pods-MoproKit_Tests-frameworks.sh",
"${BUILT_PRODUCTS_DIR}/Nimble/Nimble.framework",
"${BUILT_PRODUCTS_DIR}/Quick/Quick.framework",
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Nimble.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Quick.framework",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-MoproKit_Tests/Pods-MoproKit_Tests-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
AE90EE4FFCA95237FBC047A3 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-MoproKit_Example/Pods-MoproKit_Example-frameworks.sh",
"${BUILT_PRODUCTS_DIR}/MoproKit/MoproKit.framework",
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MoproKit.framework",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-MoproKit_Example/Pods-MoproKit_Example-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
C880BC635097821F74482E9F /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-MoproKit_Example-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
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;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
607FACCC1AFB9204008FA782 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
CEB804582AFF81BF0063F091 /* RSAViewController.swift in Sources */,
CEB804562AFF81AF0063F091 /* KeccakZkeyViewController.swift in Sources */,
607FACD81AFB9204008FA782 /* ViewController.swift in Sources */,
CEB804502AFF81960063F091 /* KeccakSetupViewController.swift in Sources */,
607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */,
E69338642AFFDB1A00B80312 /* AnonAadhaarViewController.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
607FACE11AFB9204008FA782 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
2A6E5BAF2AF499460052A601 /* CircomTests.swift in Sources */,
2A418AB02AF4B1200004B747 /* CircomUITests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
607FACE71AFB9204008FA782 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 607FACCF1AFB9204008FA782 /* MoproKit_Example */;
targetProxy = 607FACE61AFB9204008FA782 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
607FACD91AFB9204008FA782 /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
607FACDA1AFB9204008FA782 /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
607FACDE1AFB9204008FA782 /* LaunchScreen.xib */ = {
isa = PBXVariantGroup;
children = (
607FACDF1AFB9204008FA782 /* Base */,
);
name = LaunchScreen.xib;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
607FACED1AFB9204008FA782 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
};
name = Debug;
};
607FACEE1AFB9204008FA782 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
607FACF01AFB9204008FA782 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = C61628A63419B5C140B24AF7 /* Pods-MoproKit_Example.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
DEVELOPMENT_TEAM = 5B29R5LYHQ;
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
INFOPLIST_FILE = MoproKit/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
MODULE_NAME = ExampleApp;
PRODUCT_BUNDLE_IDENTIFIER = org.mopro.examples;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
SWIFT_VERSION = 4.0;
};
name = Debug;
};
607FACF11AFB9204008FA782 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 1E5E014D70B48C9A59F14658 /* Pods-MoproKit_Example.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
DEVELOPMENT_TEAM = 5B29R5LYHQ;
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
INFOPLIST_FILE = MoproKit/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
MODULE_NAME = ExampleApp;
PRODUCT_BUNDLE_IDENTIFIER = org.mopro.examples;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
SWIFT_VERSION = 4.0;
};
name = Release;
};
607FACF31AFB9204008FA782 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 5DAF212A114DFA0C9F4282B2 /* Pods-MoproKit_Tests.debug.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/MoproKit_Example.app/MoproKit_Example";
DEVELOPMENT_TEAM = 5B29R5LYHQ;
FRAMEWORK_SEARCH_PATHS = (
"$(PLATFORM_DIR)/Developer/Library/Frameworks",
"$(inherited)",
);
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
INFOPLIST_FILE = Tests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = org.mopro.examples.test;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
SWIFT_VERSION = 4.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/MoproKit_Example.app/MoproKit_Example";
};
name = Debug;
};
607FACF41AFB9204008FA782 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 90EAF1BEF8AD3C193665DBED /* Pods-MoproKit_Tests.release.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/MoproKit_Example.app/MoproKit_Example";
DEVELOPMENT_TEAM = 5B29R5LYHQ;
FRAMEWORK_SEARCH_PATHS = (
"$(PLATFORM_DIR)/Developer/Library/Frameworks",
"$(inherited)",
);
INFOPLIST_FILE = Tests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = org.mopro.examples.test;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
SWIFT_VERSION = 4.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/MoproKit_Example.app/MoproKit_Example";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "MoproKit" */ = {
isa = XCConfigurationList;
buildConfigurations = (
607FACED1AFB9204008FA782 /* Debug */,
607FACEE1AFB9204008FA782 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
607FACEF1AFB9204008FA782 /* Build configuration list for PBXNativeTarget "MoproKit_Example" */ = {
isa = XCConfigurationList;
buildConfigurations = (
607FACF01AFB9204008FA782 /* Debug */,
607FACF11AFB9204008FA782 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
607FACF21AFB9204008FA782 /* Build configuration list for PBXNativeTarget "MoproKit_Tests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
607FACF31AFB9204008FA782 /* Debug */,
607FACF41AFB9204008FA782 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 607FACC81AFB9204008FA782 /* Project object */;
}

View File

@@ -0,0 +1,129 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0900"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "607FACCF1AFB9204008FA782"
BuildableName = "MoproKit_Example.app"
BlueprintName = "MoproKit_Example"
ReferencedContainer = "container:MoproKit.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "607FACE41AFB9204008FA782"
BuildableName = "MoproKit_Tests.xctest"
BlueprintName = "MoproKit_Tests"
ReferencedContainer = "container:MoproKit.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
codeCoverageEnabled = "YES"
onlyGenerateCoverageForSpecifiedTargets = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "607FACCF1AFB9204008FA782"
BuildableName = "MoproKit_Example.app"
BlueprintName = "MoproKit_Example"
ReferencedContainer = "container:MoproKit.xcodeproj">
</BuildableReference>
</MacroExpansion>
<CodeCoverageTargets>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "607FACCF1AFB9204008FA782"
BuildableName = "MoproKit_Example.app"
BlueprintName = "MoproKit_Example"
ReferencedContainer = "container:MoproKit.xcodeproj">
</BuildableReference>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "37729DE650F35D13EA7BF03F8E697892"
BuildableName = "MoproKit.framework"
BlueprintName = "MoproKit"
ReferencedContainer = "container:Pods/Pods.xcodeproj">
</BuildableReference>
</CodeCoverageTargets>
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "607FACE41AFB9204008FA782"
BuildableName = "MoproKit_Tests.xctest"
BlueprintName = "MoproKit_Tests"
ReferencedContainer = "container:MoproKit.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "607FACCF1AFB9204008FA782"
BuildableName = "MoproKit_Example.app"
BlueprintName = "MoproKit_Example"
ReferencedContainer = "container:MoproKit.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "607FACCF1AFB9204008FA782"
BuildableName = "MoproKit_Example.app"
BlueprintName = "MoproKit_Example"
ReferencedContainer = "container:MoproKit.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -0,0 +1,192 @@
//
// AnonAadhaarViewController.swift
// MoproKit_Example
//
// Created by Yanis Meziane on 11/11/2023.
// Copyright © 2023 CocoaPods. All rights reserved.
//
import UIKit
import WebKit
import MoproKit
class AnonAadhaarViewController: UIViewController, WKScriptMessageHandler, WKNavigationDelegate {
let webView = WKWebView()
let moproCircom = MoproKit.MoproCircom()
//var setupResult: SetupResult?
var generatedProof: Data?
var publicInputs: Data?
let containerView = UIView()
var textView = UITextView()
override func viewDidLoad() {
super.viewDidLoad()
runInitAction()
setupUI()
let contentController = WKUserContentController()
contentController.add(self, name: "startProvingHandler")
contentController.add(self, name: "messageHandler")
let configuration = WKWebViewConfiguration()
configuration.userContentController = contentController
configuration.preferences.javaScriptEnabled = true
// Assign the configuration to the WKWebView
let webView = WKWebView(frame: view.bounds, configuration: configuration)
webView.navigationDelegate = self
view.addSubview(webView)
view.addSubview(textView)
guard let url = URL(string: "https://webview-anon-adhaar.vercel.app/") else { return }
webView.load(URLRequest(url: url))
}
func setupUI() {
textView.isEditable = false
textView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(textView)
// Make text view visible
textView.heightAnchor.constraint(equalToConstant: 200).isActive = true
NSLayoutConstraint.activate([
textView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
textView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
textView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -20)
])
}
@objc func runInitAction() {
// Update the textView on the main thread
DispatchQueue.main.async {
self.textView.text += "Initializing library\n"
}
// Execute long-running tasks in the background
DispatchQueue.global(qos: .userInitiated).async {
// Record start time
let start = CFAbsoluteTimeGetCurrent()
do {
try initializeMopro()
// Record end time and compute duration
let end = CFAbsoluteTimeGetCurrent()
let timeTaken = end - start
// Again, update the UI on the main thread
DispatchQueue.main.async {
self.textView.text += "Initializing arkzkey took \(timeTaken) seconds.\n"
}
} catch {
// Handle errors - update UI on main thread
DispatchQueue.main.async {
self.textView.text += "An error occurred during initialization: \(error)\n"
}
}
}
}
@objc func runProveAction(inputs: [String: [String]]) {
// Logic for prove (generate_proof2)
do {
textView.text += "Starts proving...\n"
// Record start time
let start = CFAbsoluteTimeGetCurrent()
// Generate Proof
let generateProofResult = try generateProof2(circuitInputs: inputs)
assert(!generateProofResult.proof.isEmpty, "Proof should not be empty")
//assert(Data(expectedOutput) == generateProofResult.inputs, "Circuit outputs mismatch the expected outputs")
// Record end time and compute duration
let end = CFAbsoluteTimeGetCurrent()
let timeTaken = end - start
// Store the generated proof and public inputs for later verification
generatedProof = generateProofResult.proof
publicInputs = generateProofResult.inputs
print("Proof generation took \(timeTaken) seconds.\n")
textView.text += "Proof generated!!! \n"
textView.text += "Proof generation took \(timeTaken) seconds.\n"
textView.text += "---\n"
runVerifyAction()
} catch let error as MoproError {
print("MoproError: \(error)")
} catch {
print("Unexpected error: \(error)")
}
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
webView.frame = view.bounds
}
struct Witness {
let signature: [String]
let modulus: [String]
let base_message: [String]
}
// Implement WKScriptMessageHandler method
func provingContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
if message.name == "messageHandler" {
// Handle messages for "messageHandler"
print("Received message from JavaScript:", message.body)
}
}
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
if message.name == "startProvingHandler", let data = message.body as? [String: Any] {
// Check for the "witness" key in the received data
if let witnessData = data["witness"] as? [String: [String]] {
if let signature = witnessData["signature"],
let modulus = witnessData["modulus"],
let baseMessage = witnessData["base_message"] {
let inputs: [String: [String]] = [
"signature": signature,
"modulus": modulus,
"base_message": baseMessage
]
// Call your Swift function with the received witness data
runProveAction(inputs: inputs)
}
} else if let error = data["error"] as? String {
// Handle error data
print("Received error value from JavaScript:", error)
} else {
print("No valid data keys found in the message data.")
}
}
}
@objc func runVerifyAction() {
// Logic for verify
guard let proof = generatedProof,
let publicInputs = publicInputs else {
print("Proof has not been generated yet.")
return
}
do {
// Verify Proof
let isValid = try verifyProof2(proof: proof, publicInput: publicInputs)
assert(isValid, "Proof verification should succeed")
textView.text += "Proof verification succeeded.\n"
} catch let error as MoproError {
print("MoproError: \(error)")
} catch {
print("Unexpected error: \(error)")
}
}
}

View File

@@ -0,0 +1,52 @@
//
// AppDelegate.swift
// MoproKit
//
// Created by 1552237 on 09/16/2023.
// Copyright (c) 2023 1552237. All rights reserved.
//
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
window = UIWindow(frame: UIScreen.main.bounds)
let viewController = ViewController()
let navigationController = UINavigationController(rootViewController: viewController)
window?.rootViewController = navigationController
window?.makeKeyAndVisible()
return true
}
func applicationWillResignActive(_ application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
func applicationDidEnterBackground(_ application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
func applicationWillEnterForeground(_ application: UIApplication) {
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(_ application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
}

View File

@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="iN0-l3-epB">
<rect key="frame" x="0.0" y="0.0" width="480" height="480"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text=" Copyright (c) 2015 CocoaPods. All rights reserved." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="8ie-xW-0ye">
<rect key="frame" x="20" y="439" width="441" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="MoproKit" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="kId-c2-rCX">
<rect key="frame" x="20" y="140" width="441" height="43"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="36"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="kId-c2-rCX" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="bottom" multiplier="1/3" constant="1" id="5cJ-9S-tgC"/>
<constraint firstAttribute="centerX" secondItem="kId-c2-rCX" secondAttribute="centerX" id="Koa-jz-hwk"/>
<constraint firstAttribute="bottom" secondItem="8ie-xW-0ye" secondAttribute="bottom" constant="20" id="Kzo-t9-V3l"/>
<constraint firstItem="8ie-xW-0ye" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="MfP-vx-nX0"/>
<constraint firstAttribute="centerX" secondItem="8ie-xW-0ye" secondAttribute="centerX" id="ZEH-qu-HZ9"/>
<constraint firstItem="kId-c2-rCX" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="fvb-Df-36g"/>
</constraints>
<nil key="simulatedStatusBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<point key="canvasLocation" x="548" y="455"/>
</view>
</objects>
</document>

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="vXZ-lx-hvc">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="ufC-wZ-h7g">
<objects>
<viewController id="vXZ-lx-hvc" customClass="ViewController" customModule="MoproKit_Example" customModuleProvider="target" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="jyV-Pf-zRb"/>
<viewControllerLayoutGuide type="bottom" id="2fi-mo-0CV"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="kh9-bI-dsS">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="x5A-6p-PRh" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>

View File

@@ -0,0 +1,53 @@
{
"images" : [
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "3x"
},
{
"idiom" : "ios-marketing",
"size" : "1024x1024",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
</array>
</dict>
</plist>

View File

@@ -0,0 +1,186 @@
//
// ViewController.swift
// MoproKit
//
// Created by 1552237 on 09/16/2023.
// Copyright (c) 2023 1552237. All rights reserved.
//
import UIKit
import MoproKit
class KeccakSetupViewController: UIViewController {
var setupButton = UIButton(type: .system)
var proveButton = UIButton(type: .system)
var verifyButton = UIButton(type: .system)
var textView = UITextView()
let moproCircom = MoproKit.MoproCircom()
var setupResult: SetupResult?
var generatedProof: Data?
var publicInputs: Data?
override func viewDidLoad() {
super.viewDidLoad()
// Set title
let title = UILabel()
title.text = "Keccak256 (setup)"
title.textColor = .white
title.textAlignment = .center
navigationItem.titleView = title
navigationController?.navigationBar.isHidden = false
navigationController?.navigationBar.prefersLargeTitles = true
// view.backgroundColor = .white
// navigationController?.navigationBar.prefersLargeTitles = true
// navigationController?.navigationBar.titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.black]
// navigationController?.navigationBar.barTintColor = UIColor.white // or any other contrasting color
// self.title = "Keccak256 (setup)"
setupUI()
}
func setupUI() {
setupButton.setTitle("Setup", for: .normal)
proveButton.setTitle("Prove", for: .normal)
verifyButton.setTitle("Verify", for: .normal)
textView.isEditable = false
//self.title = "Keccak256 (setup)"
//view.backgroundColor = .black
// Setup actions for buttons
setupButton.addTarget(self, action: #selector(runSetupAction), for: .touchUpInside)
proveButton.addTarget(self, action: #selector(runProveAction), for: .touchUpInside)
verifyButton.addTarget(self, action: #selector(runVerifyAction), for: .touchUpInside)
setupButton.contentEdgeInsets = UIEdgeInsets(top: 12, left: 16, bottom: 12, right: 16)
proveButton.contentEdgeInsets = UIEdgeInsets(top: 12, left: 16, bottom: 12, right: 16)
verifyButton.contentEdgeInsets = UIEdgeInsets(top: 12, left: 16, bottom: 12, right: 16)
let stackView = UIStackView(arrangedSubviews: [setupButton, proveButton, verifyButton, textView])
stackView.axis = .vertical
stackView.spacing = 10
stackView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stackView)
// Make text view visible
textView.heightAnchor.constraint(equalToConstant: 200).isActive = true
NSLayoutConstraint.activate([
stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20)
])
}
@objc func runSetupAction() {
// Logic for setup
if let wasmPath = Bundle.main.path(forResource: "keccak256_256_test", ofType: "wasm"),
let r1csPath = Bundle.main.path(forResource: "keccak256_256_test", ofType: "r1cs") {
// Multiplier example
// if let wasmPath = Bundle.main.path(forResource: "multiplier2", ofType: "wasm"),
// let r1csPath = Bundle.main.path(forResource: "multiplier2", ofType: "r1cs") {
do {
setupResult = try moproCircom.setup(wasmPath: wasmPath, r1csPath: r1csPath)
proveButton.isEnabled = true // Enable the Prove button upon successful setup
} catch let error as MoproError {
print("MoproError: \(error)")
} catch {
print("Unexpected error: \(error)")
}
} else {
print("Error getting paths for resources")
}
}
@objc func runProveAction() {
// Logic for prove
guard let setupResult = setupResult else {
print("Setup is not completed yet.")
return
}
do {
// Prepare inputs
let inputVec: [UInt8] = [
116, 101, 115, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
]
let bits = bytesToBits(bytes: inputVec)
var inputs = [String: [String]]()
inputs["in"] = bits
// Multiplier example
// var inputs = [String: [String]]()
// let a = 3
// let b = 5
// inputs["a"] = [String(a)]
// inputs["b"] = [String(b)]
// Record start time
let start = CFAbsoluteTimeGetCurrent()
// Generate Proof
let generateProofResult = try moproCircom.generateProof(circuitInputs: inputs)
assert(!generateProofResult.proof.isEmpty, "Proof should not be empty")
// Record end time and compute duration
let end = CFAbsoluteTimeGetCurrent()
let timeTaken = end - start
// Store the generated proof and public inputs for later verification
generatedProof = generateProofResult.proof
publicInputs = generateProofResult.inputs
textView.text += "Proof generation took \(timeTaken) seconds.\n"
verifyButton.isEnabled = true // Enable the Verify button once proof has been generated
} catch let error as MoproError {
print("MoproError: \(error)")
} catch {
print("Unexpected error: \(error)")
}
}
@objc func runVerifyAction() {
// Logic for verify
guard let setupResult = setupResult,
let proof = generatedProof,
let publicInputs = publicInputs else {
print("Setup is not completed or proof has not been generated yet.")
return
}
do {
// Verify Proof
let isValid = try moproCircom.verifyProof(proof: proof, publicInput: publicInputs)
assert(isValid, "Proof verification should succeed")
textView.text += "Proof verification succeeded.\n"
} catch let error as MoproError {
print("MoproError: \(error)")
} catch {
print("Unexpected error: \(error)")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
func bytesToBits(bytes: [UInt8]) -> [String] {
var bits = [String]()
for byte in bytes {
for j in 0..<8 {
let bit = (byte >> j) & 1
bits.append(String(bit))
}
}
return bits
}

View File

@@ -0,0 +1,147 @@
//
// ViewController.swift
// MoproKit
//
// Created by 1552237 on 09/16/2023.
// Copyright (c) 2023 1552237. All rights reserved.
//
import UIKit
import MoproKit
class KeccakZkeyViewController: UIViewController {
var initButton = UIButton(type: .system)
var proveButton = UIButton(type: .system)
var verifyButton = UIButton(type: .system)
var textView = UITextView()
let moproCircom = MoproKit.MoproCircom()
//var setupResult: SetupResult?
var generatedProof: Data?
var publicInputs: Data?
override func viewDidLoad() {
super.viewDidLoad()
// Set title
let title = UILabel()
title.text = "Keccak256 (Zkey)"
title.textColor = .white
title.textAlignment = .center
navigationItem.titleView = title
navigationController?.navigationBar.isHidden = false
navigationController?.navigationBar.prefersLargeTitles = true
setupUI()
}
func setupUI() {
initButton.setTitle("Init", for: .normal)
proveButton.setTitle("Prove", for: .normal)
verifyButton.setTitle("Verify", for: .normal)
// Uncomment once init separate
//proveButton.isEnabled = false
proveButton.isEnabled = true
verifyButton.isEnabled = false
textView.isEditable = false
// Setup actions for buttons
initButton.addTarget(self, action: #selector(runInitAction), for: .touchUpInside)
proveButton.addTarget(self, action: #selector(runProveAction), for: .touchUpInside)
verifyButton.addTarget(self, action: #selector(runVerifyAction), for: .touchUpInside)
initButton.contentEdgeInsets = UIEdgeInsets(top: 12, left: 16, bottom: 12, right: 16)
proveButton.contentEdgeInsets = UIEdgeInsets(top: 12, left: 16, bottom: 12, right: 16)
verifyButton.contentEdgeInsets = UIEdgeInsets(top: 12, left: 16, bottom: 12, right: 16)
let stackView = UIStackView(arrangedSubviews: [initButton, proveButton, verifyButton, textView])
stackView.axis = .vertical
stackView.spacing = 10
stackView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stackView)
// Make text view visible
textView.heightAnchor.constraint(equalToConstant: 200).isActive = true
NSLayoutConstraint.activate([
stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20)
])
}
@objc func runInitAction() {
// Logic for init
do {
textView.text += "Initializing library\n"
// Record start time
let start = CFAbsoluteTimeGetCurrent()
try initializeMopro()
// Record end time and compute duration
let end = CFAbsoluteTimeGetCurrent()
let timeTaken = end - start
textView.text += "Initializing arkzkey took \(timeTaken) seconds.\n"
} catch let error as MoproError {
print("MoproError: \(error)")
} catch {
print("Unexpected error: \(error)")
}
}
@objc func runProveAction() {
// Logic for prove (generate_proof2)
do {
// Prepare inputs
let inputVec: [UInt8] = [
116, 101, 115, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
]
let bits = bytesToBits(bytes: inputVec)
var inputs = [String: [String]]()
inputs["in"] = bits
// Expected outputs
let outputVec: [UInt8] = [
37, 17, 98, 135, 161, 178, 88, 97, 125, 150, 143, 65, 228, 211, 170, 133, 153, 9, 88,
212, 4, 212, 175, 238, 249, 210, 214, 116, 170, 85, 45, 21,
]
let outputBits: [String] = bytesToBits(bytes: outputVec)
// let expectedOutput: [UInt8] = serializeOutputs(outputBits)
// Record start time
let start = CFAbsoluteTimeGetCurrent()
// Generate Proof
let generateProofResult = try generateProof2(circuitInputs: inputs)
assert(!generateProofResult.proof.isEmpty, "Proof should not be empty")
//assert(Data(expectedOutput) == generateProofResult.inputs, "Circuit outputs mismatch the expected outputs")
// Record end time and compute duration
let end = CFAbsoluteTimeGetCurrent()
let timeTaken = end - start
// Store the generated proof and public inputs for later verification
generatedProof = generateProofResult.proof
publicInputs = generateProofResult.inputs
textView.text += "Proof generation took \(timeTaken) seconds.\n"
// TODO: Enable verify
verifyButton.isEnabled = false
//verifyButton.isEnabled = true // Enable the Verify button once proof has been generated
} catch let error as MoproError {
print("MoproError: \(error)")
} catch {
print("Unexpected error: \(error)")
}
}
@objc func runVerifyAction() {
// Logic for verify
}
}

View File

@@ -0,0 +1,235 @@
//
// ViewController.swift
// MoproKit
//
// Created by 1552237 on 09/16/2023.
// Copyright (c) 2023 1552237. All rights reserved.
//
import UIKit
import MoproKit
class RSAViewController: UIViewController {
var initButton = UIButton(type: .system)
var proveButton = UIButton(type: .system)
var verifyButton = UIButton(type: .system)
var textView = UITextView()
let moproCircom = MoproKit.MoproCircom()
//var setupResult: SetupResult?
var generatedProof: Data?
var publicInputs: Data?
override func viewDidLoad() {
super.viewDidLoad()
// Set title
let title = UILabel()
title.text = "RSA (Zkey)"
title.textColor = .white
title.textAlignment = .center
navigationItem.titleView = title
navigationController?.navigationBar.isHidden = false
navigationController?.navigationBar.prefersLargeTitles = true
setupUI()
}
func setupUI() {
initButton.setTitle("Init", for: .normal)
proveButton.setTitle("Prove", for: .normal)
verifyButton.setTitle("Verify", for: .normal)
// Uncomment once init separate
//proveButton.isEnabled = false
proveButton.isEnabled = true
verifyButton.isEnabled = false
textView.isEditable = false
// Setup actions for buttons
initButton.addTarget(self, action: #selector(runInitAction), for: .touchUpInside)
proveButton.addTarget(self, action: #selector(runProveAction), for: .touchUpInside)
verifyButton.addTarget(self, action: #selector(runVerifyAction), for: .touchUpInside)
initButton.contentEdgeInsets = UIEdgeInsets(top: 12, left: 16, bottom: 12, right: 16)
proveButton.contentEdgeInsets = UIEdgeInsets(top: 12, left: 16, bottom: 12, right: 16)
verifyButton.contentEdgeInsets = UIEdgeInsets(top: 12, left: 16, bottom: 12, right: 16)
let stackView = UIStackView(arrangedSubviews: [initButton, proveButton, verifyButton, textView])
stackView.axis = .vertical
stackView.spacing = 10
stackView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stackView)
// Make text view visible
textView.heightAnchor.constraint(equalToConstant: 200).isActive = true
NSLayoutConstraint.activate([
stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20)
])
}
@objc func runInitAction() {
// Update the textView on the main thread
DispatchQueue.main.async {
self.textView.text += "Initializing library\n"
}
// Execute long-running tasks in the background
DispatchQueue.global(qos: .userInitiated).async {
// Record start time
let start = CFAbsoluteTimeGetCurrent()
do {
try initializeMopro()
// Record end time and compute duration
let end = CFAbsoluteTimeGetCurrent()
let timeTaken = end - start
// Again, update the UI on the main thread
DispatchQueue.main.async {
self.textView.text += "Initializing arkzkey took \(timeTaken) seconds.\n"
}
} catch {
// Handle errors - update UI on main thread
DispatchQueue.main.async {
self.textView.text += "An error occurred during initialization: \(error)\n"
}
}
}
}
@objc func runProveAction() {
// Logic for prove (generate_proof2)
do {
// Prepare inputs
let signature: [String] = [
"3582320600048169363",
"7163546589759624213",
"18262551396327275695",
"4479772254206047016",
"1970274621151677644",
"6547632513799968987",
"921117808165172908",
"7155116889028933260",
"16769940396381196125",
"17141182191056257954",
"4376997046052607007",
"17471823348423771450",
"16282311012391954891",
"70286524413490741",
"1588836847166444745",
"15693430141227594668",
"13832254169115286697",
"15936550641925323613",
"323842208142565220",
"6558662646882345749",
"15268061661646212265",
"14962976685717212593",
"15773505053543368901",
"9586594741348111792",
"1455720481014374292",
"13945813312010515080",
"6352059456732816887",
"17556873002865047035",
"2412591065060484384",
"11512123092407778330",
"8499281165724578877",
"12768005853882726493",
]
let modulus: [String] = [
"13792647154200341559",
"12773492180790982043",
"13046321649363433702",
"10174370803876824128",
"7282572246071034406",
"1524365412687682781",
"4900829043004737418",
"6195884386932410966",
"13554217876979843574",
"17902692039595931737",
"12433028734895890975",
"15971442058448435996",
"4591894758077129763",
"11258250015882429548",
"16399550288873254981",
"8246389845141771315",
"14040203746442788850",
"7283856864330834987",
"12297563098718697441",
"13560928146585163504",
"7380926829734048483",
"14591299561622291080",
"8439722381984777599",
"17375431987296514829",
"16727607878674407272",
"3233954801381564296",
"17255435698225160983",
"15093748890170255670",
"15810389980847260072",
"11120056430439037392",
"5866130971823719482",
"13327552690270163501",
]
let base_message: [String] = ["18114495772705111902", "2254271930739856077",
"2068851770", "0","0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0",
"0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0","0", "0", "0","0",
]
var inputs = [String: [String]]()
inputs["signature"] = signature;
inputs["modulus"] = modulus;
inputs["base_message"] = base_message;
let start = CFAbsoluteTimeGetCurrent()
// Generate Proof
let generateProofResult = try generateProof2(circuitInputs: inputs)
assert(!generateProofResult.proof.isEmpty, "Proof should not be empty")
// Record end time and compute duration
let end = CFAbsoluteTimeGetCurrent()
let timeTaken = end - start
// Store the generated proof and public inputs for later verification
generatedProof = generateProofResult.proof
publicInputs = generateProofResult.inputs
textView.text += "Proof generation took \(timeTaken) seconds.\n"
verifyButton.isEnabled = true
} catch let error as MoproError {
print("MoproError: \(error)")
textView.text += "MoproError: \(error)\n"
} catch {
print("Unexpected error: \(error)")
textView.text += "Unexpected error: \(error)\n"
}
}
@objc func runVerifyAction() {
// Logic for verify
guard let proof = generatedProof,
let publicInputs = publicInputs else {
print("Proof has not been generated yet.")
return
}
do {
// Verify Proof
let isValid = try verifyProof2(proof: proof, publicInput: publicInputs)
assert(isValid, "Proof verification should succeed")
textView.text += "Proof verification succeeded.\n"
} catch let error as MoproError {
print("MoproError: \(error)")
} catch {
print("Unexpected error: \(error)")
}
}
}

View File

@@ -0,0 +1,100 @@
//
// ViewController.swift
// MoproKit
//
// Created by 1552237 on 09/16/2023.
// Copyright (c) 2023 1552237. All rights reserved.
//
import UIKit
import MoproKit
// Main ViewController
class ViewController: UIViewController {
let keccakSetupButton = UIButton(type: .system)
let keccakZkeyButton = UIButton(type: .system)
let rsaButton = UIButton(type: .system)
let aadhaarButton = UIButton(type: .system)
override func viewDidLoad() {
super.viewDidLoad()
// TODO: Improve style
// Set title
let title = UILabel()
title.text = "Mopro Examples"
title.textColor = .white
title.textAlignment = .center
navigationItem.titleView = title
navigationController?.navigationBar.isHidden = false
navigationController?.navigationBar.prefersLargeTitles = true
setupMainUI()
}
func setupMainUI() {
keccakSetupButton.setTitle("Keccak (Setup)", for: .normal)
keccakSetupButton.addTarget(self, action: #selector(openKeccakSetup), for: .touchUpInside)
keccakZkeyButton.setTitle("Keccak (Zkey)", for: .normal)
keccakZkeyButton.addTarget(self, action: #selector(openKeccakZkey), for: .touchUpInside)
rsaButton.setTitle("RSA", for: .normal)
rsaButton.addTarget(self, action: #selector(openRSA), for: .touchUpInside)
aadhaarButton.setTitle("Anon Aadhaar", for: .normal)
aadhaarButton.addTarget(self, action: #selector(openAnonAadhaar), for: .touchUpInside)
keccakSetupButton.contentEdgeInsets = UIEdgeInsets(top: 12, left: 16, bottom: 12, right: 16)
keccakZkeyButton.contentEdgeInsets = UIEdgeInsets(top: 12, left: 16, bottom: 12, right: 16)
rsaButton.contentEdgeInsets = UIEdgeInsets(top: 12, left: 16, bottom: 12, right: 16)
// self.title = "Mopro Examples"
// navigationController?.navigationBar.prefersLargeTitles = true
let stackView = UIStackView(arrangedSubviews: [keccakSetupButton, keccakZkeyButton, rsaButton, aadhaarButton])
stackView.axis = .vertical
stackView.spacing = 20
stackView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stackView)
NSLayoutConstraint.activate([
stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor)
])
}
@objc func openKeccakSetup() {
let keccakSetupVC = KeccakSetupViewController()
navigationController?.pushViewController(keccakSetupVC, animated: true)
}
@objc func openKeccakZkey() {
let keccakZkeyVC = KeccakZkeyViewController()
navigationController?.pushViewController(keccakZkeyVC, animated: true)
}
@objc func openRSA() {
let rsaVC = RSAViewController()
navigationController?.pushViewController(rsaVC, animated: true)
}
@objc func openAnonAadhaar() {
let anonAadhaarVC = AnonAadhaarViewController()
navigationController?.pushViewController(anonAadhaarVC, animated: true)
}
}
// // Make buttons bigger
// proveButton.contentEdgeInsets = UIEdgeInsets(top: 12, left: 16, bottom: 12, right: 16)
// verifyButton.contentEdgeInsets = UIEdgeInsets(top: 12, left: 16, bottom: 12, right: 16)
// NSLayoutConstraint.activate([
// stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
// stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
// stackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20)
// ])

View File

@@ -0,0 +1,24 @@
use_frameworks!
platform :ios, '13.0'
target 'MoproKit_Example' do
pod 'MoproKit', :path => '../'
target 'MoproKit_Tests' do
inherit! :search_paths
pod 'Quick', '~> 2.2.0'
pod 'Nimble', '~> 10.0.0'
end
end
post_install do |installer|
installer.generated_projects.each do |project|
project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '13.0'
end
end
end
end

View File

@@ -0,0 +1,109 @@
@testable import MoproKit
import XCTest
final class CircomTests: XCTestCase {
let moproCircom = MoproKit.MoproCircom()
func testMultiplier() {
let wasmPath = Bundle.main.path(forResource: "multiplier2", ofType: "wasm")!
let r1csPath = Bundle.main.path(forResource: "multiplier2", ofType: "r1cs")!
XCTAssertNoThrow(try moproCircom.setup(wasmPath: wasmPath, r1csPath: r1csPath), "Mopro circom setup failed")
do {
var inputs = [String: [String]]()
let a = 3
let b = 5
let c = a*b
inputs["a"] = [String(a)]
inputs["b"] = [String(b)]
let outputs: [String] = [String(c), String(a)]
let expectedOutput: [UInt8] = serializeOutputs(outputs)
// Generate Proof
let generateProofResult = try moproCircom.generateProof(circuitInputs: inputs)
XCTAssertFalse(generateProofResult.proof.isEmpty, "Proof should not be empty")
XCTAssertEqual(Data(expectedOutput), generateProofResult.inputs, "Circuit outputs mismatch the expected outputs")
let isValid = try moproCircom.verifyProof(proof: generateProofResult.proof, publicInput: generateProofResult.inputs)
XCTAssertTrue(isValid, "Proof verification should succeed")
} catch let error as MoproError {
print("MoproError: \(error)")
} catch {
print("Unexpected error: \(error)")
}
}
func testKeccak256() {
let wasmPath = Bundle.main.path(forResource: "keccak256_256_test", ofType: "wasm")!
let r1csPath = Bundle.main.path(forResource: "keccak256_256_test", ofType: "r1cs")!
XCTAssertNoThrow(try moproCircom.setup(wasmPath: wasmPath, r1csPath: r1csPath), "Mopro circom setup failed")
do {
// Prepare inputs
let inputVec: [UInt8] = [
116, 101, 115, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
]
let bits = bytesToBits(bytes: inputVec)
var inputs = [String: [String]]()
inputs["in"] = bits
// Expected outputs
let outputVec: [UInt8] = [
37, 17, 98, 135, 161, 178, 88, 97, 125, 150, 143, 65, 228, 211, 170, 133, 153, 9, 88,
212, 4, 212, 175, 238, 249, 210, 214, 116, 170, 85, 45, 21,
]
let outputBits: [String] = bytesToBits(bytes: outputVec)
let expectedOutput: [UInt8] = serializeOutputs(outputBits)
// Generate Proof
let generateProofResult = try moproCircom.generateProof(circuitInputs: inputs)
XCTAssertFalse(generateProofResult.proof.isEmpty, "Proof should not be empty")
XCTAssertEqual(Data(expectedOutput), generateProofResult.inputs, "Circuit outputs mismatch the expected outputs")
let isValid = try moproCircom.verifyProof(proof: generateProofResult.proof, publicInput: generateProofResult.inputs)
XCTAssertTrue(isValid, "Proof verification should succeed")
} catch let error as MoproError {
print("MoproError: \(error)")
} catch {
print("Unexpected error: \(error)")
}
}
}
func bytesToBits(bytes: [UInt8]) -> [String] {
var bits = [String]()
for byte in bytes {
for j in 0..<8 {
let bit = (byte >> j) & 1
bits.append(String(bit))
}
}
return bits
}
func serializeOutputs(_ stringArray: [String]) -> [UInt8] {
var bytesArray: [UInt8] = []
let length = stringArray.count
var littleEndianLength = length.littleEndian
let targetLength = 32
withUnsafeBytes(of: &littleEndianLength) {
bytesArray.append(contentsOf: $0)
}
for value in stringArray {
// TODO: should handle 254-bit input
var littleEndian = Int32(value)!.littleEndian
var byteLength = 0
withUnsafeBytes(of: &littleEndian) {
bytesArray.append(contentsOf: $0)
byteLength = byteLength + $0.count
}
if byteLength < targetLength {
let paddingCount = targetLength - byteLength
let paddingArray = [UInt8](repeating: 0, count: paddingCount)
bytesArray.append(contentsOf: paddingArray)
}
}
return bytesArray
}

View File

@@ -0,0 +1,13 @@
import XCTest
@testable import MoproKit_Example
final class CircomUITests: XCTestCase {
let ui = KeccakSetupViewController()
func testSuccessUI() {
XCTAssertNoThrow(ui.setupUI(), "Setup UI failed")
XCTAssertNoThrow(ui.runProveAction(), "Prove action failed")
XCTAssertNoThrow(ui.runVerifyAction(), "Verify action failed")
}
}

View File

@@ -0,0 +1,238 @@
// This file was autogenerated by some hot garbage in the `uniffi` crate.
// Trust me, you don't want to mess with it!
#pragma once
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
// The following structs are used to implement the lowest level
// of the FFI, and thus useful to multiple uniffied crates.
// We ensure they are declared exactly once, with a header guard, UNIFFI_SHARED_H.
#ifdef UNIFFI_SHARED_H
// We also try to prevent mixing versions of shared uniffi header structs.
// If you add anything to the #else block, you must increment the version suffix in UNIFFI_SHARED_HEADER_V4
#ifndef UNIFFI_SHARED_HEADER_V4
#error Combining helper code from multiple versions of uniffi is not supported
#endif // ndef UNIFFI_SHARED_HEADER_V4
#else
#define UNIFFI_SHARED_H
#define UNIFFI_SHARED_HEADER_V4
// ⚠️ Attention: If you change this #else block (ending in `#endif // def UNIFFI_SHARED_H`) you *must* ⚠️
// ⚠️ increment the version suffix in all instances of UNIFFI_SHARED_HEADER_V4 in this file. ⚠️
typedef struct RustBuffer
{
int32_t capacity;
int32_t len;
uint8_t *_Nullable data;
} RustBuffer;
typedef int32_t (*ForeignCallback)(uint64_t, int32_t, const uint8_t *_Nonnull, int32_t, RustBuffer *_Nonnull);
// Task defined in Rust that Swift executes
typedef void (*UniFfiRustTaskCallback)(const void * _Nullable, int8_t);
// Callback to execute Rust tasks using a Swift Task
//
// Args:
// executor: ForeignExecutor lowered into a size_t value
// delay: Delay in MS
// task: UniFfiRustTaskCallback to call
// task_data: data to pass the task callback
typedef int8_t (*UniFfiForeignExecutorCallback)(size_t, uint32_t, UniFfiRustTaskCallback _Nullable, const void * _Nullable);
typedef struct ForeignBytes
{
int32_t len;
const uint8_t *_Nullable data;
} ForeignBytes;
// Error definitions
typedef struct RustCallStatus {
int8_t code;
RustBuffer errorBuf;
} RustCallStatus;
// ⚠️ Attention: If you change this #else block (ending in `#endif // def UNIFFI_SHARED_H`) you *must* ⚠️
// ⚠️ increment the version suffix in all instances of UNIFFI_SHARED_HEADER_V4 in this file. ⚠️
#endif // def UNIFFI_SHARED_H
// Continuation callback for UniFFI Futures
typedef void (*UniFfiRustFutureContinuation)(void * _Nonnull, int8_t);
// Scaffolding functions
void uniffi_mopro_ffi_fn_free_moprocircom(void*_Nonnull ptr, RustCallStatus *_Nonnull out_status
);
void*_Nonnull uniffi_mopro_ffi_fn_constructor_moprocircom_new(RustCallStatus *_Nonnull out_status
);
RustBuffer uniffi_mopro_ffi_fn_method_moprocircom_generate_proof(void*_Nonnull ptr, RustBuffer circuit_inputs, RustCallStatus *_Nonnull out_status
);
RustBuffer uniffi_mopro_ffi_fn_method_moprocircom_setup(void*_Nonnull ptr, RustBuffer wasm_path, RustBuffer r1cs_path, RustCallStatus *_Nonnull out_status
);
int8_t uniffi_mopro_ffi_fn_method_moprocircom_verify_proof(void*_Nonnull ptr, RustBuffer proof, RustBuffer public_input, RustCallStatus *_Nonnull out_status
);
uint32_t uniffi_mopro_ffi_fn_func_add(uint32_t a, uint32_t b, RustCallStatus *_Nonnull out_status
);
RustBuffer uniffi_mopro_ffi_fn_func_generate_proof2(RustBuffer circuit_inputs, RustCallStatus *_Nonnull out_status
);
RustBuffer uniffi_mopro_ffi_fn_func_hello(RustCallStatus *_Nonnull out_status
);
void uniffi_mopro_ffi_fn_func_initialize_mopro(RustCallStatus *_Nonnull out_status
);
void uniffi_mopro_ffi_fn_func_initialize_mopro_dylib(RustBuffer dylib_path, RustCallStatus *_Nonnull out_status
);
int8_t uniffi_mopro_ffi_fn_func_verify_proof2(RustBuffer proof, RustBuffer public_input, RustCallStatus *_Nonnull out_status
);
RustBuffer ffi_mopro_ffi_rustbuffer_alloc(int32_t size, RustCallStatus *_Nonnull out_status
);
RustBuffer ffi_mopro_ffi_rustbuffer_from_bytes(ForeignBytes bytes, RustCallStatus *_Nonnull out_status
);
void ffi_mopro_ffi_rustbuffer_free(RustBuffer buf, RustCallStatus *_Nonnull out_status
);
RustBuffer ffi_mopro_ffi_rustbuffer_reserve(RustBuffer buf, int32_t additional, RustCallStatus *_Nonnull out_status
);
void ffi_mopro_ffi_rust_future_continuation_callback_set(UniFfiRustFutureContinuation _Nonnull callback
);
void ffi_mopro_ffi_rust_future_poll_u8(void* _Nonnull handle, void* _Nonnull uniffi_callback
);
void ffi_mopro_ffi_rust_future_cancel_u8(void* _Nonnull handle
);
void ffi_mopro_ffi_rust_future_free_u8(void* _Nonnull handle
);
uint8_t ffi_mopro_ffi_rust_future_complete_u8(void* _Nonnull handle, RustCallStatus *_Nonnull out_status
);
void ffi_mopro_ffi_rust_future_poll_i8(void* _Nonnull handle, void* _Nonnull uniffi_callback
);
void ffi_mopro_ffi_rust_future_cancel_i8(void* _Nonnull handle
);
void ffi_mopro_ffi_rust_future_free_i8(void* _Nonnull handle
);
int8_t ffi_mopro_ffi_rust_future_complete_i8(void* _Nonnull handle, RustCallStatus *_Nonnull out_status
);
void ffi_mopro_ffi_rust_future_poll_u16(void* _Nonnull handle, void* _Nonnull uniffi_callback
);
void ffi_mopro_ffi_rust_future_cancel_u16(void* _Nonnull handle
);
void ffi_mopro_ffi_rust_future_free_u16(void* _Nonnull handle
);
uint16_t ffi_mopro_ffi_rust_future_complete_u16(void* _Nonnull handle, RustCallStatus *_Nonnull out_status
);
void ffi_mopro_ffi_rust_future_poll_i16(void* _Nonnull handle, void* _Nonnull uniffi_callback
);
void ffi_mopro_ffi_rust_future_cancel_i16(void* _Nonnull handle
);
void ffi_mopro_ffi_rust_future_free_i16(void* _Nonnull handle
);
int16_t ffi_mopro_ffi_rust_future_complete_i16(void* _Nonnull handle, RustCallStatus *_Nonnull out_status
);
void ffi_mopro_ffi_rust_future_poll_u32(void* _Nonnull handle, void* _Nonnull uniffi_callback
);
void ffi_mopro_ffi_rust_future_cancel_u32(void* _Nonnull handle
);
void ffi_mopro_ffi_rust_future_free_u32(void* _Nonnull handle
);
uint32_t ffi_mopro_ffi_rust_future_complete_u32(void* _Nonnull handle, RustCallStatus *_Nonnull out_status
);
void ffi_mopro_ffi_rust_future_poll_i32(void* _Nonnull handle, void* _Nonnull uniffi_callback
);
void ffi_mopro_ffi_rust_future_cancel_i32(void* _Nonnull handle
);
void ffi_mopro_ffi_rust_future_free_i32(void* _Nonnull handle
);
int32_t ffi_mopro_ffi_rust_future_complete_i32(void* _Nonnull handle, RustCallStatus *_Nonnull out_status
);
void ffi_mopro_ffi_rust_future_poll_u64(void* _Nonnull handle, void* _Nonnull uniffi_callback
);
void ffi_mopro_ffi_rust_future_cancel_u64(void* _Nonnull handle
);
void ffi_mopro_ffi_rust_future_free_u64(void* _Nonnull handle
);
uint64_t ffi_mopro_ffi_rust_future_complete_u64(void* _Nonnull handle, RustCallStatus *_Nonnull out_status
);
void ffi_mopro_ffi_rust_future_poll_i64(void* _Nonnull handle, void* _Nonnull uniffi_callback
);
void ffi_mopro_ffi_rust_future_cancel_i64(void* _Nonnull handle
);
void ffi_mopro_ffi_rust_future_free_i64(void* _Nonnull handle
);
int64_t ffi_mopro_ffi_rust_future_complete_i64(void* _Nonnull handle, RustCallStatus *_Nonnull out_status
);
void ffi_mopro_ffi_rust_future_poll_f32(void* _Nonnull handle, void* _Nonnull uniffi_callback
);
void ffi_mopro_ffi_rust_future_cancel_f32(void* _Nonnull handle
);
void ffi_mopro_ffi_rust_future_free_f32(void* _Nonnull handle
);
float ffi_mopro_ffi_rust_future_complete_f32(void* _Nonnull handle, RustCallStatus *_Nonnull out_status
);
void ffi_mopro_ffi_rust_future_poll_f64(void* _Nonnull handle, void* _Nonnull uniffi_callback
);
void ffi_mopro_ffi_rust_future_cancel_f64(void* _Nonnull handle
);
void ffi_mopro_ffi_rust_future_free_f64(void* _Nonnull handle
);
double ffi_mopro_ffi_rust_future_complete_f64(void* _Nonnull handle, RustCallStatus *_Nonnull out_status
);
void ffi_mopro_ffi_rust_future_poll_pointer(void* _Nonnull handle, void* _Nonnull uniffi_callback
);
void ffi_mopro_ffi_rust_future_cancel_pointer(void* _Nonnull handle
);
void ffi_mopro_ffi_rust_future_free_pointer(void* _Nonnull handle
);
void*_Nonnull ffi_mopro_ffi_rust_future_complete_pointer(void* _Nonnull handle, RustCallStatus *_Nonnull out_status
);
void ffi_mopro_ffi_rust_future_poll_rust_buffer(void* _Nonnull handle, void* _Nonnull uniffi_callback
);
void ffi_mopro_ffi_rust_future_cancel_rust_buffer(void* _Nonnull handle
);
void ffi_mopro_ffi_rust_future_free_rust_buffer(void* _Nonnull handle
);
RustBuffer ffi_mopro_ffi_rust_future_complete_rust_buffer(void* _Nonnull handle, RustCallStatus *_Nonnull out_status
);
void ffi_mopro_ffi_rust_future_poll_void(void* _Nonnull handle, void* _Nonnull uniffi_callback
);
void ffi_mopro_ffi_rust_future_cancel_void(void* _Nonnull handle
);
void ffi_mopro_ffi_rust_future_free_void(void* _Nonnull handle
);
void ffi_mopro_ffi_rust_future_complete_void(void* _Nonnull handle, RustCallStatus *_Nonnull out_status
);
uint16_t uniffi_mopro_ffi_checksum_func_add(void
);
uint16_t uniffi_mopro_ffi_checksum_func_generate_proof2(void
);
uint16_t uniffi_mopro_ffi_checksum_func_hello(void
);
uint16_t uniffi_mopro_ffi_checksum_func_initialize_mopro(void
);
uint16_t uniffi_mopro_ffi_checksum_func_initialize_mopro_dylib(void
);
uint16_t uniffi_mopro_ffi_checksum_func_verify_proof2(void
);
uint16_t uniffi_mopro_ffi_checksum_method_moprocircom_generate_proof(void
);
uint16_t uniffi_mopro_ffi_checksum_method_moprocircom_setup(void
);
uint16_t uniffi_mopro_ffi_checksum_method_moprocircom_verify_proof(void
);
uint16_t uniffi_mopro_ffi_checksum_constructor_moprocircom_new(void
);
uint32_t ffi_mopro_ffi_uniffi_contract_version(void
);

View File

@@ -1,6 +1,4 @@
MIT License
Copyright (c) 2023 zk-email
Copyright (c) 2023 1552237 <oskarth@titanproxy.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -9,13 +7,13 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

View File

@@ -0,0 +1,50 @@
#
# Be sure to run `pod lib lint MoproKit.podspec' to ensure this is a
# valid spec before submitting.
#
# Any lines starting with a # are optional, but their use is encouraged
# To learn more about a Podspec see https://guides.cocoapods.org/syntax/podspec.html
#
Pod::Spec.new do |s|
s.name = 'MoproKit'
s.version = '0.1.0'
s.summary = 'A short description of MoproKit.'
# This description is used to generate tags and improve search results.
# * Think: What does it do? Why did you write it? What is the focus?
# * Try to keep it short, snappy and to the point.
# * Write the description between the DESC delimiters below.
# * Finally, don't worry about the indent, CocoaPods strips it!
s.description = <<-DESC
TODO: Add long description of the pod here.
DESC
s.homepage = 'https://github.com/1552237/MoproKit'
# s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2'
s.license = { :type => 'MIT', :file => 'LICENSE' }
s.author = { '1552237' => 'oskarth@titanproxy.com' }
s.source = { :git => 'https://github.com/1552237/MoproKit.git', :tag => s.version.to_s }
# s.social_media_url = 'https://twitter.com/<TWITTER_USERNAME>'
s.ios.deployment_target = '13.0'
s.source_files = 'MoproKit/Classes/**/*'
# libmopro library, headers and modulemap
# XXX: static library is not in source control, and needs to be inlcuded manually
# Have to be mindful of architecture and simulator or not here, should be improved
s.preserve_paths = 'Libs/libmopro_uniffi.a'
s.vendored_libraries = 'Libs/libmopro_uniffi.a'
s.source_files = 'Include/*.h', 'Bindings/*.swift'
s.resource = 'Resources/moproFFI.modulemap'
# s.resource_bundles = {
# 'MoproKit' => ['MoproKit/Assets/*.png']
# }
# s.public_header_files = 'Pod/Classes/**/*.h'
# s.frameworks = 'UIKit', 'MapKit'
# s.dependency 'AFNetworking', '~> 2.3'
end

View File

@@ -0,0 +1,29 @@
# MoproKit
[![CI Status](https://img.shields.io/travis/1552237/MoproKit.svg?style=flat)](https://travis-ci.org/1552237/MoproKit)
[![Version](https://img.shields.io/cocoapods/v/MoproKit.svg?style=flat)](https://cocoapods.org/pods/MoproKit)
[![License](https://img.shields.io/cocoapods/l/MoproKit.svg?style=flat)](https://cocoapods.org/pods/MoproKit)
[![Platform](https://img.shields.io/cocoapods/p/MoproKit.svg?style=flat)](https://cocoapods.org/pods/MoproKit)
## Example
To run the example project, clone the repo, and run `pod install` from the Example directory first.
## Requirements
## Installation
MoproKit is available through [CocoaPods](https://cocoapods.org). To install
it, simply add the following line to your Podfile:
```ruby
pod 'MoproKit'
```
## Author
1552237, oskarth@titanproxy.com
## License
MoproKit is available under the MIT license. See the LICENSE file for more info.

View File

@@ -0,0 +1,6 @@
// This file was autogenerated by some hot garbage in the `uniffi` crate.
// Trust me, you don't want to mess with it!
module moproFFI {
header "moproFFI.h"
export *
}

View File

@@ -1,193 +0,0 @@
//
// BACHandler.swift
// NFCTest
//
// Created by Andy Qua on 07/06/2019.
// Copyright © 2019 Andy Qua. All rights reserved.
//
import Foundation
#if !os(macOS)
import CoreNFC
@available(iOS 15, *)
public class BACHandler {
let KENC : [UInt8] = [0,0,0,1]
let KMAC : [UInt8] = [0,0,0,2]
public var ksenc : [UInt8] = []
public var ksmac : [UInt8] = []
var rnd_icc : [UInt8] = []
var rnd_ifd : [UInt8] = []
public var kifd : [UInt8] = []
var tagReader : TagReader?
public init() {
// For testing only
}
public init(tagReader: TagReader) {
self.tagReader = tagReader
}
public func performBACAndGetSessionKeys( mrzKey : String ) async throws {
guard let tagReader = self.tagReader else {
throw NFCPassportReaderError.NoConnectedTag
}
Log.debug( "BACHandler - deriving Document Basic Access Keys" )
_ = try self.deriveDocumentBasicAccessKeys(mrz: mrzKey)
// Make sure we clear secure messaging (could happen if we read an invalid DG or we hit a secure error
tagReader.secureMessaging = nil
// get Challenge
Log.debug( "BACHandler - Getting initial challenge" )
let response = try await tagReader.getChallenge()
Log.verbose( "DATA - \(response.data)" )
Log.debug( "BACHandler - Doing mutual authentication" )
let cmd_data = self.authentication(rnd_icc: [UInt8](response.data))
let maResponse = try await tagReader.doMutualAuthentication(cmdData: Data(cmd_data))
Log.debug( "DATA - \(maResponse.data)" )
guard maResponse.data.count > 0 else {
throw NFCPassportReaderError.InvalidMRZKey
}
let (KSenc, KSmac, ssc) = try self.sessionKeys(data: [UInt8](maResponse.data))
tagReader.secureMessaging = SecureMessaging(ksenc: KSenc, ksmac: KSmac, ssc: ssc)
Log.debug( "BACHandler - complete" )
}
func deriveDocumentBasicAccessKeys(mrz: String) throws -> ([UInt8], [UInt8]) {
let kseed = generateInitialKseed(kmrz:mrz)
Log.verbose("Calculate the Basic Access Keys (Kenc and Kmac) using TR-SAC 1.01, 4.2")
let smskg = SecureMessagingSessionKeyGenerator()
self.ksenc = try smskg.deriveKey(keySeed: kseed, mode: .ENC_MODE)
self.ksmac = try smskg.deriveKey(keySeed: kseed, mode: .MAC_MODE)
return (ksenc, ksmac)
}
///
/// Calculate the kseed from the kmrz:
/// - Calculate a SHA-1 hash of the kmrz
/// - Take the most significant 16 bytes to form the Kseed.
/// @param kmrz: The MRZ information
/// @type kmrz: a string
/// @return: a 16 bytes string
///
/// - Parameter kmrz: mrz key
/// - Returns: first 16 bytes of the mrz SHA1 hash
///
func generateInitialKseed(kmrz : String ) -> [UInt8] {
Log.verbose("Calculate the SHA-1 hash of MRZ_information")
Log.verbose("\tMRZ KEY - \(kmrz)")
let hash = calcSHA1Hash( [UInt8](kmrz.data(using:.utf8)!) )
Log.verbose("\tsha1(MRZ_information): \(binToHexRep(hash))")
let subHash = Array(hash[0..<16])
Log.verbose("Take the most significant 16 bytes to form the Kseed")
Log.verbose("\tKseed: \(binToHexRep(subHash))" )
return Array(subHash)
}
/// Construct the command data for the mutual authentication.
/// - Request an 8 byte random number from the MRTD's chip (rnd.icc)
/// - Generate an 8 byte random (rnd.ifd) and a 16 byte random (kifd)
/// - Concatenate rnd.ifd, rnd.icc and kifd (s = rnd.ifd + rnd.icc + kifd)
/// - Encrypt it with TDES and the Kenc key (eifd = TDES(s, Kenc))
/// - Compute the MAC over eifd with TDES and the Kmax key (mifd = mac(pad(eifd))
/// - Construct the APDU data for the mutualAuthenticate command (cmd_data = eifd + mifd)
///
/// @param rnd_icc: The challenge received from the ICC.
/// @type rnd_icc: A 8 bytes binary string
/// @return: The APDU binary data for the mutual authenticate command
func authentication( rnd_icc : [UInt8]) -> [UInt8] {
self.rnd_icc = rnd_icc
Log.verbose("Request an 8 byte random number from the MRTD's chip")
Log.verbose("\tRND.ICC: " + binToHexRep(self.rnd_icc))
self.rnd_icc = rnd_icc
let rnd_ifd = generateRandomUInt8Array(8)
let kifd = generateRandomUInt8Array(16)
Log.verbose("Generate an 8 byte random and a 16 byte random")
Log.verbose("\tRND.IFD: \(binToHexRep(rnd_ifd))" )
Log.verbose("\tRND.Kifd: \(binToHexRep(kifd))")
let s = rnd_ifd + rnd_icc + kifd
Log.verbose("Concatenate RND.IFD, RND.ICC and Kifd")
Log.verbose("\tS: \(binToHexRep(s))")
let iv : [UInt8] = [0, 0, 0, 0, 0, 0, 0, 0]
let eifd = tripleDESEncrypt(key: ksenc,message: s, iv: iv)
Log.verbose("Encrypt S with TDES key Kenc as calculated in Appendix 5.2")
Log.verbose("\tEifd: \(binToHexRep(eifd))")
let mifd = mac(algoName: .DES, key: ksmac, msg: pad(eifd, blockSize:8))
Log.verbose("Compute MAC over eifd with TDES key Kmac as calculated in-Appendix 5.2")
Log.verbose("\tMifd: \(binToHexRep(mifd))")
// Construct APDU
let cmd_data = eifd + mifd
Log.verbose("Construct command data for MUTUAL AUTHENTICATE")
Log.verbose("\tcmd_data: \(binToHexRep(cmd_data))")
self.rnd_ifd = rnd_ifd
self.kifd = kifd
return cmd_data
}
/// Calculate the session keys (KSenc, KSmac) and the SSC from the data
/// received by the mutual authenticate command.
/// @param data: the data received from the mutual authenticate command send to the chip.
/// @type data: a binary string
/// @return: A set of two 16 bytes keys (KSenc, KSmac) and the SSC
public func sessionKeys(data : [UInt8] ) throws -> ([UInt8], [UInt8], [UInt8]) {
Log.verbose("Decrypt and verify received data and compare received RND.IFD with generated RND.IFD \(binToHexRep(self.ksmac))" )
let response = tripleDESDecrypt(key: self.ksenc, message: [UInt8](data[0..<32]), iv: [0,0,0,0,0,0,0,0] )
let response_kicc = [UInt8](response[16..<32])
let Kseed = xor(self.kifd, response_kicc)
Log.verbose("Calculate XOR of Kifd and Kicc")
Log.verbose("\tKseed: \(binToHexRep(Kseed))" )
let smskg = SecureMessagingSessionKeyGenerator()
let KSenc = try smskg.deriveKey(keySeed: Kseed, mode: .ENC_MODE)
let KSmac = try smskg.deriveKey(keySeed: Kseed, mode: .MAC_MODE)
// let KSenc = self.keyDerivation(kseed: Kseed,c: KENC)
// let KSmac = self.keyDerivation(kseed: Kseed,c: KMAC)
Log.verbose("Calculate Session Keys (KSenc and KSmac) using Appendix 5.1")
Log.verbose("\tKSenc: \(binToHexRep(KSenc))" )
Log.verbose("\tKSmac: \(binToHexRep(KSmac))" )
let ssc = [UInt8](self.rnd_icc.suffix(4) + self.rnd_ifd.suffix(4))
Log.verbose("Calculate Send Sequence Counter")
Log.verbose("\tSSC: \(binToHexRep(ssc))" )
return (KSenc, KSmac, ssc)
}
}
#endif

View File

@@ -1,223 +0,0 @@
//
// ChipAuthenticationHandler.swift
// NFCPassportReader
//
// Created by Andy Qua on 25/02/2021.
//
import Foundation
import OpenSSL
#if !os(macOS)
import CoreNFC
import CryptoKit
@available(iOS 15, *)
class ChipAuthenticationHandler {
private static let NO_PACE_KEY_REFERENCE : UInt8 = 0x00
private static let ENC_MODE : UInt8 = 0x1
private static let MAC_MODE : UInt8 = 0x2
private static let PACE_MODE : UInt8 = 0x3
private static let COMMAND_CHAINING_CHUNK_SIZE = 224
var tagReader : TagReader?
var gaSegments = [[UInt8]]()
var chipAuthInfos = [Int:ChipAuthenticationInfo]()
var chipAuthPublicKeyInfos = [ChipAuthenticationPublicKeyInfo]()
var isChipAuthenticationSupported : Bool = false
public init(dg14 : DataGroup14, tagReader: TagReader) {
self.tagReader = tagReader
for secInfo in dg14.securityInfos {
if let cai = secInfo as? ChipAuthenticationInfo {
let keyId = cai.getKeyId()
chipAuthInfos[keyId] = cai
} else if let capki = secInfo as? ChipAuthenticationPublicKeyInfo {
chipAuthPublicKeyInfos.append(capki)
}
}
if chipAuthPublicKeyInfos.count > 0 {
isChipAuthenticationSupported = true
}
}
public func doChipAuthentication() async throws {
Log.info( "Performing Chip Authentication - number of public keys found - \(chipAuthPublicKeyInfos.count)" )
guard isChipAuthenticationSupported else {
throw NFCPassportReaderError.NotYetSupported( "ChipAuthentication not supported" )
}
var success = false
for pubKey in chipAuthPublicKeyInfos {
do {
success = try await self.doChipAuthentication( with: pubKey)
if success {
break
}
} catch {
// try next key
}
}
if !success {
throw NFCPassportReaderError.ChipAuthenticationFailed
}
}
private func doChipAuthentication( with chipAuthPublicKeyInfo : ChipAuthenticationPublicKeyInfo ) async throws -> Bool {
// So it turns out that some passports don't have ChipAuthInfo items.
// So if we do have a ChipAuthInfo the we take the keyId (if present) and OID from there,
// BUT if we don't then we will try to infer the OID from the public key
let keyId = chipAuthPublicKeyInfo.keyId
let chipAuthInfoOID : String
if let chipAuthInfo = chipAuthInfos[keyId ?? 0] {
chipAuthInfoOID = chipAuthInfo.oid
} else {
if let oid = inferOID( fromPublicKeyOID:chipAuthPublicKeyInfo.oid) {
chipAuthInfoOID = oid
} else {
return false
}
}
try await self.doCA( keyId: keyId, encryptionDetailsOID: chipAuthInfoOID, publicKey: chipAuthPublicKeyInfo.pubKey )
return true
}
/// Infer OID from public key type - Best guess seems to be to use 3DES_CBC_CBC for both ECDH and DH keys
/// Apparently works for French passports
private func inferOID(fromPublicKeyOID: String ) -> String? {
if fromPublicKeyOID == SecurityInfo.ID_PK_ECDH_OID {
Log.warning("No ChipAuthenticationInfo - guessing its id-CA-ECDH-3DES-CBC-CBC");
return SecurityInfo.ID_CA_ECDH_3DES_CBC_CBC_OID
} else if fromPublicKeyOID == SecurityInfo.ID_PK_DH_OID {
Log.warning("No ChipAuthenticationInfo - guessing its id-CA-DH-3DES-CBC-CBC");
return SecurityInfo.ID_CA_DH_3DES_CBC_CBC_OID
}
Log.warning("No ChipAuthenticationInfo and unsupported ChipAuthenticationPublicKeyInfo public key OID \(fromPublicKeyOID)")
return nil;
}
private func doCA( keyId: Int?, encryptionDetailsOID oid: String, publicKey: OpaquePointer) async throws {
// Generate Ephemeral Keypair from parameters from DG14 Public key
// This should work for both EC and DH keys
var ephemeralKeyPair : OpaquePointer? = nil
let pctx = EVP_PKEY_CTX_new(publicKey, nil)
EVP_PKEY_keygen_init(pctx)
EVP_PKEY_keygen(pctx, &ephemeralKeyPair)
EVP_PKEY_CTX_free(pctx)
// Send the public key to the passport
try await sendPublicKey(oid: oid, keyId: keyId, pcdPublicKey: ephemeralKeyPair!)
Log.debug( "Public Key successfully sent to passport!" )
// Use our ephemeral private key and the passports public key to generate a shared secret
// (the passport with do the same thing with their private key and our public key)
let sharedSecret = OpenSSLUtils.computeSharedSecret(privateKeyPair:ephemeralKeyPair!, publicKey:publicKey)
// Now try to restart Secure Messaging using the new shared secret and
try restartSecureMessaging( oid : oid, sharedSecret : sharedSecret, maxTranceiveLength : 1, shouldCheckMAC : true)
}
private func sendPublicKey(oid : String, keyId : Int?, pcdPublicKey : OpaquePointer) async throws {
let cipherAlg = try ChipAuthenticationInfo.toCipherAlgorithm(oid: oid)
guard let keyData = OpenSSLUtils.getPublicKeyData(from: pcdPublicKey) else {
throw NFCPassportReaderError.InvalidDataPassed("Unable to get public key data from public key" )
}
if cipherAlg.hasPrefix("DESede") {
var idData : [UInt8] = []
if let keyId = keyId {
idData = intToBytes( val:keyId, removePadding:true)
idData = wrapDO( b:0x84, arr:idData)
}
let wrappedKeyData = wrapDO( b:0x91, arr:keyData)
_ = try await self.tagReader?.sendMSEKAT(keyData: Data(wrappedKeyData), idData: Data(idData))
} else if cipherAlg.hasPrefix("AES") {
_ = try await self.tagReader?.sendMSESetATIntAuth(oid: oid, keyId: keyId)
let data = wrapDO(b: 0x80, arr:keyData)
gaSegments = self.chunk(data: data, segmentSize: ChipAuthenticationHandler.COMMAND_CHAINING_CHUNK_SIZE )
try await self.handleGeneralAuthentication()
} else {
throw NFCPassportReaderError.InvalidDataPassed("Cipher Algorithm \(cipherAlg) not supported")
}
}
private func handleGeneralAuthentication() async throws {
repeat {
// Pull next segment from list
let segment = gaSegments.removeFirst()
let isLast = gaSegments.isEmpty
// send it
_ = try await self.tagReader?.sendGeneralAuthenticate(data: segment, isLast: isLast)
} while ( !gaSegments.isEmpty )
}
private func restartSecureMessaging( oid : String, sharedSecret : [UInt8], maxTranceiveLength : Int, shouldCheckMAC : Bool) throws {
let cipherAlg = try ChipAuthenticationInfo.toCipherAlgorithm(oid: oid)
let keyLength = try ChipAuthenticationInfo.toKeyLength(oid: oid)
// Start secure messaging.
let smskg = SecureMessagingSessionKeyGenerator()
let ksEnc = try smskg.deriveKey(keySeed: sharedSecret, cipherAlgName: cipherAlg, keyLength: keyLength, mode: .ENC_MODE)
let ksMac = try smskg.deriveKey(keySeed: sharedSecret, cipherAlgName: cipherAlg, keyLength: keyLength, mode: .MAC_MODE)
let ssc = withUnsafeBytes(of: 0.bigEndian, Array.init)
if (cipherAlg.hasPrefix("DESede")) {
Log.info( "Restarting secure messaging using DESede encryption")
let sm = SecureMessaging(encryptionAlgorithm: .DES, ksenc: ksEnc, ksmac: ksMac, ssc: ssc)
tagReader?.secureMessaging = sm
} else if (cipherAlg.hasPrefix("AES")) {
Log.info( "Restarting secure messaging using AES encryption")
let sm = SecureMessaging(encryptionAlgorithm: .AES, ksenc: ksEnc, ksmac: ksMac, ssc: ssc)
tagReader?.secureMessaging = sm
} else {
Log.error( "Not restarting secure messaging as unsupported cipher algorithm requested - \(cipherAlg)")
throw NFCPassportReaderError.InvalidDataPassed("Unsupported cipher algorithm \(cipherAlg)" )
}
}
func inferDigestAlgorithmFromCipherAlgorithmForKeyDerivation( cipherAlg : String, keyLength : Int) throws -> String {
if cipherAlg == "DESede" || cipherAlg == "AES-128" {
return "SHA1"
}
if cipherAlg == "AES" && keyLength == 128 {
return "SHA1"
}
if cipherAlg == "AES-256" || cipherAlg == "AES-192" {
return "SHA256"
}
if cipherAlg == "AES" && (keyLength == 192 || keyLength == 256) {
return "SHA256"
}
throw NFCPassportReaderError.InvalidDataPassed("Unsupported cipher algorithm or key length")
}
/// Chunks up a byte array into a number of segments of the given size,
/// and a final segment if there is a remainder.
/// - Parameter segmentSize the number of bytes per segment
/// - Parameter data the data to be partitioned
/// - Parameter a list with the segments
func chunk( data : [UInt8], segmentSize: Int ) -> [[UInt8]] {
return stride(from: 0, to: data.count, by: segmentSize).map {
Array(data[$0 ..< Swift.min($0 + segmentSize, data.count)])
}
}
}
#endif

View File

@@ -1,16 +0,0 @@
//
// DataGroupHash.swift
// NFCPassportReader
//
// Created by Andy Qua on 09/02/2021.
// Copyright © 2021 Andy Qua. All rights reserved.
//
@available(iOS 13, macOS 10.15, *)
public struct DataGroupHash {
public var id: String
public var sodHash: String
public var computedHash : String
public var match : Bool
}

View File

@@ -1,36 +0,0 @@
//
// DataGroupParser.swift
//
// Created by Andy Qua on 14/06/2019.
//
import OpenSSL
@available(iOS 13, macOS 10.15, *)
class DataGroupParser {
static let dataGroupNames = ["Common", "DG1", "DG2", "DG3", "DG4", "DG5", "DG6", "DG7", "DG8", "DG9", "DG10", "DG11", "DG12", "DG13", "DG14", "DG15", "DG16", "SecurityData"]
static let tags : [UInt8] = [0x60, 0x61, 0x75, 0x63, 0x76, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x77]
static let classes : [DataGroup.Type] = [COM.self, DataGroup1.self, DataGroup2.self,
NotImplementedDG.self, NotImplementedDG.self, NotImplementedDG.self,
NotImplementedDG.self, DataGroup7.self, NotImplementedDG.self,
NotImplementedDG.self, NotImplementedDG.self, DataGroup11.self,
DataGroup12.self, NotImplementedDG.self, DataGroup14.self,
DataGroup15.self, NotImplementedDG.self, SOD.self]
func parseDG( data : [UInt8] ) throws -> DataGroup {
let header = data[0..<4]
let dg = try tagToDG(header[0])
return try dg.init(data)
}
func tagToDG( _ tag : UInt8 ) throws -> DataGroup.Type {
guard let index = DataGroupParser.tags.firstIndex(of: tag) else { throw NFCPassportReaderError.UnknownTag}
return DataGroupParser.classes[index]
}
}

View File

@@ -1,145 +0,0 @@
//
// Errors.swift
// NFCPassportReader
//
// Created by Andy Qua on 09/02/2021.
// Copyright © 2021 Andy Qua. All rights reserved.
//
import Foundation
// MARK: TagError
@available(iOS 13, macOS 10.15, *)
public enum NFCPassportReaderError: Error {
case ResponseError(String, UInt8, UInt8)
case InvalidResponse
case UnexpectedError
case NFCNotSupported
case NoConnectedTag
case D087Malformed
case InvalidResponseChecksum
case MissingMandatoryFields
case CannotDecodeASN1Length
case InvalidASN1Value
case UnableToProtectAPDU
case UnableToUnprotectAPDU
case UnsupportedDataGroup
case DataGroupNotRead
case UnknownTag
case UnknownImageFormat
case NotImplemented
case TagNotValid
case ConnectionError
case UserCanceled
case InvalidMRZKey
case MoreThanOneTagFound
case InvalidHashAlgorithmSpecified
case UnsupportedCipherAlgorithm
case UnsupportedMappingType
case PACEError(String,String)
case ChipAuthenticationFailed
case InvalidDataPassed(String)
case NotYetSupported(String)
var value: String {
switch self {
case .ResponseError(let errMsg, _, _): return errMsg
case .InvalidResponse: return "InvalidResponse"
case .UnexpectedError: return "UnexpectedError"
case .NFCNotSupported: return "NFCNotSupported"
case .NoConnectedTag: return "NoConnectedTag"
case .D087Malformed: return "D087Malformed"
case .InvalidResponseChecksum: return "InvalidResponseChecksum"
case .MissingMandatoryFields: return "MissingMandatoryFields"
case .CannotDecodeASN1Length: return "CannotDecodeASN1Length"
case .InvalidASN1Value: return "InvalidASN1Value"
case .UnableToProtectAPDU: return "UnableToProtectAPDU"
case .UnableToUnprotectAPDU: return "UnableToUnprotectAPDU"
case .UnsupportedDataGroup: return "UnsupportedDataGroup"
case .DataGroupNotRead: return "DataGroupNotRead"
case .UnknownTag: return "UnknownTag"
case .UnknownImageFormat: return "UnknownImageFormat"
case .NotImplemented: return "NotImplemented"
case .TagNotValid: return "TagNotValid"
case .ConnectionError: return "ConnectionError"
case .UserCanceled: return "UserCanceled"
case .InvalidMRZKey: return "InvalidMRZKey"
case .MoreThanOneTagFound: return "MoreThanOneTagFound"
case .InvalidHashAlgorithmSpecified: return "InvalidHashAlgorithmSpecified"
case .UnsupportedCipherAlgorithm: return "UnsupportedCipherAlgorithm"
case .UnsupportedMappingType: return "UnsupportedMappingType"
case .PACEError(let step, let reason): return "PACEError (\(step)) - \(reason)"
case .ChipAuthenticationFailed: return "ChipAuthenticationFailed"
case .InvalidDataPassed(let reason) : return "Invalid data passed - \(reason)"
case .NotYetSupported(let reason) : return "Not yet supported - \(reason)"
}
}
}
@available(iOS 13, macOS 10.15, *)
extension NFCPassportReaderError: LocalizedError {
public var errorDescription: String? {
return NSLocalizedString(value, comment: "My error")
}
}
// MARK: OpenSSLError
@available(iOS 13, macOS 10.15, *)
public enum OpenSSLError: Error {
case UnableToGetX509CertificateFromPKCS7(String)
case UnableToVerifyX509CertificateForSOD(String)
case VerifyAndReturnSODEncapsulatedData(String)
case UnableToReadECPublicKey(String)
case UnableToExtractSignedDataFromPKCS7(String)
case VerifySignedAttributes(String)
case UnableToParseASN1(String)
case UnableToDecryptRSASignature(String)
}
@available(iOS 13, macOS 10.15, *)
extension OpenSSLError: LocalizedError {
public var errorDescription: String? {
switch self {
case .UnableToGetX509CertificateFromPKCS7(let reason):
return NSLocalizedString("Unable to read the SOD PKCS7 Certificate. \(reason)", comment: "UnableToGetPKCS7CertificateForSOD")
case .UnableToVerifyX509CertificateForSOD(let reason):
return NSLocalizedString("Unable to verify the SOD X509 certificate. \(reason)", comment: "UnableToVerifyX509CertificateForSOD")
case .VerifyAndReturnSODEncapsulatedData(let reason):
return NSLocalizedString("Unable to verify the SOD Datagroup hashes. \(reason)", comment: "UnableToGetSignedDataFromPKCS7")
case .UnableToReadECPublicKey(let reason):
return NSLocalizedString("Unable to read ECDSA Public key \(reason)!", comment: "UnableToReadECPublicKey")
case .UnableToExtractSignedDataFromPKCS7(let reason):
return NSLocalizedString("Unable to extract Signer data from PKCS7 \(reason)!", comment: "UnableToExtractSignedDataFromPKCS7")
case .VerifySignedAttributes(let reason):
return NSLocalizedString("Unable to Verify the SOD SignedAttributes \(reason)!", comment: "UnableToExtractSignedDataFromPKCS7")
case .UnableToParseASN1(let reason):
return NSLocalizedString("Unable to parse ASN1 \(reason)!", comment: "UnableToParseASN1")
case .UnableToDecryptRSASignature(let reason):
return NSLocalizedString("DatUnable to decrypt RSA Signature \(reason)!", comment: "UnableToDecryptRSSignature")
}
}
}
// MARK: PassiveAuthenticationError
public enum PassiveAuthenticationError: Error {
case UnableToParseSODHashes(String)
case InvalidDataGroupHash(String)
case SODMissing(String)
}
extension PassiveAuthenticationError: LocalizedError {
public var errorDescription: String? {
switch self {
case .UnableToParseSODHashes(let reason):
return NSLocalizedString("Unable to parse the SOD Datagroup hashes. \(reason)", comment: "UnableToParseSODHashes")
case .InvalidDataGroupHash(let reason):
return NSLocalizedString("DataGroup hash not present or didn't match \(reason)!", comment: "InvalidDataGroupHash")
case .SODMissing(let reason):
return NSLocalizedString("DataGroup SOD not present or not read \(reason)!", comment: "SODMissing")
}
}
}

View File

@@ -1,68 +0,0 @@
//
// Logging.swift
// NFCTest
//
// Created by Andy Qua on 11/06/2019.
// Copyright © 2019 Andy Qua. All rights reserved.
//
import Foundation
// TODO: Quick log functions - will move this to something better
public enum LogLevel : Int, CaseIterable {
case verbose = 0
case debug = 1
case info = 2
case warning = 3
case error = 4
case none = 5
}
public class Log {
public static var logLevel : LogLevel = .info
public static var storeLogs = false
public static var logData = [String]()
private static let df = DateFormatter()
private static var dfInit = false
public class func verbose( _ msg : @autoclosure () -> String ) {
log( .verbose, msg )
}
public class func debug( _ msg : @autoclosure () -> String ) {
log( .debug, msg )
}
public class func info( _ msg : @autoclosure () -> String ) {
log( .info, msg )
}
public class func warning( _ msg : @autoclosure () -> String ) {
log( .warning, msg )
}
public class func error( _ msg : @autoclosure () -> String ) {
log( .error, msg )
}
public class func clearStoredLogs() {
logData.removeAll()
}
class func log( _ logLevel : LogLevel, _ msg : () -> String ) {
guard logLevel != .none else { return }
if !dfInit {
df.dateFormat = "y-MM-dd H:m:ss.SSSS"
dfInit = true
}
if self.logLevel.rawValue <= logLevel.rawValue {
let message = msg()
print( "\(df.string(from:Date())) - \(message)" )
if storeLogs {
logData.append( message )
}
}
}
}

View File

@@ -1,534 +0,0 @@
//
// NFCPassportModel.swift
// NFCPassportReader
//
// Created by Andy Qua on 29/10/2019.
//
import Foundation
#if os(iOS)
import UIKit
#endif
public enum PassportAuthenticationStatus {
case notDone
case success
case failed
}
@available(iOS 13, macOS 10.15, *)
public class NFCPassportModel {
public private(set) lazy var documentType : String = { return String( passportDataElements?["5F03"]?.first ?? "?" ) }()
public private(set) lazy var documentSubType : String = { return String( passportDataElements?["5F03"]?.last ?? "?" ) }()
public private(set) lazy var documentNumber : String = { return (passportDataElements?["5A"] ?? "?").replacingOccurrences(of: "<", with: "" ) }()
public private(set) lazy var issuingAuthority : String = { return passportDataElements?["5F28"] ?? "?" }()
public private(set) lazy var documentExpiryDate : String = { return passportDataElements?["59"] ?? "?" }()
public private(set) lazy var dateOfBirth : String = { return passportDataElements?["5F57"] ?? "?" }()
public private(set) lazy var gender : String = { return passportDataElements?["5F35"] ?? "?" }()
public private(set) lazy var nationality : String = { return passportDataElements?["5F2C"] ?? "?" }()
public private(set) lazy var lastName : String = {
return names[0].replacingOccurrences(of: "<", with: " " )
}()
public private(set) lazy var firstName : String = {
var name = ""
for i in 1 ..< names.count {
let fn = names[i].replacingOccurrences(of: "<", with: " " ).trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
name += fn + " "
}
return name.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
}()
public private(set) lazy var passportMRZ : String = { return passportDataElements?["5F1F"] ?? "NOT FOUND" }()
// Extract fields from DG11 if present
private lazy var names : [String] = {
guard let dg11 = dataGroupsRead[.DG11] as? DataGroup11,
let fullName = dg11.fullName?.components(separatedBy: "<<") else { return (passportDataElements?["5B"] ?? "?").components(separatedBy: "<<") }
return fullName
}()
public private(set) lazy var placeOfBirth : String? = {
guard let dg11 = dataGroupsRead[.DG11] as? DataGroup11,
let placeOfBirth = dg11.placeOfBirth else { return nil }
return placeOfBirth
}()
/// residence address
public private(set) lazy var residenceAddress : String? = {
guard let dg11 = dataGroupsRead[.DG11] as? DataGroup11,
let address = dg11.address else { return nil }
return address
}()
/// phone number
public private(set) lazy var phoneNumber : String? = {
guard let dg11 = dataGroupsRead[.DG11] as? DataGroup11,
let telephone = dg11.telephone else { return nil }
return telephone
}()
/// personal number
public private(set) lazy var personalNumber : String? = {
if let dg11 = dataGroupsRead[.DG11] as? DataGroup11,
let personalNumber = dg11.personalNumber { return personalNumber }
return (passportDataElements?["53"] ?? "?").replacingOccurrences(of: "<", with: "" )
}()
public private(set) lazy var documentSigningCertificate : X509Wrapper? = {
return certificateSigningGroups[.documentSigningCertificate]
}()
public private(set) lazy var countrySigningCertificate : X509Wrapper? = {
return certificateSigningGroups[.issuerSigningCertificate]
}()
// Extract data from COM
public private(set) lazy var LDSVersion : String = {
guard let com = dataGroupsRead[.COM] as? COM else { return "Unknown" }
return com.version
}()
public private(set) lazy var dataGroupsPresent : [String] = {
guard let com = dataGroupsRead[.COM] as? COM else { return [] }
return com.dataGroupsPresent
}()
// Parsed datagroup hashes
public private(set) var dataGroupsAvailable = [DataGroupId]()
public private(set) var dataGroupsRead : [DataGroupId:DataGroup] = [:]
public private(set) var dataGroupHashes = [DataGroupId: DataGroupHash]()
public internal(set) var cardAccess : CardAccess?
public internal(set) var BACStatus : PassportAuthenticationStatus = .notDone
public internal(set) var PACEStatus : PassportAuthenticationStatus = .notDone
public internal(set) var chipAuthenticationStatus : PassportAuthenticationStatus = .notDone
public private(set) var passportCorrectlySigned : Bool = false
public private(set) var documentSigningCertificateVerified : Bool = false
public private(set) var passportDataNotTampered : Bool = false
public private(set) var activeAuthenticationPassed : Bool = false
public private(set) var activeAuthenticationChallenge : [UInt8] = []
public private(set) var activeAuthenticationSignature : [UInt8] = []
public private(set) var verificationErrors : [Error] = []
public var isPACESupported : Bool {
get {
if cardAccess?.paceInfo != nil {
return true
} else {
// We may not have stored the cardAccess so check the DG14
if let dg14 = dataGroupsRead[.DG14] as? DataGroup14,
(dg14.securityInfos.filter { ($0 as? PACEInfo) != nil }).count > 0 {
return true
}
return false
}
}
}
public var isChipAuthenticationSupported : Bool {
get {
if let dg14 = dataGroupsRead[.DG14] as? DataGroup14,
(dg14.securityInfos.filter { ($0 as? ChipAuthenticationPublicKeyInfo) != nil }).count > 0 {
return true
} else {
return false
}
}
}
#if os(iOS)
public var passportImage : UIImage? {
guard let dg2 = dataGroupsRead[.DG2] as? DataGroup2 else { return nil }
return dg2.getImage()
}
public var signatureImage : UIImage? {
guard let dg7 = dataGroupsRead[.DG7] as? DataGroup7 else { return nil }
return dg7.getImage()
}
#endif
public var activeAuthenticationSupported : Bool {
guard let dg15 = dataGroupsRead[.DG15] as? DataGroup15 else { return false }
if dg15.ecdsaPublicKey != nil || dg15.rsaPublicKey != nil {
return true
}
return false
}
private var certificateSigningGroups : [CertificateType:X509Wrapper] = [:]
private var passportDataElements : [String:String]? {
guard let dg1 = dataGroupsRead[.DG1] as? DataGroup1 else { return nil }
return dg1.elements
}
public init() {
}
public init( from dump: [String:String] ) {
var AAChallenge : [UInt8]?
var AASignature : [UInt8]?
for (key,value) in dump {
if let data = Data(base64Encoded: value) {
let bin = [UInt8](data)
if key == "AASignature" {
AASignature = bin
} else if key == "AAChallenge" {
AAChallenge = bin
} else {
do {
let dg = try DataGroupParser().parseDG(data: bin)
let dgId = DataGroupId.getIDFromName(name:key)
self.addDataGroup( dgId, dataGroup:dg )
} catch {
Log.error("Failed to import Datagroup - \(key) from dump - \(error)" )
}
}
}
}
// See if we have Active Auth info in the dump
if let challenge = AAChallenge, let signature = AASignature {
verifyActiveAuthentication(challenge: challenge, signature: signature)
}
}
public func addDataGroup(_ id : DataGroupId, dataGroup: DataGroup ) {
self.dataGroupsRead[id] = dataGroup
if id != .COM && id != .SOD {
self.dataGroupsAvailable.append( id )
}
}
public func getDataGroup( _ id : DataGroupId ) -> DataGroup? {
return dataGroupsRead[id]
}
/// Dumps the passport data
/// - Parameters:
/// selectedDataGroups - the Data Groups to be exported (if they are present in the passport)
/// includeActiveAutheticationData - Whether to include the Active Authentication challenge and response (if supported and retrieved)
/// - Returns: dictionary of DataGroup ids and Base64 encoded data
public func dumpPassportData( selectedDataGroups : [DataGroupId], includeActiveAuthenticationData : Bool = false) -> [String:String] {
var ret = [String:String]()
for dg in selectedDataGroups {
if let dataGroup = self.dataGroupsRead[dg] {
let val = Data(dataGroup.data)
let base64 = val.base64EncodedString()
ret[dg.getName()] = base64
}
}
if includeActiveAuthenticationData && self.activeAuthenticationSupported {
ret["AAChallenge"] = Data(activeAuthenticationChallenge).base64EncodedString()
ret["AASignature"] = Data(activeAuthenticationSignature).base64EncodedString()
}
return ret
}
public func getHashesForDatagroups( hashAlgorythm: String ) -> [DataGroupId:[UInt8]] {
var ret = [DataGroupId:[UInt8]]()
for (key, value) in dataGroupsRead {
if hashAlgorythm == "SHA1" {
ret[key] = calcSHA1Hash(value.body)
} else if hashAlgorythm == "SHA224" {
ret[key] = calcSHA224Hash(value.body)
} else if hashAlgorythm == "SHA256" {
ret[key] = calcSHA256Hash(value.body)
} else if hashAlgorythm == "SHA384" {
ret[key] = calcSHA384Hash(value.body)
} else if hashAlgorythm == "SHA512" {
ret[key] = calcSHA512Hash(value.body)
}
}
return ret
}
/// This method performs the passive authentication
/// Passive Authentication : Two Parts:
/// Part 1 - Has the SOD (Security Object Document) been signed by a valid country signing certificate authority (CSCA)?
/// Part 2 - has it been tampered with (e.g. hashes of Datagroups match those in the SOD?
/// guard let sod = model.getDataGroup(.SOD) else { return }
///
/// - Parameter masterListURL: the path to the masterlist to try to verify the document signing certiifcate in the SOD
/// - Parameter useCMSVerification: Should we use OpenSSL CMS verification to verify the SOD content
/// is correctly signed by the document signing certificate OR should we do this manully based on RFC5652
/// CMS fails under certain circumstances (e.g. hashes are SHA512 whereas content is signed with SHA256RSA).
/// Currently defaulting to manual verification - hoping this will replace the CMS verification totally
/// CMS Verification currently there just in case
public func verifyPassport( masterListURL: URL?, useCMSVerification : Bool = false ) {
if let masterListURL = masterListURL {
do {
try validateAndExtractSigningCertificates( masterListURL: masterListURL )
} catch let error {
verificationErrors.append( error )
}
}
do {
try ensureReadDataNotBeenTamperedWith( useCMSVerification : useCMSVerification )
} catch let error {
verificationErrors.append( error )
}
}
public func verifyActiveAuthentication( challenge: [UInt8], signature: [UInt8] ) {
self.activeAuthenticationChallenge = challenge
self.activeAuthenticationSignature = signature
Log.verbose( "Active Authentication")
Log.verbose( " challange - \(binToHexRep(challenge))")
Log.verbose( " signature - \(binToHexRep(signature))")
// Get AA Public key
self.activeAuthenticationPassed = false
guard let dg15 = self.dataGroupsRead[.DG15] as? DataGroup15 else { return }
if let rsaKey = dg15.rsaPublicKey {
do {
var decryptedSig = try OpenSSLUtils.decryptRSASignature(signature: Data(signature), pubKey: rsaKey)
// Decrypted signature compromises of header (6A), Message, Digest hash, Trailer
// Trailer can be 1 byte (BC - SHA-1 hash) or 2 bytes (xxCC) - where xx identifies the hash algorithm used
// if the last byte of the digest is 0xBC, then this uses dedicated hash function 3 (SHA-1),
// If the last byte is 0xCC, then the preceding byte tells you which hash function
// should be used (currently not yet implemented!)
// See ISO/IEC9796-2 for details on the verification and ISO/IEC 10118-3 for the dedicated hash functions!
var hashTypeByte = decryptedSig.popLast() ?? 0x00
if hashTypeByte == 0xCC {
hashTypeByte = decryptedSig.popLast() ?? 0x00
}
var hashType : String = ""
var hashLength = 0
switch hashTypeByte {
case 0xBC, 0x33:
hashType = "SHA1"
hashLength = 20 // 160 bits for SHA-1 -> 20 bytes
case 0x34:
hashType = "SHA256"
hashLength = 32 // 256 bits for SHA-256 -> 32 bytes
case 0x35:
hashType = "SHA512"
hashLength = 64 // 512 bits for SHA-512 -> 64 bytes
case 0x36:
hashType = "SHA384"
hashLength = 48 // 384 bits for SHA-384 -> 48 bytes
case 0x38:
hashType = "SHA224"
hashLength = 28 // 224 bits for SHA-224 -> 28 bytes
default:
Log.error( "Error identifying Active Authentication RSA message digest hash algorithm" )
return
}
let message = [UInt8](decryptedSig[1 ..< (decryptedSig.count-hashLength)])
let digest = [UInt8](decryptedSig[(decryptedSig.count-hashLength)...])
// Concatenate the challenge to the end of the message
let fullMsg = message + challenge
// Then generate the hash
let msgHash : [UInt8] = try calcHash(data: fullMsg, hashAlgorithm: hashType)
// Check hashes match
if msgHash == digest {
self.activeAuthenticationPassed = true
Log.debug( "Active Authentication (RSA) successful" )
} else {
Log.error( "Error verifying Active Authentication RSA signature - Hash doesn't match" )
}
} catch {
Log.error( "Error verifying Active Authentication RSA signature - \(error)" )
}
} else if let ecdsaPublicKey = dg15.ecdsaPublicKey {
var digestType = ""
if let dg14 = dataGroupsRead[.DG14] as? DataGroup14,
let aa = dg14.securityInfos.compactMap({ $0 as? ActiveAuthenticationInfo }).first {
digestType = aa.getSignatureAlgorithmOIDString() ?? ""
}
if OpenSSLUtils.verifyECDSASignature( publicKey:ecdsaPublicKey, signature: signature, data: challenge, digestType: digestType ) {
self.activeAuthenticationPassed = true
Log.debug( "Active Authentication (ECDSA) successful" )
} else {
Log.error( "Error verifying Active Authentication ECDSA signature" )
}
}
}
// Check if signing certificate is on the revocation list
// We do this by trying to build a trust chain of the passport certificate against the ones in the revocation list
// and if we are successful then its been revoked.
// NOTE - NOT USED YET AS NOT ABLE TO TEST
func hasCertBeenRevoked( revocationListURL : URL ) -> Bool {
var revoked = false
do {
try validateAndExtractSigningCertificates( masterListURL: revocationListURL )
// Certificate chain found - which means certificate is on revocation list
revoked = true
} catch {
// No chain found - certificate not revoked
}
return revoked
}
private func validateAndExtractSigningCertificates( masterListURL: URL ) throws {
self.passportCorrectlySigned = false
guard let sod = getDataGroup(.SOD) else {
throw PassiveAuthenticationError.SODMissing("No SOD found" )
}
let data = Data(sod.body)
let cert = try OpenSSLUtils.getX509CertificatesFromPKCS7( pkcs7Der: data ).first!
self.certificateSigningGroups[.documentSigningCertificate] = cert
let rc = OpenSSLUtils.verifyTrustAndGetIssuerCertificate( x509:cert, CAFile: masterListURL )
switch rc {
case .success(let csca):
self.certificateSigningGroups[.issuerSigningCertificate] = csca
case .failure(let error):
throw error
}
Log.debug( "Passport passed SOD Verification" )
self.passportCorrectlySigned = true
}
private func ensureReadDataNotBeenTamperedWith( useCMSVerification: Bool ) throws {
guard let sod = getDataGroup(.SOD) as? SOD else {
throw PassiveAuthenticationError.SODMissing("No SOD found" )
}
// Get SOD Content and verify that its correctly signed by the Document Signing Certificate
var signedData : Data
documentSigningCertificateVerified = false
do {
if useCMSVerification {
signedData = try OpenSSLUtils.verifyAndReturnSODEncapsulatedDataUsingCMS(sod: sod)
} else {
signedData = try OpenSSLUtils.verifyAndReturnSODEncapsulatedData(sod: sod)
}
documentSigningCertificateVerified = true
} catch {
signedData = try sod.getEncapsulatedContent()
}
// Now Verify passport data by comparing compare Hashes in SOD against
// computed hashes to ensure data not been tampered with
passportDataNotTampered = false
let asn1Data = try OpenSSLUtils.ASN1Parse( data: signedData )
let (sodHashAlgorythm, sodHashes) = try parseSODSignatureContent( asn1Data )
var errors : String = ""
// pour chaque dataGroupsRead, il faut que le hash sodHashVal parsé du eContent (signedData) soit égal
// au computedHashVal qui sort du dataGroupsRead
for (id,dgVal) in dataGroupsRead {
guard let sodHashVal = sodHashes[id] else {
// SOD and COM don't have hashes so these aren't errors
if id != .SOD && id != .COM {
errors += "DataGroup \(id) is missing!\n"
}
continue
}
let computedHashVal = binToHexRep(dgVal.hash(sodHashAlgorythm))
var match = true
if computedHashVal != sodHashVal {
errors += "\(id) invalid hash:\n SOD hash:\(sodHashVal)\n Computed hash:\(computedHashVal)\n"
match = false
}
dataGroupHashes[id] = DataGroupHash(id: id.getName(), sodHash:sodHashVal, computedHash:computedHashVal, match:match)
}
if errors != "" {
Log.error( "HASH ERRORS - \(errors)" )
throw PassiveAuthenticationError.InvalidDataGroupHash(errors)
}
Log.debug( "Passport passed Datagroup Tampering check" )
passportDataNotTampered = true
}
/// Parses an text ASN1 structure, and extracts the Hash Algorythm and Hashes contained from the Octect strings
/// - Parameter content: the text ASN1 stucure format
/// - Returns: The Hash Algorythm used - either SHA1 or SHA256, and a dictionary of hashes for the datagroups (currently only DG1 and DG2 are handled)
private func parseSODSignatureContent( _ content : String ) throws -> (String, [DataGroupId : String]){
var currentDG = ""
var sodHashAlgo = ""
var sodHashes : [DataGroupId : String] = [:]
let lines = content.components(separatedBy: "\n")
let dgList : [DataGroupId] = [.COM,.DG1,.DG2,.DG3,.DG4,.DG5,.DG6,.DG7,.DG8,.DG9,.DG10,.DG11,.DG12,.DG13,.DG14,.DG15,.DG16,.SOD]
for line in lines {
if line.contains( "d=2" ) && line.contains( "OBJECT" ) {
if line.contains( "sha1" ) {
sodHashAlgo = "SHA1"
} else if line.contains( "sha224" ) {
sodHashAlgo = "SHA224"
} else if line.contains( "sha256" ) {
sodHashAlgo = "SHA256"
} else if line.contains( "sha384" ) {
sodHashAlgo = "SHA384"
} else if line.contains( "sha512" ) {
sodHashAlgo = "SHA512"
}
} else if line.contains("d=3" ) && line.contains( "INTEGER" ) {
if let range = line.range(of: "INTEGER") {
let substr = line[range.upperBound..<line.endIndex]
if let r2 = substr.range(of: ":") {
currentDG = String(line[r2.upperBound...])
}
}
} else if line.contains("d=3" ) && line.contains( "OCTET STRING" ) {
if let range = line.range(of: "[HEX DUMP]:") {
let val = line[range.upperBound..<line.endIndex]
if currentDG != "", let id = Int(currentDG, radix:16) {
sodHashes[dgList[id]] = String(val)
currentDG = ""
}
}
}
}
if sodHashAlgo == "" {
throw PassiveAuthenticationError.UnableToParseSODHashes("Unable to find hash algorythm used" )
}
if sodHashes.count == 0 {
throw PassiveAuthenticationError.UnableToParseSODHashes("Unable to extract hashes" )
}
Log.debug( "Parse SOD - Using Algo - \(sodHashAlgo)" )
Log.debug( " - Hashes - \(sodHashes)" )
return (sodHashAlgo, sodHashes)
}
}

View File

@@ -1,6 +1,6 @@
//
// PassportReader.m
// AwesomeProject
// ProofOfPassport
//
// Created by Y E on 27/07/2023.
//

View File

@@ -1,6 +1,6 @@
//
// PassportReader.swift
// AwesomeProject
// ProofOfPassport
//
// Created by Y E on 27/07/2023.
//
@@ -8,6 +8,26 @@
import Foundation
import React
import NFCPassportReader
import Security
@available(iOS 13, macOS 10.15, *)
extension CertificateType {
func stringValue() -> String {
switch self {
case .documentSigningCertificate:
return "documentSigningCertificate"
case .issuerSigningCertificate:
return "issuerSigningCertificate"
}
}
}
// Helper function to map the keys of a dictionary
extension Dictionary {
func mapKeys<T: Hashable>(_ transform: (Key) -> T) -> Dictionary<T, Value> {
Dictionary<T, Value>(uniqueKeysWithValues: map { (transform($0.key), $0.value) })
}
}
@available(iOS 15, *)
@objc(PassportReader)
@@ -58,42 +78,276 @@ class PassportReader: NSObject{
@objc(scanPassport:dateOfBirth:dateOfExpiry:resolve:reject:)
func scanPassport(_ passportNumber: String, dateOfBirth: String, dateOfExpiry: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
let customMessageHandler : (NFCViewDisplayMessage)->String? = { (displayMessage) in
switch displayMessage {
case .requestPresentPassport:
return "Hold your iPhone near ann NFC enabled passport."
default:
// Return nil for all other messages so we use the provided default
return nil
}
}
switch displayMessage {
case .requestPresentPassport:
return "Hold your iPhone against an NFC enabled passport."
default:
// Return nil for all other messages so we use the provided default
return nil
}
}
Task { [weak self] in
guard let self = self else {
return
}
guard let self = self else {
return
}
do {
let mrzKey = getMRZKey( passportNumber: passportNumber, dateOfBirth: dateOfBirth, dateOfExpiry: dateOfExpiry)
let masterListURL = Bundle.main.url(forResource: "masterList", withExtension: ".pem")
passportReader.setMasterListURL( masterListURL! )
do {
let mrzKey = getMRZKey( passportNumber: passportNumber, dateOfBirth: dateOfBirth, dateOfExpiry: dateOfExpiry)
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, customDisplayMessage: customMessageHandler)
let passportData = passport.lastName
resolve(passportData)
} catch {
reject("E_PASSPORT_READ", "Failed to read passport", error)
}
var ret = [String:String]()
print("documentType", passport.documentType)
ret["documentType"] = passport.documentType
ret["documentSubType"] = passport.documentSubType
ret["documentNumber"] = passport.documentNumber
ret["issuingAuthority"] = passport.issuingAuthority
ret["documentExpiryDate"] = passport.documentExpiryDate
ret["dateOfBirth"] = passport.dateOfBirth
ret["gender"] = passport.gender
ret["nationality"] = passport.nationality
ret["lastName"] = passport.lastName
ret["firstName"] = passport.firstName
ret["passportMRZ"] = passport.passportMRZ
ret["placeOfBirth"] = passport.placeOfBirth
ret["residenceAddress"] = passport.residenceAddress
ret["phoneNumber"] = passport.phoneNumber
ret["personalNumber"] = passport.personalNumber
// documentSigningCertificate
// countrySigningCertificate
if let serializedDocumentSigningCertificate = serializeX509Wrapper(passport.documentSigningCertificate) {
ret["documentSigningCertificate"] = serializedDocumentSigningCertificate
}
if let serializedCountrySigningCertificate = serializeX509Wrapper(passport.countrySigningCertificate) {
ret["countrySigningCertificate"] = serializedCountrySigningCertificate
}
print("passport.documentSigningCertificate", passport.documentSigningCertificate)
print("passport.countrySigningCertificate", passport.countrySigningCertificate)
ret["LDSVersion"] = passport.LDSVersion
ret["dataGroupsPresent"] = passport.dataGroupsPresent.joined(separator: ", ")
print("passport.LDSVersion", passport.LDSVersion)
// ret["dataGroupsAvailable"] = passport.dataGroupsAvailable.map(dataGroupIdToString)
print("passport.dataGroupsAvailable", passport.dataGroupsAvailable)
print("passport.dataGroupsRead", passport.dataGroupsRead)
print("passport.dataGroupHashes", passport.dataGroupHashes)
// do {
// let dataGroupsReadData = try JSONSerialization.data(withJSONObject: passport.dataGroupsRead.mapValues { self.convertDataGroupToSerializableFormat($0) }, options: [])
// let dataGroupsReadJsonString = String(data: dataGroupsReadData, encoding: .utf8) ?? ""
// ret["dataGroupsRead"] = dataGroupsReadJsonString
// } catch {
// print("Error serializing dataGroupsRead: \(error)")
// }
// ret["dataGroupsRead"] = passport.dataGroupsRead.mapValues { convertDataGroupToSerializableFormat($0) }
do {
let dataGroupHashesDict = passport.dataGroupHashes.mapKeys { "\($0)" }
let serializableDataGroupHashes = dataGroupHashesDict.mapValues { convertDataGroupHashToSerializableFormat($0) }
let dataGroupHashesData = try JSONSerialization.data(withJSONObject: serializableDataGroupHashes, options: [])
let dataGroupHashesJsonString = String(data: dataGroupHashesData, encoding: .utf8) ?? ""
ret["dataGroupHashes"] = dataGroupHashesJsonString
} catch {
print("Error serializing dataGroupHashes: \(error)")
}
// cardAccess
// BACStatus
// PACEStatus
// chipAuthenticationStatus
ret["passportCorrectlySigned"] = String(passport.passportCorrectlySigned)
ret["documentSigningCertificateVerified"] = String(passport.documentSigningCertificateVerified)
ret["passportDataNotTampered"] = String(passport.passportDataNotTampered)
ret["activeAuthenticationPassed"] = String(passport.activeAuthenticationPassed)
ret["activeAuthenticationChallenge"] = encodeByteArrayToHexString(passport.activeAuthenticationChallenge)
ret["activeAuthenticationSignature"] = encodeByteArrayToHexString(passport.activeAuthenticationSignature)
ret["verificationErrors"] = encodeErrors(passport.verificationErrors).joined(separator: ", ")
ret["isPACESupported"] = String(passport.isPACESupported)
ret["isChipAuthenticationSupported"] = String(passport.isChipAuthenticationSupported)
// passportImage
// signatureImage
// activeAuthenticationSupported
print("passport.certificateSigningGroups", passport.certificateSigningGroups)
// ret["certificateSigningGroups"] = passport.certificateSigningGroups.mapKeys(certificateTypeToString).mapValues(encodeX509WrapperToJsonString)
// if let passportDataElements = passport.passportDataElements {
// ret["passportDataElements"] = passportDataElements
// } else {
// ret["passportDataElements"] = [:]
// }
do {
let sod = passport.getDataGroup(DataGroupId.SOD) as! SOD
// ret["concatenatedDataHashes"] = try sod.getEncapsulatedContent().base64EncodedString() // this is what we call concatenatedDataHashes, not the true eContent
ret["eContentBase64"] = try sod.getEncapsulatedContent().base64EncodedString() // this is what we call concatenatedDataHashes, not the true eContent
ret["signatureAlgorithm"] = try sod.getSignatureAlgorithm()
let messageDigestFromSignedAttributes = try sod.getMessageDigestFromSignedAttributes()
let signedAttributes = try sod.getSignedAttributes()
print("messageDigestFromSignedAttributes", messageDigestFromSignedAttributes)
ret["signedAttributes"] = signedAttributes.base64EncodedString()
// if let pubKey = convertOpaquePointerToSecKey(opaquePointer: sod.pubKey),
// let serializedPublicKey = serializePublicKey(pubKey) {
// ret["publicKeyBase64"] = serializedPublicKey
// } else {
// // Handle the case where pubKey is nil
// }
if let serializedSignature = serializeSignature(from: sod) {
ret["signatureBase64"] = serializedSignature
}
} catch {
print("Error serializing SOD data: \(error)")
reject("E_PASSPORT_READ", "Failed to read passport", error)
}
let stringified = String(data: try JSONEncoder().encode(ret), encoding: .utf8)
resolve(stringified)
} catch {
reject("E_PASSPORT_READ", "Failed to read passport", error)
}
}
}
// mrz
// dataHashes
// eContentBytes
// pubkey
// signature
// func convertOpaquePointerToSecKey(opaquePointer: OpaquePointer?) -> SecKey? {
// guard let opaquePointer = opaquePointer else { return nil }
// // Assuming the key is in DER format
// // Replace with actual code to convert OpaquePointer to Data
// let keyData = Data(bytes: opaquePointer, count: keyLength) // Replace `keyLength` with actual length of key data
// let attributes: [String: Any] = [
// kSecAttrKeyType as String: kSecAttrKeyTypeRSA, // or kSecAttrKeyTypeECSECPrimeRandom for ECDSA
// kSecAttrKeyClass as String: kSecAttrKeyClassPublic
// ]
// var error: Unmanaged<CFError>?
// let secKey = SecKeyCreateWithData(keyData as CFData, attributes as CFDictionary, &error)
// if let error = error {
// print("Error creating SecKey: \(error.takeRetainedValue())")
// return nil
// }
// return secKey
// }
func serializePublicKey(_ publicKey: SecKey) -> String? {
var error: Unmanaged<CFError>?
guard let publicKeyData = SecKeyCopyExternalRepresentation(publicKey, &error) as Data? else {
print("Error serializing public key: \(error!.takeRetainedValue() as Error)")
return nil
}
return publicKeyData.base64EncodedString()
}
func serializeSignature(from sod: SOD) -> String? {
do {
let signature = try sod.getSignature()
return signature.base64EncodedString()
} catch {
print("Error extracting signature: \(error)")
return nil
}
}
func serializeX509Wrapper(_ certificate: X509Wrapper?) -> String? {
guard let certificate = certificate else { return nil }
let itemsDict = certificate.getItemsAsDict()
var certInfoStringKeys = [String: String]()
// Convert CertificateItem keys to String keys
for (key, value) in itemsDict {
certInfoStringKeys[key.rawValue] = value
}
// Add PEM representation
let certPEM = certificate.certToPEM()
certInfoStringKeys["PEM"] = certPEM
do {
let jsonData = try JSONSerialization.data(withJSONObject: certInfoStringKeys, options: [])
return String(data: jsonData, encoding: .utf8)
} catch {
print("Error serializing X509Wrapper: \(error)")
return nil
}
}
// @objc(scanPassport:dateOfBirth:dateOfExpiry:resolve:reject:)
// func scanPassport(_ passportNumber: String, dateOfBirth: String, dateOfExpiry: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
// let concatenatedString = "\(passportNumber), \(dateOfBirth), \(dateOfExpiry)"
// resolve(concatenatedString)
// }
func encodeX509WrapperToJsonString(_ certificate: X509Wrapper?) -> String? {
guard let certificate = certificate else { return nil }
let certificateItems = certificate.getItemsAsDict()
// Convert certificate items to JSON
do {
let jsonData = try JSONSerialization.data(withJSONObject: certificateItems, options: [])
return String(data: jsonData, encoding: .utf8)
} catch {
print("Error serializing certificate items to JSON: \(error)")
return nil
}
}
func encodeByteArrayToHexString(_ byteArray: [UInt8]) -> String {
return byteArray.map { String(format: "%02x", $0) }.joined()
}
func encodeErrors(_ errors: [Error]) -> [String] {
return errors.map { $0.localizedDescription }
}
func convertDataGroupHashToSerializableFormat(_ dataGroupHash: DataGroupHash) -> [String: Any] {
return [
"id": dataGroupHash.id,
"sodHash": dataGroupHash.sodHash,
"computedHash": dataGroupHash.computedHash,
"match": dataGroupHash.match
]
}
func dataGroupIdToString(_ id: DataGroupId) -> String {
return String(id.rawValue) // or any other method to get a string representation
}
func certificateTypeToString(_ type: CertificateType) -> String {
return type.stringValue()
}
func convertDataGroupToSerializableFormat(_ dataGroup: DataGroup) -> [String: Any] {
return [
"datagroupType": dataGroupIdToString(dataGroup.datagroupType),
"body": encodeByteArrayToHexString(dataGroup.body),
"data": encodeByteArrayToHexString(dataGroup.data)
]
}
@objc
static func requiresMainQueueSetup() -> Bool {
return true

View File

@@ -1,3 +1,5 @@
use_frameworks!
# Resolve react_native_pods.rb with node to allow for hoisting
require Pod::Executable.execute_command('node', ['-p',
'require.resolve(
@@ -5,7 +7,7 @@ require Pod::Executable.execute_command('node', ['-p',
{paths: [process.argv[1]]},
)', __dir__]).strip
platform :ios, min_ios_version_supported
platform :ios, '13.0'
prepare_react_native_project!
# If you are using a `react-native-flipper` your iOS build will fail when `NO_FLIPPER=1` is set.
@@ -25,14 +27,15 @@ if linkage != nil
use_frameworks! :linkage => linkage.to_sym
end
target 'AwesomeProject' do
target 'ProofOfPassport' do
config = use_native_modules!
# Flags change depending on the env values.
flags = get_default_flags()
use_frameworks!
pod 'NFCPassportReader', git:'https://github.com/yssf-io/NFCPassportReader.git'
pod 'NFCPassportReader', git: 'https://github.com/0xturboblitz/NFCPassportReader.git', commit: 'd36952eeaa2025ff1a9c9abbc244bd5ff53eb0f9'
pod 'MoproKit', :path => './MoproKit'
use_react_native!(
:path => config[:reactNativePath],
@@ -48,12 +51,20 @@ target 'AwesomeProject' do
:app_path => "#{Pod::Config.instance.installation_root}/.."
)
target 'AwesomeProjectTests' do
target 'ProofOfPassportTests' do
inherit! :complete
# Pods for testing
end
post_install do |installer|
installer.generated_projects.each do |project|
project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '13.0'
end
end
end
# https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202
react_native_post_install(
installer,

View File

@@ -11,7 +11,8 @@ PODS:
- ReactCommon/turbomodule/core (= 0.72.3)
- fmt (6.2.1)
- glog (0.3.5)
- NFCPassportReader (2.0.2):
- MoproKit (0.1.0)
- NFCPassportReader (2.0.3):
- OpenSSL-Universal (= 1.1.1100)
- OpenSSL-Universal (1.1.1100)
- RCT-Folly (2021.07.22.00):
@@ -390,7 +391,7 @@ PODS:
- React-jsi (= 0.72.3)
- React-logger (= 0.72.3)
- React-perflogger (= 0.72.3)
- RNFS (2.20.0):
- RNSVG (13.4.0):
- React-Core
- SocketRocket (0.6.1)
- Yoga (1.14.0)
@@ -401,7 +402,8 @@ DEPENDENCIES:
- FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`)
- FBReactNativeSpec (from `../node_modules/react-native/React/FBReactNativeSpec`)
- glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
- NFCPassportReader (from `https://github.com/yssf-io/NFCPassportReader.git`)
- MoproKit (from `./MoproKit`)
- NFCPassportReader (from `https://github.com/0xturboblitz/NFCPassportReader.git`, commit `d36952eeaa2025ff1a9c9abbc244bd5ff53eb0f9`)
- RCT-Folly (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`)
- RCTRequired (from `../node_modules/react-native/Libraries/RCTRequired`)
- RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`)
@@ -435,7 +437,7 @@ DEPENDENCIES:
- React-runtimescheduler (from `../node_modules/react-native/ReactCommon/react/renderer/runtimescheduler`)
- React-utils (from `../node_modules/react-native/ReactCommon/react/utils`)
- ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`)
- RNFS (from `../node_modules/react-native-fs`)
- RNSVG (from `../node_modules/react-native-svg`)
- Yoga (from `../node_modules/react-native/ReactCommon/yoga`)
SPEC REPOS:
@@ -455,8 +457,11 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/React/FBReactNativeSpec"
glog:
:podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec"
MoproKit:
:path: "./MoproKit"
NFCPassportReader:
:git: https://github.com/yssf-io/NFCPassportReader.git
:commit: d36952eeaa2025ff1a9c9abbc244bd5ff53eb0f9
:git: https://github.com/0xturboblitz/NFCPassportReader.git
RCT-Folly:
:podspec: "../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec"
RCTRequired:
@@ -521,15 +526,15 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/ReactCommon/react/utils"
ReactCommon:
:path: "../node_modules/react-native/ReactCommon"
RNFS:
:path: "../node_modules/react-native-fs"
RNSVG:
:path: "../node_modules/react-native-svg"
Yoga:
:path: "../node_modules/react-native/ReactCommon/yoga"
CHECKOUT OPTIONS:
NFCPassportReader:
:commit: 08f9874f1e3287c9a726225e1e9193ef2b194d93
:git: https://github.com/yssf-io/NFCPassportReader.git
:commit: d36952eeaa2025ff1a9c9abbc244bd5ff53eb0f9
:git: https://github.com/0xturboblitz/NFCPassportReader.git
SPEC CHECKSUMS:
boost: 57d2868c099736d80fcd648bf211b4431e51a558
@@ -538,7 +543,8 @@ SPEC CHECKSUMS:
FBReactNativeSpec: c6bd9e179757b3c0ecf815864fae8032377903ef
fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9
glog: 04b94705f318337d7ead9e6d17c019bd9b1f6b1b
NFCPassportReader: 09906a0f1940f09eea79c141cb5b53a19aa74bab
MoproKit: d1faf8f9495e8e84d085f6c4e57e36f951e6f07e
NFCPassportReader: a160b80e3df3b5325c13902f90405f5eef7520b3
OpenSSL-Universal: ebc357f1e6bc71fa463ccb2fe676756aff50e88c
RCT-Folly: 424b8c9a7a0b9ab2886ffe9c3b041ef628fd4fb1
RCTRequired: a2faf4bad4e438ca37b2040cb8f7799baa065c18
@@ -549,13 +555,13 @@ SPEC CHECKSUMS:
React-Core: 8293312ad137ea82fd2c29deb163dbc24aa4e00e
React-CoreModules: 32fab1d62416849a3b6dac6feff9d54e5ddc2d1e
React-cxxreact: 55d0f7cb6b4cc09ba9190797f1da87182d1a2fb6
React-debug: 6c3d27a8aef6df933dff63b0f31fdcf2ad437718
React-debug: 7e61555c8158126c6cd98c3154381ad3821aaaca
React-jsc: 0db8e8cc2074d979c37ffa7b8d7c914833960497
React-jsi: 58677ff4848ceb6aeb9118fe03448a843ea5e16a
React-jsiexecutor: 2c15ba1bace70177492368d5180b564f165870fd
React-jsinspector: b511447170f561157547bc0bef3f169663860be7
React-logger: c5b527272d5f22eaa09bb3c3a690fee8f237ae95
React-NativeModulesApple: a1c74fa3d7f0b1f93bbd4102061a93abfc8a739a
React-NativeModulesApple: 0438665fc7473be6edc496e823e6ea0b0537b46c
React-perflogger: 6bd153e776e6beed54c56b0847e1220a3ff92ba5
React-RCTActionSheet: c0b62af44e610e69d9a2049a682f5dba4e9dff17
React-RCTAnimation: fe7005136b58f58871cab2f70732343b6e330d30
@@ -569,13 +575,13 @@ SPEC CHECKSUMS:
React-RCTVibration: ea3a68a49873a54ced927c90923fc6932baf344a
React-rncore: 9672a017af4a7da7495d911f0b690cbcae9dd18d
React-runtimeexecutor: 369ae9bb3f83b65201c0c8f7d50b72280b5a1dbc
React-runtimescheduler: 5c392d612fffa213b673e651f886ead35995e262
React-utils: 2589af83a714e02813d10a4c5fc064a6832c006b
ReactCommon: a34545a02528203cee0839eeb01d754e8c4ad948
RNFS: 4ac0f0ea233904cb798630b3c077808c06931688
React-runtimescheduler: ec1066a4f2d1152eb1bc3fb61d69376b3bc0dde0
React-utils: d55ba834beb39f01b0b470ae43478c0a3a024abe
ReactCommon: 68e3a815fbb69af3bb4196e04c6ae7abb306e7a8
RNSVG: 07dbd870b0dcdecc99b3a202fa37c8ca163caec2
SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17
Yoga: 8796b55dba14d7004f980b54bcc9833ee45b28ce
PODFILE CHECKSUM: ef3dd3fed039eab5043d469e8f98dbcd0de8a7d5
PODFILE CHECKSUM: d401e6efe0c933985349c8c115c7edca8fef3182
COCOAPODS: 1.12.1
COCOAPODS: 1.14.3

View File

@@ -7,16 +7,21 @@
objects = {
/* Begin PBXBuildFile section */
00E356F31AD99517003FC87E /* AwesomeProjectTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* AwesomeProjectTests.m */; };
00E356F31AD99517003FC87E /* ProofOfPassportTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* ProofOfPassportTests.m */; };
057DFC5F2B56DC0D003D24A3 /* libmopro_ffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 057DFC5E2B56DC0D003D24A3 /* libmopro_ffi.a */; };
05BD9DCC2B548AA900823023 /* MoproKit in Resources */ = {isa = PBXBuildFile; fileRef = 05BD9DCB2B548AA900823023 /* MoproKit */; };
05BD9DCE2B554FA300823023 /* libmopro_ffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 05BD9DCD2B554FA300823023 /* libmopro_ffi.a */; };
05EDEDC62B52D25D00AA51AD /* Prover.m in Sources */ = {isa = PBXBuildFile; fileRef = 05EDEDC42B52D25D00AA51AD /* Prover.m */; };
05EDEDC72B52D25D00AA51AD /* Prover.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05EDEDC52B52D25D00AA51AD /* Prover.swift */; };
0651723A94C70A2B31E3E4F8 /* Pods_ProofOfPassport.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAAF621B99F62C9ED35AA07 /* Pods_ProofOfPassport.framework */; };
13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; };
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
1ADA5CBFFFB043C12B3C4011 /* Pods_AwesomeProject.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9F81894AE401F61E2393104D /* Pods_AwesomeProject.framework */; };
75E785E6A486EA107852C8A6 /* Pods_ProofOfPassport_ProofOfPassportTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CFAE0EE7E1942128592D0CC4 /* Pods_ProofOfPassport_ProofOfPassportTests.framework */; };
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
905B70052A72767900AFA232 /* PassportReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 905B70042A72767900AFA232 /* PassportReader.swift */; };
905B70072A72774000AFA232 /* PassportReader.m in Sources */ = {isa = PBXBuildFile; fileRef = 905B70062A72774000AFA232 /* PassportReader.m */; };
905B700B2A72A5E900AFA232 /* masterList.pem in Resources */ = {isa = PBXBuildFile; fileRef = 905B700A2A72A5E900AFA232 /* masterList.pem */; };
D5B4CD952178B51C14F77614 /* Pods_AwesomeProject_AwesomeProjectTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 455B3F561C1D3D833AB15B26 /* Pods_AwesomeProject_AwesomeProjectTests.framework */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -25,32 +30,38 @@
containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 13B07F861A680F5B00A75B9A;
remoteInfo = AwesomeProject;
remoteInfo = ProofOfPassport;
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
00E356EE1AD99517003FC87E /* AwesomeProjectTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AwesomeProjectTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
00E356EE1AD99517003FC87E /* ProofOfPassportTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ProofOfPassportTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
00E356F21AD99517003FC87E /* AwesomeProjectTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AwesomeProjectTests.m; sourceTree = "<group>"; };
0D05C085F8CBAC104F72E160 /* Pods-AwesomeProject-AwesomeProjectTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AwesomeProject-AwesomeProjectTests.release.xcconfig"; path = "Target Support Files/Pods-AwesomeProject-AwesomeProjectTests/Pods-AwesomeProject-AwesomeProjectTests.release.xcconfig"; sourceTree = "<group>"; };
13B07F961A680F5B00A75B9A /* AwesomeProject.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AwesomeProject.app; sourceTree = BUILT_PRODUCTS_DIR; };
13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = AwesomeProject/AppDelegate.h; sourceTree = "<group>"; };
13B07FB01A68108700A75B9A /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AppDelegate.mm; path = AwesomeProject/AppDelegate.mm; sourceTree = "<group>"; };
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = AwesomeProject/Images.xcassets; sourceTree = "<group>"; };
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = AwesomeProject/Info.plist; sourceTree = "<group>"; };
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = AwesomeProject/main.m; sourceTree = "<group>"; };
26A432E0434B89485C7E3765 /* Pods-AwesomeProject.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AwesomeProject.debug.xcconfig"; path = "Target Support Files/Pods-AwesomeProject/Pods-AwesomeProject.debug.xcconfig"; sourceTree = "<group>"; };
455B3F561C1D3D833AB15B26 /* Pods_AwesomeProject_AwesomeProjectTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AwesomeProject_AwesomeProjectTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = AwesomeProject/LaunchScreen.storyboard; sourceTree = "<group>"; };
905B70032A72767800AFA232 /* AwesomeProject-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "AwesomeProject-Bridging-Header.h"; sourceTree = "<group>"; };
00E356F21AD99517003FC87E /* ProofOfPassportTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ProofOfPassportTests.m; sourceTree = "<group>"; };
057DFC5E2B56DC0D003D24A3 /* libmopro_ffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libmopro_ffi.a; path = MoproKit/Libs/libmopro_ffi.a; sourceTree = "<group>"; };
05A0773D2B5333CE0037E489 /* MoproKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = MoproKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
05BD9DCB2B548AA900823023 /* MoproKit */ = {isa = PBXFileReference; lastKnownFileType = folder; path = MoproKit; sourceTree = "<group>"; };
05BD9DCD2B554FA300823023 /* libmopro_ffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libmopro_ffi.a; path = MoproKit/Libs/libmopro_ffi.a; sourceTree = "<group>"; };
05EDEDC42B52D25D00AA51AD /* Prover.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Prover.m; sourceTree = "<group>"; };
05EDEDC52B52D25D00AA51AD /* Prover.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Prover.swift; sourceTree = "<group>"; };
13B07F961A680F5B00A75B9A /* ProofOfPassport.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ProofOfPassport.app; sourceTree = BUILT_PRODUCTS_DIR; };
13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = ProofOfPassport/AppDelegate.h; sourceTree = "<group>"; };
13B07FB01A68108700A75B9A /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AppDelegate.mm; path = ProofOfPassport/AppDelegate.mm; sourceTree = "<group>"; };
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = ProofOfPassport/Images.xcassets; sourceTree = "<group>"; };
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = ProofOfPassport/Info.plist; sourceTree = "<group>"; };
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = ProofOfPassport/main.m; sourceTree = "<group>"; };
22FDF2ADA5789E09558ADB4E /* Pods-ProofOfPassport-ProofOfPassportTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ProofOfPassport-ProofOfPassportTests.release.xcconfig"; path = "Target Support Files/Pods-ProofOfPassport-ProofOfPassportTests/Pods-ProofOfPassport-ProofOfPassportTests.release.xcconfig"; sourceTree = "<group>"; };
2B01EC4981C171CA304E6D2B /* Pods-ProofOfPassport.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ProofOfPassport.release.xcconfig"; path = "Target Support Files/Pods-ProofOfPassport/Pods-ProofOfPassport.release.xcconfig"; sourceTree = "<group>"; };
3DAAF621B99F62C9ED35AA07 /* Pods_ProofOfPassport.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ProofOfPassport.framework; sourceTree = BUILT_PRODUCTS_DIR; };
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = ProofOfPassport/LaunchScreen.storyboard; sourceTree = "<group>"; };
905B70032A72767800AFA232 /* ProofOfPassport-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ProofOfPassport-Bridging-Header.h"; sourceTree = "<group>"; };
905B70042A72767900AFA232 /* PassportReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PassportReader.swift; sourceTree = "<group>"; };
905B70062A72774000AFA232 /* PassportReader.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PassportReader.m; sourceTree = "<group>"; };
905B70082A729CD400AFA232 /* AwesomeProject.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = AwesomeProject.entitlements; path = AwesomeProject/AwesomeProject.entitlements; sourceTree = "<group>"; };
905B700A2A72A5E900AFA232 /* masterList.pem */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = masterList.pem; path = AwesomeProject/masterList.pem; sourceTree = "<group>"; };
9F81894AE401F61E2393104D /* Pods_AwesomeProject.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AwesomeProject.framework; sourceTree = BUILT_PRODUCTS_DIR; };
A1B7A34F7E4C3F2D19C5D973 /* Pods-AwesomeProject.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AwesomeProject.release.xcconfig"; path = "Target Support Files/Pods-AwesomeProject/Pods-AwesomeProject.release.xcconfig"; sourceTree = "<group>"; };
BCC787E0FDFB928F9DA23E3F /* Pods-AwesomeProject-AwesomeProjectTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AwesomeProject-AwesomeProjectTests.debug.xcconfig"; path = "Target Support Files/Pods-AwesomeProject-AwesomeProjectTests/Pods-AwesomeProject-AwesomeProjectTests.debug.xcconfig"; sourceTree = "<group>"; };
905B70082A729CD400AFA232 /* ProofOfPassport.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = ProofOfPassport.entitlements; path = ProofOfPassport/ProofOfPassport.entitlements; sourceTree = "<group>"; };
905B700A2A72A5E900AFA232 /* masterList.pem */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = masterList.pem; path = ProofOfPassport/masterList.pem; sourceTree = "<group>"; };
918081ECA23C8F232594E334 /* Pods-ProofOfPassport-ProofOfPassportTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ProofOfPassport-ProofOfPassportTests.debug.xcconfig"; path = "Target Support Files/Pods-ProofOfPassport-ProofOfPassportTests/Pods-ProofOfPassport-ProofOfPassportTests.debug.xcconfig"; sourceTree = "<group>"; };
CE0B085EC65BAFEB61DD9C49 /* Pods-ProofOfPassport.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ProofOfPassport.debug.xcconfig"; path = "Target Support Files/Pods-ProofOfPassport/Pods-ProofOfPassport.debug.xcconfig"; sourceTree = "<group>"; };
CFAE0EE7E1942128592D0CC4 /* Pods_ProofOfPassport_ProofOfPassportTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ProofOfPassport_ProofOfPassportTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
/* End PBXFileReference section */
@@ -59,7 +70,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
D5B4CD952178B51C14F77614 /* Pods_AwesomeProject_AwesomeProjectTests.framework in Frameworks */,
75E785E6A486EA107852C8A6 /* Pods_ProofOfPassport_ProofOfPassportTests.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -67,20 +78,22 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
1ADA5CBFFFB043C12B3C4011 /* Pods_AwesomeProject.framework in Frameworks */,
0651723A94C70A2B31E3E4F8 /* Pods_ProofOfPassport.framework in Frameworks */,
05BD9DCE2B554FA300823023 /* libmopro_ffi.a in Frameworks */,
057DFC5F2B56DC0D003D24A3 /* libmopro_ffi.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
00E356EF1AD99517003FC87E /* AwesomeProjectTests */ = {
00E356EF1AD99517003FC87E /* ProofOfPassportTests */ = {
isa = PBXGroup;
children = (
00E356F21AD99517003FC87E /* AwesomeProjectTests.m */,
00E356F21AD99517003FC87E /* ProofOfPassportTests.m */,
00E356F01AD99517003FC87E /* Supporting Files */,
);
path = AwesomeProjectTests;
path = ProofOfPassportTests;
sourceTree = "<group>";
};
00E356F01AD99517003FC87E /* Supporting Files */ = {
@@ -91,30 +104,36 @@
name = "Supporting Files";
sourceTree = "<group>";
};
13B07FAE1A68108700A75B9A /* AwesomeProject */ = {
13B07FAE1A68108700A75B9A /* ProofOfPassport */ = {
isa = PBXGroup;
children = (
05EDEDC42B52D25D00AA51AD /* Prover.m */,
05EDEDC52B52D25D00AA51AD /* Prover.swift */,
905B700A2A72A5E900AFA232 /* masterList.pem */,
905B70082A729CD400AFA232 /* AwesomeProject.entitlements */,
905B70082A729CD400AFA232 /* ProofOfPassport.entitlements */,
057DFC5E2B56DC0D003D24A3 /* libmopro_ffi.a */,
13B07FAF1A68108700A75B9A /* AppDelegate.h */,
05BD9DCB2B548AA900823023 /* MoproKit */,
13B07FB01A68108700A75B9A /* AppDelegate.mm */,
13B07FB51A68108700A75B9A /* Images.xcassets */,
13B07FB61A68108700A75B9A /* Info.plist */,
05BD9DCD2B554FA300823023 /* libmopro_ffi.a */,
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */,
13B07FB71A68108700A75B9A /* main.m */,
905B70042A72767900AFA232 /* PassportReader.swift */,
905B70032A72767800AFA232 /* AwesomeProject-Bridging-Header.h */,
905B70032A72767800AFA232 /* ProofOfPassport-Bridging-Header.h */,
905B70062A72774000AFA232 /* PassportReader.m */,
);
name = AwesomeProject;
name = ProofOfPassport;
sourceTree = "<group>";
};
2D16E6871FA4F8E400B85C8A /* Frameworks */ = {
isa = PBXGroup;
children = (
05A0773D2B5333CE0037E489 /* MoproKit.framework */,
ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
9F81894AE401F61E2393104D /* Pods_AwesomeProject.framework */,
455B3F561C1D3D833AB15B26 /* Pods_AwesomeProject_AwesomeProjectTests.framework */,
3DAAF621B99F62C9ED35AA07 /* Pods_ProofOfPassport.framework */,
CFAE0EE7E1942128592D0CC4 /* Pods_ProofOfPassport_ProofOfPassportTests.framework */,
);
name = Frameworks;
sourceTree = "<group>";
@@ -129,9 +148,9 @@
83CBB9F61A601CBA00E9B192 = {
isa = PBXGroup;
children = (
13B07FAE1A68108700A75B9A /* AwesomeProject */,
13B07FAE1A68108700A75B9A /* ProofOfPassport */,
832341AE1AAA6A7D00B99B32 /* Libraries */,
00E356EF1AD99517003FC87E /* AwesomeProjectTests */,
00E356EF1AD99517003FC87E /* ProofOfPassportTests */,
83CBBA001A601CBA00E9B192 /* Products */,
2D16E6871FA4F8E400B85C8A /* Frameworks */,
BBD78D7AC51CEA395F1C20DB /* Pods */,
@@ -144,8 +163,8 @@
83CBBA001A601CBA00E9B192 /* Products */ = {
isa = PBXGroup;
children = (
13B07F961A680F5B00A75B9A /* AwesomeProject.app */,
00E356EE1AD99517003FC87E /* AwesomeProjectTests.xctest */,
13B07F961A680F5B00A75B9A /* ProofOfPassport.app */,
00E356EE1AD99517003FC87E /* ProofOfPassportTests.xctest */,
);
name = Products;
sourceTree = "<group>";
@@ -153,10 +172,10 @@
BBD78D7AC51CEA395F1C20DB /* Pods */ = {
isa = PBXGroup;
children = (
26A432E0434B89485C7E3765 /* Pods-AwesomeProject.debug.xcconfig */,
A1B7A34F7E4C3F2D19C5D973 /* Pods-AwesomeProject.release.xcconfig */,
BCC787E0FDFB928F9DA23E3F /* Pods-AwesomeProject-AwesomeProjectTests.debug.xcconfig */,
0D05C085F8CBAC104F72E160 /* Pods-AwesomeProject-AwesomeProjectTests.release.xcconfig */,
CE0B085EC65BAFEB61DD9C49 /* Pods-ProofOfPassport.debug.xcconfig */,
2B01EC4981C171CA304E6D2B /* Pods-ProofOfPassport.release.xcconfig */,
918081ECA23C8F232594E334 /* Pods-ProofOfPassport-ProofOfPassportTests.debug.xcconfig */,
22FDF2ADA5789E09558ADB4E /* Pods-ProofOfPassport-ProofOfPassportTests.release.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
@@ -164,45 +183,45 @@
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
00E356ED1AD99517003FC87E /* AwesomeProjectTests */ = {
00E356ED1AD99517003FC87E /* ProofOfPassportTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "AwesomeProjectTests" */;
buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "ProofOfPassportTests" */;
buildPhases = (
2CEEDB4F696405D4E4B0CA04 /* [CP] Check Pods Manifest.lock */,
30EF76FEB71F2239D12E988C /* [CP] Check Pods Manifest.lock */,
00E356EA1AD99517003FC87E /* Sources */,
00E356EB1AD99517003FC87E /* Frameworks */,
00E356EC1AD99517003FC87E /* Resources */,
4786C99960C198CBC0841652 /* [CP] Embed Pods Frameworks */,
3407A7677F910117EC6ADA91 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
dependencies = (
00E356F51AD99517003FC87E /* PBXTargetDependency */,
);
name = AwesomeProjectTests;
productName = AwesomeProjectTests;
productReference = 00E356EE1AD99517003FC87E /* AwesomeProjectTests.xctest */;
name = ProofOfPassportTests;
productName = ProofOfPassportTests;
productReference = 00E356EE1AD99517003FC87E /* ProofOfPassportTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
13B07F861A680F5B00A75B9A /* AwesomeProject */ = {
13B07F861A680F5B00A75B9A /* ProofOfPassport */ = {
isa = PBXNativeTarget;
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "AwesomeProject" */;
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "ProofOfPassport" */;
buildPhases = (
141C3B9CB8F4134662795CF4 /* [CP] Check Pods Manifest.lock */,
CC47E87AFD57D7866D1463AC /* [CP] Check Pods Manifest.lock */,
FD10A7F022414F080027D42C /* Start Packager */,
13B07F871A680F5B00A75B9A /* Sources */,
13B07F8C1A680F5B00A75B9A /* Frameworks */,
13B07F8E1A680F5B00A75B9A /* Resources */,
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
FAF2988CBB80C1B370711DF7 /* [CP] Embed Pods Frameworks */,
A8CC45FE941CED993895A21C /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = AwesomeProject;
productName = AwesomeProject;
productReference = 13B07F961A680F5B00A75B9A /* AwesomeProject.app */;
name = ProofOfPassport;
productName = ProofOfPassport;
productReference = 13B07F961A680F5B00A75B9A /* ProofOfPassport.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
@@ -222,7 +241,7 @@
};
};
};
buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "AwesomeProject" */;
buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "ProofOfPassport" */;
compatibilityVersion = "Xcode 12.0";
developmentRegion = en;
hasScannedForEncodings = 0;
@@ -235,8 +254,8 @@
projectDirPath = "";
projectRoot = "";
targets = (
13B07F861A680F5B00A75B9A /* AwesomeProject */,
00E356ED1AD99517003FC87E /* AwesomeProjectTests */,
13B07F861A680F5B00A75B9A /* ProofOfPassport */,
00E356ED1AD99517003FC87E /* ProofOfPassportTests */,
);
};
/* End PBXProject section */
@@ -254,6 +273,7 @@
buildActionMask = 2147483647;
files = (
905B700B2A72A5E900AFA232 /* masterList.pem in Resources */,
05BD9DCC2B548AA900823023 /* MoproKit in Resources */,
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */,
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
);
@@ -278,7 +298,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";
};
141C3B9CB8F4134662795CF4 /* [CP] Check Pods Manifest.lock */ = {
30EF76FEB71F2239D12E988C /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@@ -293,14 +313,48 @@
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-AwesomeProject-checkManifestLockResult.txt",
"$(DERIVED_FILE_DIR)/Pods-ProofOfPassport-ProofOfPassportTests-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
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;
};
2CEEDB4F696405D4E4B0CA04 /* [CP] Check Pods Manifest.lock */ = {
3407A7677F910117EC6ADA91 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-ProofOfPassport-ProofOfPassportTests/Pods-ProofOfPassport-ProofOfPassportTests-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-ProofOfPassport-ProofOfPassportTests/Pods-ProofOfPassport-ProofOfPassportTests-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ProofOfPassport-ProofOfPassportTests/Pods-ProofOfPassport-ProofOfPassportTests-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
A8CC45FE941CED993895A21C /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-ProofOfPassport/Pods-ProofOfPassport-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-ProofOfPassport/Pods-ProofOfPassport-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ProofOfPassport/Pods-ProofOfPassport-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
CC47E87AFD57D7866D1463AC /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@@ -315,47 +369,13 @@
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-AwesomeProject-AwesomeProjectTests-checkManifestLockResult.txt",
"$(DERIVED_FILE_DIR)/Pods-ProofOfPassport-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
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;
};
4786C99960C198CBC0841652 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-AwesomeProject-AwesomeProjectTests/Pods-AwesomeProject-AwesomeProjectTests-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-AwesomeProject-AwesomeProjectTests/Pods-AwesomeProject-AwesomeProjectTests-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-AwesomeProject-AwesomeProjectTests/Pods-AwesomeProject-AwesomeProjectTests-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
FAF2988CBB80C1B370711DF7 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-AwesomeProject/Pods-AwesomeProject-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-AwesomeProject/Pods-AwesomeProject-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-AwesomeProject/Pods-AwesomeProject-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
FD10A7F022414F080027D42C /* Start Packager */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@@ -382,7 +402,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
00E356F31AD99517003FC87E /* AwesomeProjectTests.m in Sources */,
00E356F31AD99517003FC87E /* ProofOfPassportTests.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -391,9 +411,11 @@
buildActionMask = 2147483647;
files = (
13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */,
05EDEDC72B52D25D00AA51AD /* Prover.swift in Sources */,
13B07FC11A68108700A75B9A /* main.m in Sources */,
905B70072A72774000AFA232 /* PassportReader.m in Sources */,
905B70052A72767900AFA232 /* PassportReader.swift in Sources */,
05EDEDC62B52D25D00AA51AD /* Prover.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -402,7 +424,7 @@
/* Begin PBXTargetDependency section */
00E356F51AD99517003FC87E /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 13B07F861A680F5B00A75B9A /* AwesomeProject */;
target = 13B07F861A680F5B00A75B9A /* ProofOfPassport */;
targetProxy = 00E356F41AD99517003FC87E /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
@@ -410,7 +432,7 @@
/* Begin XCBuildConfiguration section */
00E356F61AD99517003FC87E /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = BCC787E0FDFB928F9DA23E3F /* Pods-AwesomeProject-AwesomeProjectTests.debug.xcconfig */;
baseConfigurationReference = 918081ECA23C8F232594E334 /* Pods-ProofOfPassport-ProofOfPassportTests.debug.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
BUNDLE_LOADER = "$(TEST_HOST)";
@@ -418,7 +440,7 @@
"DEBUG=1",
"$(inherited)",
);
INFOPLIST_FILE = AwesomeProjectTests/Info.plist;
INFOPLIST_FILE = ProofOfPassportTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
@@ -432,18 +454,18 @@
);
PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AwesomeProject.app/AwesomeProject";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ProofOfPassport.app/ProofOfPassport";
};
name = Debug;
};
00E356F71AD99517003FC87E /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 0D05C085F8CBAC104F72E160 /* Pods-AwesomeProject-AwesomeProjectTests.release.xcconfig */;
baseConfigurationReference = 22FDF2ADA5789E09558ADB4E /* Pods-ProofOfPassport-ProofOfPassportTests.release.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
BUNDLE_LOADER = "$(TEST_HOST)";
COPY_PHASE_STRIP = NO;
INFOPLIST_FILE = AwesomeProjectTests/Info.plist;
INFOPLIST_FILE = ProofOfPassportTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
@@ -457,25 +479,32 @@
);
PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AwesomeProject.app/AwesomeProject";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ProofOfPassport.app/ProofOfPassport";
};
name = Release;
};
13B07F941A680F5B00A75B9A /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 26A432E0434B89485C7E3765 /* Pods-AwesomeProject.debug.xcconfig */;
baseConfigurationReference = CE0B085EC65BAFEB61DD9C49 /* Pods-ProofOfPassport.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = AwesomeProject/AwesomeProject.entitlements;
CODE_SIGN_ENTITLEMENTS = ProofOfPassport/ProofOfPassport.entitlements;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = G46U6J456T;
DEVELOPMENT_TEAM = 5B29R5LYHQ;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = AwesomeProject/Info.plist;
INFOPLIST_FILE = ProofOfPassport/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"$(PROJECT_DIR)/../../cargo/target/universal/release",
);
LIBRARY_SEARCH_PATHS = (
"$(SDKROOT)/usr/lib/swift",
"$(inherited)",
"$(PROJECT_DIR)",
"$(PROJECT_DIR)/MoproKit/Libs",
);
MARKETING_VERSION = 1.0;
OTHER_LDFLAGS = (
@@ -483,9 +512,8 @@
"-ObjC",
"-lc++",
);
PRODUCT_BUNDLE_IDENTIFIER = com.warroom.nfcios;
PRODUCT_NAME = AwesomeProject;
SWIFT_OBJC_BRIDGING_HEADER = "AwesomeProject-Bridging-Header.h";
PRODUCT_BUNDLE_IDENTIFIER = com.warroom.proofofpassport;
PRODUCT_NAME = ProofOfPassport;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
@@ -494,28 +522,33 @@
};
13B07F951A680F5B00A75B9A /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = A1B7A34F7E4C3F2D19C5D973 /* Pods-AwesomeProject.release.xcconfig */;
baseConfigurationReference = 2B01EC4981C171CA304E6D2B /* Pods-ProofOfPassport.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = AwesomeProject/AwesomeProject.entitlements;
CODE_SIGN_ENTITLEMENTS = ProofOfPassport/ProofOfPassport.entitlements;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = G46U6J456T;
INFOPLIST_FILE = AwesomeProject/Info.plist;
DEVELOPMENT_TEAM = 5B29R5LYHQ;
INFOPLIST_FILE = ProofOfPassport/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
LIBRARY_SEARCH_PATHS = (
"$(SDKROOT)/usr/lib/swift",
"$(inherited)",
"$(PROJECT_DIR)",
"$(PROJECT_DIR)/MoproKit/Libs",
);
MARKETING_VERSION = 1.0;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
"-lc++",
);
PRODUCT_BUNDLE_IDENTIFIER = com.warroom.nfcios;
PRODUCT_NAME = AwesomeProject;
SWIFT_OBJC_BRIDGING_HEADER = "AwesomeProject-Bridging-Header.h";
PRODUCT_BUNDLE_IDENTIFIER = com.warroom.proofofpassport;
PRODUCT_NAME = ProofOfPassport;
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
@@ -679,7 +712,7 @@
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "AwesomeProjectTests" */ = {
00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "ProofOfPassportTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
00E356F61AD99517003FC87E /* Debug */,
@@ -688,7 +721,7 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "AwesomeProject" */ = {
13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "ProofOfPassport" */ = {
isa = XCConfigurationList;
buildConfigurations = (
13B07F941A680F5B00A75B9A /* Debug */,
@@ -697,7 +730,7 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "AwesomeProject" */ = {
83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "ProofOfPassport" */ = {
isa = XCConfigurationList;
buildConfigurations = (
83CBBA201A601CBA00E9B192 /* Debug */,

View File

@@ -15,9 +15,9 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
BuildableName = "AwesomeProject.app"
BlueprintName = "AwesomeProject"
ReferencedContainer = "container:AwesomeProject.xcodeproj">
BuildableName = "ProofOfPassport.app"
BlueprintName = "ProofOfPassport"
ReferencedContainer = "container:ProofOfPassport.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
@@ -33,9 +33,9 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "00E356ED1AD99517003FC87E"
BuildableName = "AwesomeProjectTests.xctest"
BlueprintName = "AwesomeProjectTests"
ReferencedContainer = "container:AwesomeProject.xcodeproj">
BuildableName = "ProofOfPassportTests.xctest"
BlueprintName = "ProofOfPassportTests"
ReferencedContainer = "container:ProofOfPassport.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
@@ -55,9 +55,9 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
BuildableName = "AwesomeProject.app"
BlueprintName = "AwesomeProject"
ReferencedContainer = "container:AwesomeProject.xcodeproj">
BuildableName = "ProofOfPassport.app"
BlueprintName = "ProofOfPassport"
ReferencedContainer = "container:ProofOfPassport.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
@@ -72,9 +72,9 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
BuildableName = "AwesomeProject.app"
BlueprintName = "AwesomeProject"
ReferencedContainer = "container:AwesomeProject.xcodeproj">
BuildableName = "ProofOfPassport.app"
BlueprintName = "ProofOfPassport"
ReferencedContainer = "container:ProofOfPassport.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>

View File

@@ -2,7 +2,7 @@
<Workspace
version = "1.0">
<FileRef
location = "group:AwesomeProject.xcodeproj">
location = "group:ProofOfPassport.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">

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