Files
self/app/ios/Prover.swift
0xturboblitz 7b213f71f3 Integrate new two-step process and proving in mobile app
- still need to solve https call to merkle tree server
- not tested on android
2024-05-16 17:26:36 +09:00

183 lines
6.8 KiB
Swift

// Prover.swift
// ProofOfPassport
// Created by Florent on 13/01/2024.
import Foundation
import React
import Security
#if canImport(witnesscalc_register_sha256WithRSAEncryption_65537)
import witnesscalc_register_sha256WithRSAEncryption_65537
#endif
#if canImport(witnesscalc_disclose)
import witnesscalc_disclose
#endif
#if canImport(groth16_prover)
import groth16_prover
#endif
struct Proof: Codable {
let piA: [String]
let piB: [[String]]
let piC: [String]
let proofProtocol: String
enum CodingKeys: String, CodingKey {
case piA = "pi_a"
case piB = "pi_b"
case piC = "pi_c"
case proofProtocol = "protocol"
}
}
@available(iOS 15, *)
@objc(Prover)
class Prover: NSObject {
@objc(runProveAction:witness_calculator:dat_file_name:inputs:resolve:reject:)
func runProveAction(_ zkey_path: String, witness_calculator: String, dat_file_name: String, inputs: [String: [String]], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
do {
let inputsJson = try! JSONEncoder().encode(inputs)
print("inputs size: \(inputsJson.count) bytes")
print("inputs data: \(String(data: inputsJson, encoding: .utf8) ?? "")")
let wtns = try! calcWtns(
witness_calculator: witness_calculator,
dat_file_name: dat_file_name,
inputsJson: inputsJson
)
print("wtns size: \(wtns.count) bytes")
let (proofRaw, pubSignalsRaw) = try groth16prove(zkey_path: zkey_path, wtns: wtns)
let proof = try JSONDecoder().decode(Proof.self, from: proofRaw)
let pubSignals = try JSONDecoder().decode([String].self, from: pubSignalsRaw)
let proofObject: [String: Any] = [
"proof": [
"a": proof.piA,
"b": proof.piB,
"c": proof.piC,
],
"inputs": pubSignals
]
let proofData = try JSONSerialization.data(withJSONObject: proofObject, options: [])
let proofObjectString = String(data: proofData, encoding: .utf8) ?? ""
print("Whole proof: \(proofObjectString)")
resolve(proofObjectString)
} catch {
print("Unexpected error: \(error)")
reject("PROVER", "An error occurred during proof generation", error)
}
}
}
public func calcWtns(witness_calculator: String, dat_file_name: String, inputsJson: Data) throws -> Data {
let dat = NSDataAsset(name: dat_file_name + ".dat")!.data
return try _calcWtns(witness_calculator: witness_calculator, dat: dat, jsonData: inputsJson)
}
private func _calcWtns(witness_calculator: String, dat: Data, jsonData: Data) throws -> Data {
let datSize = UInt(dat.count)
let jsonDataSize = UInt(jsonData.count)
let errorSize = UInt(256);
let wtnsSize = UnsafeMutablePointer<UInt>.allocate(capacity: Int(1));
wtnsSize.initialize(to: UInt(100 * 1024 * 1024 ))
let wtnsBuffer = UnsafeMutablePointer<UInt8>.allocate(capacity: (100 * 1024 * 1024))
let errorBuffer = UnsafeMutablePointer<UInt8>.allocate(capacity: Int(errorSize))
let result: Int32
if witness_calculator == "register_sha256WithRSAEncryption_65537" {
result = witnesscalc_register_sha256WithRSAEncryption_65537(
(dat as NSData).bytes, datSize,
(jsonData as NSData).bytes, jsonDataSize,
wtnsBuffer, wtnsSize,
errorBuffer, errorSize
)
} else if witness_calculator == "disclose" {
result = witnesscalc_disclose(
(dat as NSData).bytes, datSize,
(jsonData as NSData).bytes, jsonDataSize,
wtnsBuffer, wtnsSize,
errorBuffer, errorSize
)
} else {
fatalError("Invalid witness calculator name")
}
if result == WITNESSCALC_ERROR {
let errorMessage = String(bytes: Data(bytes: errorBuffer, count: Int(errorSize)), encoding: .utf8)!
.replacingOccurrences(of: "\0", with: "")
throw NSError(domain: "WitnessCalculationError", code: Int(WITNESSCALC_ERROR), userInfo: [NSLocalizedDescriptionKey: errorMessage])
}
if result == WITNESSCALC_ERROR_SHORT_BUFFER {
let shortBufferMessage = "Short buffer, required size: \(wtnsSize.pointee)"
throw NSError(domain: "WitnessCalculationError", code: Int(WITNESSCALC_ERROR_SHORT_BUFFER), userInfo: [NSLocalizedDescriptionKey: shortBufferMessage])
}
return Data(bytes: wtnsBuffer, count: Int(wtnsSize.pointee))
}
public func groth16prove(zkey_path: String, wtns: Data) throws -> (proof: Data, publicInputs: Data) {
guard let zkeyURL = URL(string: "file://" + zkey_path) else {
throw NSError(domain: "YourErrorDomain", code: 0, userInfo: [NSLocalizedDescriptionKey: "Invalid zkey file path."])
}
print("zkeyURL: \(zkeyURL)")
guard let zkeyData = try? Data(contentsOf: zkeyURL) else {
throw NSError(domain: "YourErrorDomain", code: 0, userInfo: [NSLocalizedDescriptionKey: "Failed to load zkey file."])
}
return try _groth16Prover(zkey: zkeyData, wtns: wtns)
}
public func _groth16Prover(zkey: Data, wtns: Data) throws -> (proof: Data, publicInputs: Data) {
let zkeySize = zkey.count
let wtnsSize = wtns.count
var proofSize: UInt = 4 * 1024 * 1024
var publicSize: UInt = 4 * 1024 * 1024
let proofBuffer = UnsafeMutablePointer<UInt8>.allocate(capacity: Int(proofSize))
let publicBuffer = UnsafeMutablePointer<UInt8>.allocate(capacity: Int(publicSize))
let errorBuffer = UnsafeMutablePointer<Int8>.allocate(capacity: 256)
let errorMaxSize: UInt = 256
let result = groth16_prover(
(zkey as NSData).bytes, UInt(zkeySize),
(wtns as NSData).bytes, UInt(wtnsSize),
proofBuffer, &proofSize,
publicBuffer, &publicSize,
errorBuffer, errorMaxSize
)
if result == PROVER_ERROR {
let errorMessage = String(bytes: Data(bytes: errorBuffer, count: Int(errorMaxSize)), encoding: .utf8)!
.replacingOccurrences(of: "\0", with: "")
throw NSError(domain: "", code: Int(result), userInfo: [NSLocalizedDescriptionKey: errorMessage])
}
if result == PROVER_ERROR_SHORT_BUFFER {
let shortBufferMessage = "Proof or public inputs buffer is too short"
throw NSError(domain: "", code: Int(result), userInfo: [NSLocalizedDescriptionKey: shortBufferMessage])
}
var proof = Data(bytes: proofBuffer, count: Int(proofSize))
var publicInputs = Data(bytes: publicBuffer, count: Int(publicSize))
let proofNullIndex = proof.firstIndex(of: 0x00)!
let publicInputsNullIndex = publicInputs.firstIndex(of: 0x00)!
proof = proof[0..<proofNullIndex]
publicInputs = publicInputs[0..<publicInputsNullIndex]
return (proof: proof, publicInputs: publicInputs)
}