mirror of
https://github.com/selfxyz/self.git
synced 2026-01-22 21:17:59 -05:00
merge origin main to fork UI
This commit is contained in:
3
.idea/.gitignore
generated
vendored
3
.idea/.gitignore
generated
vendored
@@ -1,3 +0,0 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
5
.idea/misc.xml
generated
5
.idea/misc.xml
generated
@@ -1,5 +0,0 @@
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
||||
8
.idea/modules.xml
generated
8
.idea/modules.xml
generated
@@ -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
9
.idea/mygit.iml
generated
@@ -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
7
.idea/vcs.xml
generated
@@ -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
64
.idea/workspace.xml
generated
Normal 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">{
|
||||
"keyToString": {
|
||||
"RunOnceActivity.OpenProjectViewOnStart": "true",
|
||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||
"RunOnceActivity.cidr.known.project.marker": "true",
|
||||
"cf.first.check.clang-format": "false",
|
||||
"cidr.known.project.marker": "true",
|
||||
"git-widget-placeholder": "master",
|
||||
"last_opened_file_path": "/Users/remicolin/Documents/Proof of passport/mygit"
|
||||
}
|
||||
}</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>
|
||||
23
README.md
23
README.md
@@ -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 ?
|
||||
|
||||

|
||||
|
||||
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!
|
||||
|
||||
236
app/App.tsx
236
app/App.tsx
@@ -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.`)
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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'
|
||||
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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. */
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ public class MainActivity extends ReactActivity {
|
||||
*/
|
||||
@Override
|
||||
protected String getMainComponentName() {
|
||||
return "Proof of Passport";
|
||||
return "ProofOfPassport";
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"name": "Proof of Passport",
|
||||
"name": "ProofOfPassport",
|
||||
"displayName": "Proof of Passport"
|
||||
}
|
||||
|
||||
@@ -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
14
app/ark-zkey/.gitignore
vendored
Normal 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
32
app/ark-zkey/Cargo.toml
Normal 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
52
app/ark-zkey/README.md
Normal 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**
|
||||
36
app/ark-zkey/src/bin/arkzkey_util.rs
Normal file
36
app/ark-zkey/src/bin/arkzkey_util.rs
Normal 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
247
app/ark-zkey/src/lib.rs
Normal 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
@@ -1 +1 @@
|
||||
{"ProofOfPassport":"0x7D459347d092D35f043f73021f06c19f834f8c3E","Groth16Verifier":"0x72d8Fb7Dd33c264b53D313ff3BF25D993c0AD050"}
|
||||
{"ProofOfPassport":"0x50c47Be849dFac1FCbEDa031425896AF49d9FDFC","Groth16Verifier":"0xD6F5CA79b90a9E20B38df773122B4f50a1fda317"}
|
||||
@@ -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 []
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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)" )
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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...])
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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)"
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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 ?? "" )
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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-----
|
||||
@@ -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
52
app/ios/MoproKit/.gitignore
vendored
Normal 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
|
||||
14
app/ios/MoproKit/.travis.yml
Normal file
14
app/ios/MoproKit/.travis.yml
Normal 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
|
||||
805
app/ios/MoproKit/Bindings/mopro.swift
Normal file
805
app/ios/MoproKit/Bindings/mopro.swift
Normal 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")
|
||||
}
|
||||
}
|
||||
670
app/ios/MoproKit/Example/MoproKit.xcodeproj/project.pbxproj
Normal file
670
app/ios/MoproKit/Example/MoproKit.xcodeproj/project.pbxproj
Normal 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 */;
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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)")
|
||||
}
|
||||
}
|
||||
}
|
||||
52
app/ios/MoproKit/Example/MoproKit/AppDelegate.swift
Normal file
52
app/ios/MoproKit/Example/MoproKit/AppDelegate.swift
Normal 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:.
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
30
app/ios/MoproKit/Example/MoproKit/Base.lproj/Main.storyboard
Normal file
30
app/ios/MoproKit/Example/MoproKit/Base.lproj/Main.storyboard
Normal 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>
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
39
app/ios/MoproKit/Example/MoproKit/Info.plist
Normal file
39
app/ios/MoproKit/Example/MoproKit/Info.plist
Normal 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>
|
||||
@@ -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
|
||||
}
|
||||
147
app/ios/MoproKit/Example/MoproKit/KeccakZkeyViewController.swift
Normal file
147
app/ios/MoproKit/Example/MoproKit/KeccakZkeyViewController.swift
Normal 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
|
||||
}
|
||||
}
|
||||
235
app/ios/MoproKit/Example/MoproKit/RSAViewController.swift
Normal file
235
app/ios/MoproKit/Example/MoproKit/RSAViewController.swift
Normal 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)")
|
||||
}
|
||||
}
|
||||
}
|
||||
100
app/ios/MoproKit/Example/MoproKit/ViewController.swift
Normal file
100
app/ios/MoproKit/Example/MoproKit/ViewController.swift
Normal 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)
|
||||
// ])
|
||||
24
app/ios/MoproKit/Example/Podfile
Normal file
24
app/ios/MoproKit/Example/Podfile
Normal 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
|
||||
109
app/ios/MoproKit/Example/Tests/CircomTests.swift
Normal file
109
app/ios/MoproKit/Example/Tests/CircomTests.swift
Normal 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
|
||||
}
|
||||
13
app/ios/MoproKit/Example/Tests/CircomUITests.swift
Normal file
13
app/ios/MoproKit/Example/Tests/CircomUITests.swift
Normal 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")
|
||||
}
|
||||
}
|
||||
238
app/ios/MoproKit/Include/moproFFI.h
Normal file
238
app/ios/MoproKit/Include/moproFFI.h
Normal 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
|
||||
|
||||
);
|
||||
|
||||
@@ -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.
|
||||
0
app/ios/MoproKit/Libs/.gitkeep
Normal file
0
app/ios/MoproKit/Libs/.gitkeep
Normal file
50
app/ios/MoproKit/MoproKit.podspec
Normal file
50
app/ios/MoproKit/MoproKit.podspec
Normal 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
|
||||
0
app/ios/MoproKit/MoproKit/Assets/.gitkeep
Normal file
0
app/ios/MoproKit/MoproKit/Assets/.gitkeep
Normal file
29
app/ios/MoproKit/README.md
Normal file
29
app/ios/MoproKit/README.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# MoproKit
|
||||
|
||||
[](https://travis-ci.org/1552237/MoproKit)
|
||||
[](https://cocoapods.org/pods/MoproKit)
|
||||
[](https://cocoapods.org/pods/MoproKit)
|
||||
[](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.
|
||||
6
app/ios/MoproKit/Resources/moproFFI.modulemap
Normal file
6
app/ios/MoproKit/Resources/moproFFI.modulemap
Normal 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 *
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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]
|
||||
}
|
||||
}
|
||||
@@ -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")
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 )
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
//
|
||||
// PassportReader.m
|
||||
// AwesomeProject
|
||||
// ProofOfPassport
|
||||
//
|
||||
// Created by Y E on 27/07/2023.
|
||||
//
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 */,
|
||||
@@ -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>
|
||||
@@ -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
Reference in New Issue
Block a user