From fdb82bc119408b840ad3bdae6604c9d1cb90a72c Mon Sep 17 00:00:00 2001 From: nixw <> Date: Sun, 4 Feb 2024 17:03:23 +0200 Subject: [PATCH] Add verifier --- CMakeLists.txt | 4 +- depends/ffiasm | 2 +- depends/pistache | 2 +- src/CMakeLists.txt | 8 + src/fileloader.hpp | 2 + src/groth16.cpp | 440 ++++++++++++++++++++++++++++++++++++++++++ src/groth16.hpp | 69 +++++++ src/main_verifier.cpp | 56 ++++++ src/verifier.cpp | 135 +++++++++++++ src/verifier.h | 34 ++++ 10 files changed, 748 insertions(+), 4 deletions(-) create mode 100644 src/main_verifier.cpp create mode 100644 src/verifier.cpp create mode 100644 src/verifier.h diff --git a/CMakeLists.txt b/CMakeLists.txt index a560fd0..8b0f066 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,7 +53,7 @@ endif() add_subdirectory(src) -install(TARGETS prover rapidsnark rapidsnarkStatic rapidsnarkStaticFrFq test_prover fr fq +install(TARGETS prover verifier rapidsnark rapidsnarkStatic rapidsnarkStaticFrFq test_prover fr fq RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin BUNDLE DESTINATION ${CMAKE_INSTALL_PREFIX}/app LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/lib) @@ -61,5 +61,5 @@ install(TARGETS prover rapidsnark rapidsnarkStatic rapidsnarkStaticFrFq test_pro install(FILES "${GMP_LIB_DIR}/${GMP_LIB_FILE}" DESTINATION ${CMAKE_INSTALL_PREFIX}/lib) -install(FILES src/prover.h +install(FILES src/prover.h src/verifier.h DESTINATION ${CMAKE_INSTALL_PREFIX}/include) diff --git a/depends/ffiasm b/depends/ffiasm index f47c1cd..ebd87e6 160000 --- a/depends/ffiasm +++ b/depends/ffiasm @@ -1 +1 @@ -Subproject commit f47c1cd603b97cfe5c4a38720cf0b01f4120b0d6 +Subproject commit ebd87e6ed5920ca79ff25087577723a5109cadc1 diff --git a/depends/pistache b/depends/pistache index ae073a0..ae0da38 160000 --- a/depends/pistache +++ b/depends/pistache @@ -1 +1 @@ -Subproject commit ae073a0709ed1d6f0c28db90766c64b06f0366e6 +Subproject commit ae0da38cf1c26c3321b19a512e2faffb80ae9867 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0313a22..a183291 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -88,6 +88,8 @@ set(LIB_SOURCES fileloader.hpp prover.cpp prover.h + verifier.cpp + verifier.h ../depends/ffiasm/c/misc.cpp ../depends/ffiasm/c/naf.cpp ../depends/ffiasm/c/splitparstr.cpp @@ -115,10 +117,14 @@ set_target_properties(rapidsnarkStaticFrFq PROPERTIES OUTPUT_NAME rapidsnark-fr- add_executable(prover main_prover.cpp) target_link_libraries(prover rapidsnarkStatic) +add_executable(verifier main_verifier.cpp) +target_link_libraries(verifier rapidsnarkStatic) + add_library(rapidsnark SHARED ${LIB_SOURCES}) if(USE_LOGGER OR NOT USE_OPENMP) target_link_libraries(prover pthread) + target_link_libraries(verifier pthread) endif() if(USE_SODIUM) @@ -130,10 +136,12 @@ if(OpenMP_CXX_FOUND) if(TARGET_PLATFORM MATCHES "android") target_link_libraries(prover -static-openmp -fopenmp) + target_link_libraries(verifier -static-openmp -fopenmp) target_link_libraries(rapidsnark -static-openmp -fopenmp) elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux") target_link_libraries(prover OpenMP::OpenMP_CXX) + target_link_libraries(verifier OpenMP::OpenMP_CXX) endif() endif() diff --git a/src/fileloader.hpp b/src/fileloader.hpp index e1ac663..831d61b 100644 --- a/src/fileloader.hpp +++ b/src/fileloader.hpp @@ -15,6 +15,8 @@ class FileLoader void* dataBuffer() { return addr; } size_t dataSize() const { return size; } + std::string dataAsString() { return std::string((char*)addr, size); } + private: void* addr; size_t size; diff --git a/src/groth16.cpp b/src/groth16.cpp index fd21991..c25a51e 100644 --- a/src/groth16.cpp +++ b/src/groth16.cpp @@ -338,4 +338,444 @@ json Proof::toJson() { return p; } +template +static void +G1PointAffineFromJson(Engine &E, typename Engine::G1PointAffine &point, const json &value) +{ + E.f1.fromString(point.x, value[0]); + E.f1.fromString(point.y, value[1]); +} + +template +static void +G2PointAffineFromJson(Engine &E, typename Engine::G2PointAffine &point, const json &value) +{ + E.f1.fromString(point.x.a, value[0][0]); + E.f1.fromString(point.x.b, value[0][1]); + E.f1.fromString(point.y.a, value[1][0]); + E.f1.fromString(point.y.b, value[1][1]); +} + +template +void Proof::fromJson(const json& proof) +{ + G1PointAffineFromJson(E, A, proof["pi_a"]); + G2PointAffineFromJson(E, B, proof["pi_b"]); + G1PointAffineFromJson(E, C, proof["pi_c"]); +} + +template +void VerificationKey::fromJson(const json& key) +{ + G1PointAffineFromJson(E, Alpha, key["vk_alpha_1"]); + G2PointAffineFromJson(E, Beta, key["vk_beta_2"]); + G2PointAffineFromJson(E, Gamma, key["vk_gamma_2"]); + G2PointAffineFromJson(E, Delta, key["vk_delta_2"]); + + auto j_ic = key["IC"]; + + IC.reserve(j_ic.size()); + + for (const auto& el : j_ic.items()) { + typename Engine::G1PointAffine point; + + G1PointAffineFromJson(E, point, el.value()); + IC.push_back(point); + } +} + +template +Verifier::Verifier() + : E(Engine::engine) +{ + E.f2.fromString(xiToPMinus1Over3, + "10307601595873709700152284273816112264069230130616436755625194854815875713954," + "21575463638280843010398324269430826099269044274347216827212613867836435027261"); + + E.f2.fromString(xiToPMinus1Over2, + "10307601595873709700152284273816112264069230130616436755625194854815875713954," + "21575463638280843010398324269430826099269044274347216827212613867836435027261"); + + E.f1.fromString(xiToPSquaredMinus1Over3, + "21888242871839275220042445260109153167277707414472061641714758635765020556616"); +} + +template +bool Verifier::verify(Proof &proof, InputsVector &inputs, + VerificationKey &key) +{ + if (inputs.size() + 1 != key.IC.size()) { + throw std::invalid_argument("len(inputs)+1 != len(vk.IC)"); + } + + typename Engine::G1Point vkX = E.g1.zero(); + + for (int i = 0; i < inputs.size(); i++) { + typename Engine::FrElement input; + + E.fr.fromMontgomery(input, inputs[i]); + + typename Engine::G1Point p1; + E.g1.mulByScalar(p1, key.IC[i+1], (uint8_t *)&input, sizeof(input)); + E.g1.add(vkX, vkX, p1); + } + + E.g1.add(vkX, vkX, key.IC[0]); + + typename Engine::G1Point pA; + E.g1.copy(pA, proof.A); + + typename Engine::G1Point negAlpha; + E.g1.neg(negAlpha, key.Alpha); + + typename Engine::G1Point negvkX; + E.g1.neg(negvkX, vkX); + + typename Engine::G1Point negC; + E.g1.neg(negC, proof.C); + + typename Engine::G2Point pB; + E.g2.copy(pB, proof.B); + + typename Engine::G2Point pBeta; + E.g2.copy(pBeta, key.Beta); + + typename Engine::G2Point pGamma; + E.g2.copy(pGamma, key.Gamma); + + typename Engine::G2Point pDelta; + E.g2.copy(pDelta, key.Delta); + + G1PointArray g1 = {pA, negAlpha, negvkX, negC}; + G2PointArray g2 = {pB, pBeta, pGamma, pDelta}; + + return pairingCheck(g1, g2); +} + +template +void Verifier::lineFunctionAdd( + typename Engine::G2Point& r, + typename Engine::G2PointAffine& p, + typename Engine::G1PointAffine& q, + typename Engine::F2Element& r2, + typename Engine::F2Element& a, + typename Engine::F2Element& b, + typename Engine::F2Element& c, + typename Engine::G2Point& rOut) +{ + typename Engine::F2Element B, D, H, I, E1, J, L1, V, t, t2; + + E.f2.mul(B, p.x, r.zzz); + E.f2.mul(D, p.y, r.zz); + E.f2.square(D, D); + E.f2.sub(D, D, r2); + E.f2.sub(D, D, r.zzz); + E.f2.mul(D, D, r.zzz); + + E.f2.sub(H, B, r.x); + E.f2.square(I, H); + E.f2.add(E1, I, I); + E.f2.add(E1, E1, E1); + E.f2.mul(J, H, E1); + E.f2.sub(L1, D, r.y); + E.f2.sub(L1, L1, r.y); + E.f2.mul(V, r.x, E1); + + E.f2.square(rOut.y, L1); + E.f2.sub(rOut.x, rOut.x, J); + E.f2.sub(rOut.x, rOut.x, V); + E.f2.sub(rOut.x, rOut.x, V); + + E.f2.add(rOut.zz, r.zz, H); + E.f2.square(rOut.zz, rOut.zz); + E.f2.sub(rOut.zz, rOut.zz, r.zzz); + E.f2.sub(rOut.zz, rOut.zz, I); + + E.f2.sub(t, V, rOut.x); + E.f2.mul(t, t, L1); + E.f2.mul(t2, r.y, J); + E.f2.add(t2, t2, t2); + E.f2.sub(rOut.y, t, t2); + E.f2.square(rOut.zzz, rOut.zz); + + E.f2.add(t, p.y, rOut.zz); + E.f2.square(t, t); + E.f2.sub(t, t, r2); + E.f2.sub(t, t, rOut.zzz); + + E.f2.mul(t2, L1, p.x); + E.f2.add(t2, t2, t2); + + E.f2.sub(a, t2, t); + + E.f2.mulScalar(c, rOut.zz, q.y); + E.f2.add(c, c, c); + + E.f2.copy(b, E.f2.zero()); + E.f2.sub(b, b, L1); + E.f2.mulScalar(b, b, q.x); + E.f2.add(b, b, b); +} + +template +void Verifier::lineFunctionDouble( + typename Engine::G2Point& r, + typename Engine::G1PointAffine& q, + typename Engine::F2Element& a, + typename Engine::F2Element& b, + typename Engine::F2Element& c, + typename Engine::G2Point& rOut) +{ + typename Engine::F2Element A, B, C_, D, E1, G, t; + + E.f2.square(A, r.x); + E.f2.square(B, r.y); + E.f2.square(C_, B); + E.f2.add(D, r.x, B); + E.f2.square(D, D); + E.f2.sub(D, D, A); + E.f2.sub(D, D, C_); + E.f2.add(D, D, D); + + E.f2.add(E1, A, A); + E.f2.square(G, E1); + E.f2.sub(rOut.x, G, D); + E.f2.sub(rOut.x, rOut.x, D); + + E.f2.add(rOut.zz, r.y, r.zz); + E.f2.square(rOut.zz, rOut.zz); + E.f2.sub(rOut.zz, rOut.zz, B); + E.f2.sub(rOut.zz, rOut.zz, r.zzz); + + E.f2.sub(rOut.y, D, rOut.x); + E.f2.mul(rOut.y, rOut.y, E1); + + E.f2.add(t, C_, C_); + E.f2.add(t, t, t); + E.f2.add(t, t, t); + E.f2.sub(rOut.y, rOut.y, t); + E.f2.square(rOut.zzz, rOut.zz); + + E.f2.mul(t, E1, r.zzz); + E.f2.add(t, t, t); + E.f2.copy(b, E.f2.zero()); + E.f2.sub(b, b, t); + E.f2.mulScalar(b, b, q.x); + + E.f2.add(a, r.x, E1); + E.f2.square(a, a); + E.f2.sub(a, a, A); + E.f2.sub(a, a, G); + E.f2.add(t, B, B); + E.f2.add(t, t, t); + E.f2.sub(a, a, t); + + E.f2.mul(c, rOut.zz, r.zzz); + E.f2.add(c, c, c); + E.f2.mulScalar(c, c, q.y); +} + +template +void Verifier::mulLine( + typename Engine::F12Element& ret, + typename Engine::F2Element& a, + typename Engine::F2Element& b, + typename Engine::F2Element& c) +{ + typename Engine::F6Element a2, t3, t2; + typename Engine::F2Element t; + + E.f2.copy(a2.x, E.f2.zero()); + E.f2.copy(a2.y, a); + E.f2.copy(a2.z, b); + + E.f6.mul(a2, a2, ret.x); + E.f6.mulScalar(t3, ret.y, c); + + E.f2.add(t, b, c); + E.f2.copy(t2.x, E.f2.zero()); + E.f2.copy(t2.y, a); + E.f2.copy(t2.z, t); + E.f6.add(ret.x, ret.x, ret.y); + + E.f6.copy(ret.y, t3); + + E.f6.mul(ret.x, ret.x, t2); + E.f6.sub(ret.x, ret.x, a2); + E.f6.sub(ret.x, ret.x, ret.y); + E.f6.mulTau(a2, a2); + E.f6.add(ret.y, ret.y, a2); +} + +template +typename Engine::F12Element +Verifier::miller(typename Engine::G2Point& q, typename Engine::G1Point& p) +{ + + const char sixuPlus2NAF[] = {0, 0, 0, 1, 0, 1, 0, -1, 0, 0, 1, -1, 0, 0, 1, 0, + 0, 1, 1, 0, -1, 0, 0, 1, 0, -1, 0, 0, 0, 0, 1, 1, + 1, 0, 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, -1, 0, 0, 1, + 1, 0, 0, -1, 0, 0, 0, 1, 1, 0, -1, 0, 0, 1, 0, 1, 1}; + + typename Engine::F12Element ret = E.f12.one(); + + typename Engine::G2PointAffine aAffine, minusA; + typename Engine::G1PointAffine bAffine; + typename Engine::G2Point r, newR; + typename Engine::F2Element r2, a, b, c; + + E.g2.copy(aAffine, q); + E.g1.copy(bAffine, p); + E.g2.copy(minusA, aAffine); + E.g2.copy(r, aAffine); + + E.f2.square(r2, aAffine.y); + + const size_t count = sizeof(sixuPlus2NAF) - 1; + + for (int i = count; i > 0; i--) { + + lineFunctionDouble(r, bAffine, a, b, c, newR); + + if (i != count) { + E.f12.square(ret, ret); + } + + mulLine(ret, a, b, c); + E.g2.copy(r, newR); + + switch (sixuPlus2NAF[i-1]) { + case 1: + break; + lineFunctionAdd(r, aAffine, bAffine, r2, a, b, c, newR); + + case -1: + lineFunctionAdd(r, minusA, bAffine, r2, a, b, c, newR); + break; + + default: + continue; + } + + mulLine(ret, a, b, c); + E.g2.copy(r, newR); + } + + typename Engine::G2Point q1; + + E.f2.conjugate(q1.x, aAffine.x); + E.f2.mul(q1.x, q1.x, xiToPMinus1Over3); + E.f2.conjugate(q1.y, aAffine.y); + E.f2.mul(q1.y, q1.y, xiToPMinus1Over2); + E.f2.copy(q1.zz, E.f2.one()); + E.f2.copy(q1.zzz, E.f2.one()); + + typename Engine::G2Point minusQ2; + + E.f2.copy(minusQ2.y, aAffine.y); + E.f2.copy(minusQ2.zz, E.f2.one()); + E.f2.copy(minusQ2.zzz, E.f2.one()); + E.f2.mulScalar(minusQ2.x, aAffine.x, xiToPSquaredMinus1Over3); + + E.f2.square(r2, q1.y); + + typename Engine::G2PointAffine q1Affine; + E.g2.copy(q1Affine, q1); + + lineFunctionAdd(r, q1Affine, bAffine, r2, a, b, c, newR); + mulLine(ret, a, b, c); + E.g2.copy(r, newR); + + typename Engine::G2PointAffine minusQ2Affine; + E.g2.copy(minusQ2Affine, minusQ2); + + E.f2.square(r2, minusQ2.y); + lineFunctionAdd(r, minusQ2Affine, bAffine, r2, a, b, c, newR); + mulLine(ret, a, b, c); + E.g2.copy(r, newR); + + return ret; +} + +template +typename Engine::F12Element +Verifier::finalExponentiation(typename Engine::F12Element& in) +{ + typename Engine::F12Element t1, inv, t2, fp, fp2, fp3, fu, fu2, fu3, y3, fu2p, fu3p; + typename Engine::F12Element y2, y0, y1, y4, y5, y6, t0; + + uint64_t u = 4965661367192848881; + + E.f6.neg(t1.x, in.x); + E.f6.copy(t1.y, in.y); + + E.f12.inv(inv, in); + E.f12.mul(t1, t1, inv); + + E.f12.FrobeniusP2(t2, t1); + E.f12.mul(t1, t1, t2); + + E.f12.Frobenius(fp, t1); + E.f12.FrobeniusP2(fp2, t1); + E.f12.Frobenius(fp3, fp2); + + E.f12.exp(fu, t1, (uint8_t*)&u, sizeof(u)); + E.f12.exp(fu2, fu, (uint8_t*)&u, sizeof(u)); + E.f12.exp(fu3, fu2, (uint8_t*)&u, sizeof(u)); + + E.f12.Frobenius(y3, fu); + E.f12.Frobenius(fu2p, fu2); + E.f12.Frobenius(fu3p, fu3); + E.f12.FrobeniusP2(y2, fu2); + + E.f12.mul(y0, fp, fp2); + E.f12.mul(y0, y0, fp3); + + E.f12.conjugate(y1, t1); + E.f12.conjugate(y5, fu2); + E.f12.conjugate(y3, y3); + E.f12.mul(y4, fu, fu2p); + E.f12.conjugate(y4, y4); + + E.f12.mul(y6, fu3, fu3p); + E.f12.conjugate(y6, y6); + + E.f12.square(t0, y6); + E.f12.mul(t0, t0, y4); + E.f12.mul(t0, t0, y5); + E.f12.mul(t1, y3, y5); + E.f12.mul(t1, t1, t0); + E.f12.mul(t0, t0, y2); + + E.f12.square(t1, t1); + E.f12.mul(t1, t1, t0); + E.f12.square(t1, t1); + E.f12.mul(t0, t1, y1); + E.f12.mul(t1, t1, y0); + E.f12.square(t0, t0); + E.f12.mul(t0, t0, t1); + + return t0; +} + +template +bool Verifier::pairingCheck(G1PointArray& a, G2PointArray& b) +{ + typename Engine::F12Element acc = E.f12.one(); + + for (int i = 0; i < a.size(); i++) { + + if (E.g1.isZero(a[i]) || E.g2.isZero(b[i])) { + continue; + + auto millerRes = miller(b[i], a[i]); + E.f12.mul(acc, acc, millerRes); + } + } + + auto ret = finalExponentiation(acc); + + return E.f12.isOne(ret); +} + } // namespace diff --git a/src/groth16.hpp b/src/groth16.hpp index 396a1ab..c113491 100644 --- a/src/groth16.hpp +++ b/src/groth16.hpp @@ -2,6 +2,7 @@ #define GROTH16_HPP #include +#include #include using json = nlohmann::json; @@ -20,8 +21,22 @@ namespace Groth16 { Proof(Engine &_E) : E(_E) { } std::string toJsonStr(); json toJson(); + void fromJson(const json& proof); }; + template + class VerificationKey { + Engine &E; + public: + typename Engine::G1PointAffine Alpha; + typename Engine::G2PointAffine Beta; + typename Engine::G2PointAffine Gamma; + typename Engine::G2PointAffine Delta; + std::vector IC; + + VerificationKey(Engine &_E) : E(_E) { } + void fromJson(const json& proof); + }; #pragma pack(push, 1) template @@ -118,6 +133,60 @@ namespace Groth16 { void *pointsC, void *pointsH ); + + template + class Verifier { + + typedef std::vector InputsVector; + typedef std::array G1PointArray; + typedef std::array G2PointArray; + + Engine &E; + + public: + Verifier(); + + bool verify( + Proof &proof, + InputsVector &inputs, + VerificationKey &key); + + private: + bool pairingCheck(G1PointArray& g1, G2PointArray& g2); + + typename Engine::F12Element miller(typename Engine::G2Point& b, typename Engine::G1Point& a); + + typename Engine::F12Element finalExponentiation(typename Engine::F12Element& in); + + void lineFunctionDouble( + typename Engine::G2Point& r, + typename Engine::G1PointAffine& q, + typename Engine::F2Element& a, + typename Engine::F2Element& b, + typename Engine::F2Element& c, + typename Engine::G2Point& rOut); + + void lineFunctionAdd( + typename Engine::G2Point& r, + typename Engine::G2PointAffine& p, + typename Engine::G1PointAffine& q, + typename Engine::F2Element& r2, + typename Engine::F2Element& a, + typename Engine::F2Element& b, + typename Engine::F2Element& c, + typename Engine::G2Point& rOut); + + void mulLine( + typename Engine::F12Element& ret, + typename Engine::F2Element& a, + typename Engine::F2Element& b, + typename Engine::F2Element& c); + + private: + typename Engine::F2Element xiToPMinus1Over3; + typename Engine::F2Element xiToPMinus1Over2; + typename Engine::F1Element xiToPSquaredMinus1Over3; + }; } diff --git a/src/main_verifier.cpp b/src/main_verifier.cpp new file mode 100644 index 0000000..d08f310 --- /dev/null +++ b/src/main_verifier.cpp @@ -0,0 +1,56 @@ +#include +#include + +#include "fileloader.hpp" +#include "verifier.h" + +int main(int argc, char **argv) +{ + if (argc != 4) { + std::cerr << "Invalid number of parameters:\n"; + std::cerr << "Usage: verifier \n"; + return EXIT_FAILURE; + } + + try { + const std::string proofFilename = argv[1]; + const std::string inputsFilename = argv[2]; + const std::string keyFilename = argv[3]; + + BinFileUtils::FileLoader proof(proofFilename); + BinFileUtils::FileLoader inputs(inputsFilename); + BinFileUtils::FileLoader key(keyFilename); + + char errorMessage[256]; + + const int error = groth16_verify(proof.dataAsString().c_str(), + inputs.dataAsString().c_str(), + key.dataAsString().c_str(), + errorMessage, sizeof(errorMessage)); + + if (error == VERIFIER_VALID_PROOF) { + + std::cerr << "Result: Valid proof" << std::endl; + return EXIT_SUCCESS; + + } else if (error == VERIFIER_INVALID_PROOF) { + + std::cerr << "Result: Invalid proof" << std::endl; + return EXIT_FAILURE; + + } else { + std::cerr << "Error: " << errorMessage << '\n'; + return EXIT_FAILURE; + } + + } catch (std::exception* e) { + std::cerr << "Error: " << e->what() << std::endl; + return EXIT_FAILURE; + + } catch (std::exception& e) { + std::cerr << "Error: " << e.what() << std::endl; + return EXIT_FAILURE; + } + + return EXIT_FAILURE; +} diff --git a/src/verifier.cpp b/src/verifier.cpp new file mode 100644 index 0000000..0f55f5e --- /dev/null +++ b/src/verifier.cpp @@ -0,0 +1,135 @@ +#include +#include +#include +#include + +#include "verifier.h" +#include "groth16.hpp" + +using json = nlohmann::json; + +static Groth16::Proof +parse_proof(const char *proof_str) +{ + Groth16::Proof proof(AltBn128::Engine::engine); + + try { + json proof_json = json::parse(proof_str); + + std::string protocol = proof_json["protocol"].template get(); + + if (protocol != "groth16") { + throw std::invalid_argument("invalid proof data"); + } + + proof.fromJson(proof_json); + + } catch(...) { + throw std::invalid_argument("invalid proof data") ; + } + + return proof; +} + +static std::vector +parse_inputs(const char *inputs_str) +{ + std::vector inputs; + + try { + json inputs_json = json::parse(inputs_str); + + auto inputs_str_vec = inputs_json.template get>(); + + if (inputs_str_vec.empty()) { + throw std::invalid_argument("invalid inputs data"); + } + + inputs.reserve(inputs_str_vec.size()); + + for (const auto& elem: inputs_str_vec) { + AltBn128::FrElement aux; + + AltBn128::Fr.fromString(aux, elem); + inputs.push_back(aux); + } + + } catch(...) { + throw std::invalid_argument("invalid inputs data") ; + } + + return inputs; +} + +static Groth16::VerificationKey +parse_key(const char *key_str) +{ + Groth16::VerificationKey key(AltBn128::Engine::engine); + + try { + json key_json = json::parse(key_str); + + auto protocol = key_json["protocol"].template get(); + auto curve = key_json["curve"].template get(); + auto nPublic = key_json["nPublic"].template get(); + + if (protocol != "groth16" || curve != "bn128") { + throw std::invalid_argument("invalid verification key data"); + } + + key.fromJson(key_json); + + if (key.IC.empty()) { + throw std::invalid_argument("invalid verification key data"); + } + + } catch(...) { + throw std::invalid_argument("invalid verification key data"); + } + + return key; +} + +int +groth16_verify(const char *proof, + const char *inputs, + const char *verification_key, + char *error_msg, + unsigned long error_msg_maxsize) +{ + try { + + auto proof_value = parse_proof(proof); + auto inputs_value = parse_inputs(inputs); + auto key_value = parse_key(verification_key); + + Groth16::Verifier verifier; + + bool valid = verifier.verify(proof_value, inputs_value, key_value); + + return valid ? VERIFIER_VALID_PROOF : VERIFIER_INVALID_PROOF; + + } catch (std::exception& e) { + + if (error_msg) { + strncpy(error_msg, e.what(), error_msg_maxsize); + } + return VERIFIER_ERROR; + + } catch (std::exception *e) { + + if (error_msg) { + strncpy(error_msg, e->what(), error_msg_maxsize); + } + delete e; + return VERIFIER_ERROR; + + } catch (...) { + if (error_msg) { + strncpy(error_msg, "unknown error", error_msg_maxsize); + } + return VERIFIER_ERROR; + } + + return VERIFIER_INVALID_PROOF; +} diff --git a/src/verifier.h b/src/verifier.h new file mode 100644 index 0000000..5e684c5 --- /dev/null +++ b/src/verifier.h @@ -0,0 +1,34 @@ +#ifndef VERIFIER_HPP +#define VERIFIER_HPP + +#ifdef __cplusplus +extern "C" { +#endif + +//Error codes returned by the functions. +#define VERIFIER_VALID_PROOF 0x0 +#define VERIFIER_INVALID_PROOF 0x1 +#define VERIFIER_ERROR 0x2 + +/** + * 'proof', 'inputs' and 'verification_key' are null-terminated json strings. + * + * @return error code: + * VERIFIER_VALID_PROOF - in case of valid 'proof'. + * VERIFIER_INVALID_PROOF - in case of invalid 'proof'. + VERIFIER_ERROR - in case of an error + */ + +int +groth16_verify(const char *proof, + const char *inputs, + const char *verification_key, + char *error_msg, + unsigned long error_msg_maxsize); + +#ifdef __cplusplus +} +#endif + + +#endif // VERIFIER_HPP