From 7a1e04de3e5f3a9f0cfb27a43c9f41c986c1b9ed Mon Sep 17 00:00:00 2001 From: Oleksandr Brezhniev Date: Mon, 11 Mar 2024 16:55:56 +0100 Subject: [PATCH] Tags and fixes (#126) * Introduce tags. Fix some review comments from Arnau * Fixes * Wrapped circuits to fix unit tests * Update tags-managing.circom Co-authored-by: GopherDID <74898029+vmidyllic@users.noreply.github.com> --------- Co-authored-by: GopherDID <74898029+vmidyllic@users.noreply.github.com> --- circuits/auth/authV2.circom | 14 +- circuits/lib/idOwnership.circom | 2 +- circuits/lib/linked/linkId.circom | 3 +- circuits/lib/query/comparators.circom | 16 +- .../query/processQueryWithModifiers.circom | 22 +- circuits/lib/query/query.circom | 14 +- circuits/lib/stateTransition.circom | 2 +- circuits/lib/utils/claimUtils.circom | 64 +++-- circuits/lib/utils/claimUtilsWrappers.circom | 17 -- circuits/lib/utils/idUtils.circom | 5 +- circuits/lib/utils/safeOne.circom | 2 +- circuits/lib/utils/tags-managing.circom | 256 ++++++++++++++++++ circuits/lib/utils/treeUtils.circom | 10 +- .../credentialAtomicQueryV3OffChain.circom | 17 +- .../credentialAtomicQueryV3OnChain.circom | 9 +- .../claimUtils_getClaimMerklizeRoot.circom | 16 +- .../utils_checkIdenStateMatchesRoots.circom | 18 +- .../utils_getClaimSubjectOtherIden.circom | 13 +- .../utils/utils_getSubjectLocation.circom | 12 +- test/circuits/utils/utils_isExpirable.circom | 12 +- test/circuits/utils/utils_isUpdatable.circom | 12 +- .../utils/utils_verifyClaimSignature.circom | 22 +- .../utils/utils_verifyExpirationTime.circom | 14 +- 23 files changed, 469 insertions(+), 103 deletions(-) delete mode 100644 circuits/lib/utils/claimUtilsWrappers.circom create mode 100644 circuits/lib/utils/tags-managing.circom diff --git a/circuits/auth/authV2.circom b/circuits/auth/authV2.circom index 68004507..7fe9efbc 100644 --- a/circuits/auth/authV2.circom +++ b/circuits/auth/authV2.circom @@ -9,8 +9,8 @@ include "../lib/utils/safeOne.circom"; template AuthV2(IdOwnershipLevels, onChainLevels) { signal input genesisID; - // random number, which should be stored by user - // if there is a need to generate the same userID (ProfileID) output for different proofs + // random number, which should be stored by user if there is a need to + // generate the same userID (ProfileID) output for different proofs signal input profileNonce; // user state @@ -31,7 +31,7 @@ template AuthV2(IdOwnershipLevels, onChainLevels) { signal input authClaimNonRevMtpAuxHi; signal input authClaimNonRevMtpAuxHv; - // challenge signature + // challenge and it's signature signal input challenge; signal input challengeSignatureR8x; signal input challengeSignatureR8y; @@ -50,7 +50,7 @@ template AuthV2(IdOwnershipLevels, onChainLevels) { signal output userID; // get safe zero and one values to be used in ForceEqualIfEnabled - signal one <== SafeOne()(genesisID); + signal {binary} one <== SafeOne()(genesisID); checkAuthV2(IdOwnershipLevels, onChainLevels)( one, @@ -81,7 +81,7 @@ template AuthV2(IdOwnershipLevels, onChainLevels) { } template checkAuthV2(IdOwnershipLevels, onChainLevels) { - signal input enabled; + signal input {binary} enabled; signal input genesisID; @@ -143,7 +143,7 @@ template checkAuthV2(IdOwnershipLevels, onChainLevels) { signal isStateGenesis <== IsEqual()([cutId, cutState]); - signal genesisIDhash <== Poseidon(1)([genesisID]); + signal genesisIDHash <== Poseidon(1)([genesisID]); SMTVerifier(onChainLevels)( enabled <== enabled, @@ -153,7 +153,7 @@ template checkAuthV2(IdOwnershipLevels, onChainLevels) { oldKey <== gistMtpAuxHi, oldValue <== gistMtpAuxHv, isOld0 <== gistMtpNoAux, - key <== genesisIDhash, + key <== genesisIDHash, value <== state ); } diff --git a/circuits/lib/idOwnership.circom b/circuits/lib/idOwnership.circom index fb7e96bf..ac2768f0 100644 --- a/circuits/lib/idOwnership.circom +++ b/circuits/lib/idOwnership.circom @@ -12,7 +12,7 @@ include "utils/claimUtils.circom"; include "utils/treeUtils.circom"; template IdOwnership(nLevels) { - signal input enabled; + signal input {binary} enabled; signal input userState; diff --git a/circuits/lib/linked/linkId.circom b/circuits/lib/linked/linkId.circom index 03f961f7..e7ec76f1 100644 --- a/circuits/lib/linked/linkId.circom +++ b/circuits/lib/linked/linkId.circom @@ -1,7 +1,8 @@ pragma circom 2.1.5; -include "../../../node_modules/circomlib/circuits/poseidon.circom"; include "../../../node_modules/circomlib/circuits/comparators.circom"; +include "../../../node_modules/circomlib/circuits/mux1.circom"; +include "../../../node_modules/circomlib/circuits/poseidon.circom"; template LinkID() { signal input claimHash; diff --git a/circuits/lib/query/comparators.circom b/circuits/lib/query/comparators.circom index a2740bd1..2e089bb0 100644 --- a/circuits/lib/query/comparators.circom +++ b/circuits/lib/query/comparators.circom @@ -3,9 +3,12 @@ pragma circom 2.1.1; include "../../../node_modules/circomlib/circuits/comparators.circom"; include "../../../node_modules/circomlib/circuits/gates.circom"; -// nElements - number of value elements -// Example nElements = 3, '1' in ['12', '1231', '9999'], 1 not in array of values -template IN (valueArraySize){ +// Checks if value `in` is included is in value array +// Returns 1 if at least one value is equal to `in`, 0 otherwise +// valueArraySize - size of value array +// Example: IN(3)(1, [12, 1231, 9999]) ==> 0 (1 is not in array of values) +// Example: IN(3)(1231, [12, 1231, 9999]) ==> 1 (1231 is in array of values) +template IN(valueArraySize){ signal input in; signal input value[valueArraySize]; @@ -25,7 +28,8 @@ template IN (valueArraySize){ } // Same as IN but stops checking on valueArraySize -template InWithDynamicArraySize (maxValueArraySize){ +// Example: InWithDynamicArraySize(5)(0, [12, 1231, 9999, 0, 0], 3) ==> 0 (0 is not in the first 3 elements of value array) +template InWithDynamicArraySize(maxValueArraySize){ signal input in; signal input value[maxValueArraySize]; signal input valueArraySize; @@ -38,7 +42,7 @@ template InWithDynamicArraySize (maxValueArraySize){ signal lt[maxValueArraySize]; isEq[0] <== 0; for (var i=0; i skip MTP verification + fnc <== claimPathNotExists, // inclusion (or non-inclusion in case exists==false) root <== merklizedRoot, siblings <== claimPathMtp, oldKey <== claimPathMtpAuxHi, @@ -55,11 +57,11 @@ template ProcessQueryWithModifiers(claimLevels, maxValueArraySize){ merklized ); - // For non-merklized credentials exists / non-exist operators don't work - signal operatorNotExists <== NOT()(IsEqual()([operator, 11])); + // For non-merklized credentials exists / non-exist operators should always fail + signal isOpExists <== IsEqual()([operator, 11]); ForceEqualIfEnabled()( AND()(enabled, NOT()(merklized)), - [1, operatorNotExists] + [isOpExists, 0] ); ///////////////////////////////////////////////////////////////// diff --git a/circuits/lib/query/query.circom b/circuits/lib/query/query.circom index 0cb2e59c..eff2a9c6 100644 --- a/circuits/lib/query/query.circom +++ b/circuits/lib/query/query.circom @@ -9,7 +9,7 @@ include "comparators.circom"; /* Operators: Query operators: - 0 - noop, skip execution. Ignores all `in` and `value` passed to query, out 1 + 0 - noop. Ignores all `in` and `value` passed to query, out 1 1 - equals 2 - less than 3 - greater than @@ -18,14 +18,16 @@ include "comparators.circom"; 6 - not equals 7 - less than or equal 8 - greater than or equal - 9 - between + 9 - between (value[0] <= in <= value[1]) 10 - not between - 11 - exist + 11 - exist (true / false) Modifier/computation operators: 16 - selective disclosure (16 = 10000 binary) */ -// Query template works only with Query operators (0-15), for the rest returns 0 +// Query template works only with Query operators (0-15). +// Returns 1 if query operator is satisfied, 0 otherwise. +// For modifier/computation operators (16-31) it always returns 0. template Query (maxValueArraySize) { // signals signal input in; @@ -66,7 +68,7 @@ template Query (maxValueArraySize) { // modifier/computation operator. It's used in the final mux. _ <== opBits[4]; - queryOpSatisfied.c[0] <== 1; // noop; skip execution + queryOpSatisfied.c[0] <== 1; // noop; always succeeds queryOpSatisfied.c[1] <== eq; queryOpSatisfied.c[2] <== lt; queryOpSatisfied.c[3] <== gt; @@ -77,7 +79,7 @@ template Query (maxValueArraySize) { queryOpSatisfied.c[8] <== gte; // gte === !lt queryOpSatisfied.c[9] <== between; // between queryOpSatisfied.c[10] <== NOT()(between); // not between - queryOpSatisfied.c[11] <== 1; // exists; + queryOpSatisfied.c[11] <== 1; // exists(true/false) - actual check is done by checking inclusion/non-inclusion of claimPathKey in merklized root by SMTVerifier outside queryOpSatisfied.c[12] <== 0; // not used queryOpSatisfied.c[13] <== 0; // not used queryOpSatisfied.c[14] <== 0; // not used diff --git a/circuits/lib/stateTransition.circom b/circuits/lib/stateTransition.circom index 67621806..7271fe21 100644 --- a/circuits/lib/stateTransition.circom +++ b/circuits/lib/stateTransition.circom @@ -38,7 +38,7 @@ template StateTransition(IdOwnershipLevels) { signal input newRootsTreeRoot; // get safe one values to be used in ForceEqualIfEnabled - signal one <== SafeOne()(userID); + signal {binary} one <== SafeOne()(userID); signal cutId <== cutId()(userID); diff --git a/circuits/lib/utils/claimUtils.circom b/circuits/lib/utils/claimUtils.circom index e849ca40..82a29e68 100644 --- a/circuits/lib/utils/claimUtils.circom +++ b/circuits/lib/utils/claimUtils.circom @@ -11,24 +11,27 @@ include "./idUtils.circom"; // getClaimSubjectOtherIden checks that a claim Subject is OtherIden and outputs the identity within. template getClaimSubjectOtherIden() { signal input claim[8]; - signal input claimFlags[32]; + signal input {binary} claimFlags[32]; signal output id; // get subject location from header flags. component subjectLocation = getSubjectLocation(); subjectLocation.claimFlags <== claimFlags; - component mux = Mux2(); - component n2b = Num2Bits(2); + component mux = Mux3(); + component n2b = Num2Bits(3); n2b.in <== subjectLocation.out; - mux.s[0] <== n2b.out[0]; - mux.s[1] <== n2b.out[1]; + mux.s <== n2b.out; mux.c[0] <== 0; mux.c[1] <== 0; mux.c[2] <== claim[0*4 + 1]; mux.c[3] <== claim[1*4 + 1]; + mux.c[4] <== 0; + mux.c[5] <== 0; + mux.c[6] <== 0; + mux.c[7] <== 0; id <== mux.out; @@ -41,8 +44,8 @@ template getClaimSubjectOtherIden() { // getClaimMerkilizeFlag checks that a claim flag is set and return merklized slot. template getClaimMerklizeRoot() { signal input claim[8]; - signal input claimFlags[32]; - signal output flag; // 0 non merklized, 1 merklized + signal input {binary} claimFlags[32]; + signal output {binary} flag; // 0 non merklized, 1 merklized // merklizeFlag = 0 out = 0 , non merkilized // merklizeFlag = 1 out=claim_i_2, root is stored in index slot 2 (i_2) @@ -52,26 +55,26 @@ template getClaimMerklizeRoot() { // get subject location from header flags. signal merklizeLocation <== getMerklizeLocation()(claimFlags); - component mux = Mux2(); - component n2b = Num2Bits(2); + component mux = Mux3(); + component n2b = Num2Bits(3); n2b.in <== merklizeLocation; - mux.s[0] <== n2b.out[0]; - mux.s[1] <== n2b.out[1]; + mux.s <== n2b.out; mux.c[0] <== 0; mux.c[1] <== claim[0*4 +2]; mux.c[2] <== claim[1*4 +2]; mux.c[3] <== 0; + mux.c[4] <== 0; + mux.c[5] <== 0; + mux.c[6] <== 0; + mux.c[7] <== 0; out <== mux.out; - component gt = GreaterThan(3); // there's only 3 bits in merklizeLocation - gt.in[0] <== merklizeLocation; - gt.in[1] <== 0; - flag <== gt.out; + flag <== GreaterThan(3)([merklizeLocation, 0]); // there's only 3 bits in merklizeLocation - // explicitly state that these signals are not used and it's ok + // explicitly state that some signals are not used and it's ok for (var i=0; i<8; i++) { _ <== claim[i]; } @@ -84,7 +87,7 @@ template getClaimHeader() { signal input claim[8]; signal output schema; - signal output claimFlags[32]; + signal output {binary} claimFlags[32]; component i0Bits = Num2Bits(254); i0Bits.in <== claim[0]; @@ -169,9 +172,9 @@ template getClaimHash() { // verifyCredentialSubject verifies that claim is issued to a specified identity or identity profile // if nonce 0 is used, the claim should be issued to the genesis identity template verifyCredentialSubjectProfile() { - signal input enabled; + signal input {binary} enabled; signal input claim[8]; - signal input claimFlags[32]; + signal input {binary} claimFlags[32]; signal input id; signal input nonce; @@ -190,7 +193,7 @@ template verifyCredentialSubjectProfile() { // verifyCredentialSchema verifies that claim matches provided schema template verifyCredentialSchema() { - signal input enabled; + signal input {binary} enabled; signal input claimSchema; signal input schema; @@ -202,7 +205,7 @@ template verifyCredentialSchema() { // verifyClaimSignature verifies that claim is signed with the provided public key template verifyClaimSignature() { - signal input enabled; + signal input {binary} enabled; signal input claimHash; signal input sigR8x; signal input sigR8y; @@ -223,7 +226,7 @@ template verifyClaimSignature() { } template checkDataSignatureWithPubKeyInClaim() { - signal input enabled; + signal input {binary} enabled; signal input claim[8]; signal input signatureS; signal input signatureR8X; @@ -280,10 +283,12 @@ template getValueByIndex(){ // verify that the claim has expiration time and it is less then timestamp template verifyExpirationTime() { - signal input expirationFlag; // claimFlags[3] (expiration flag) is set + signal input {binary} expirationFlag; // claimFlags[3] (expiration flag) is set signal input claim[8]; signal input timestamp; + _ <== Num2Bits(64)(timestamp); // allow max 64 bit number for timestamp + signal claimExpiration <== getClaimExpiration()(claim); // timestamp < claimExpiration @@ -302,7 +307,7 @@ template verifyExpirationTime() { template getClaimExpiration() { signal input claim[8]; - signal output expiration; + signal output {maxbit} expiration; component expirationBits = Bits2Num(64); @@ -311,6 +316,7 @@ template getClaimExpiration() { for (var i=0; i<64; i++) { expirationBits.in[i] <== v0Bits.out[i+64]; } + expiration.maxbit = 64; expiration <== expirationBits.out; // explicitly state that some of these signals are not used and it's ok @@ -324,7 +330,7 @@ template getClaimExpiration() { // getSubjectLocation extract subject from claim flags. template getSubjectLocation() { - signal input claimFlags[32]; + signal input {binary} claimFlags[32]; signal output out; component subjectBits = Bits2Num(3); @@ -344,7 +350,7 @@ template getSubjectLocation() { // getMerklizeLocation extract merklize from claim flags. // 0 - not merklized, 1 - root in index slot i_2[root], 2 - root in value slot v_2[root] template getMerklizeLocation() { - signal input claimFlags[32]; + signal input {binary} claimFlags[32]; signal output out; component mtBits = Bits2Num(3); @@ -363,7 +369,7 @@ template getMerklizeLocation() { // isExpirable return 1 if expiration flag is set otherwise 0. template isExpirable() { - signal input claimFlags[32]; + signal input {binary} claimFlags[32]; signal output out; out <== claimFlags[3]; @@ -371,8 +377,8 @@ template isExpirable() { // isUpdatable return 1 if updatable flag is set otherwise 0. template isUpdatable() { - signal input claimFlags[32]; - signal output out; + signal input {binary} claimFlags[32]; + signal output {binary} out; out <== claimFlags[4]; } diff --git a/circuits/lib/utils/claimUtilsWrappers.circom b/circuits/lib/utils/claimUtilsWrappers.circom deleted file mode 100644 index 31d91d35..00000000 --- a/circuits/lib/utils/claimUtilsWrappers.circom +++ /dev/null @@ -1,17 +0,0 @@ -pragma circom 2.1.1; - -include "../../../node_modules/circomlib/circuits/mux3.circom"; -include "../../../node_modules/circomlib/circuits/mux1.circom"; -include "../../../node_modules/circomlib/circuits/mux2.circom"; -include "./claimUtils.circom"; - -template getClaimSubjectOtherIdenWrapper() { - signal input claim[8]; - signal output id; - - // get header flags from claim. - component header = getClaimHeader(); - header.claim <== claim; - - id <== getClaimSubjectOtherIden()(claim, header.claimFlags); -} diff --git a/circuits/lib/utils/idUtils.circom b/circuits/lib/utils/idUtils.circom index 7d370579..89065e21 100644 --- a/circuits/lib/utils/idUtils.circom +++ b/circuits/lib/utils/idUtils.circom @@ -3,6 +3,7 @@ pragma circom 2.1.1; include "../../../node_modules/circomlib/circuits/bitify.circom"; include "../../../node_modules/circomlib/circuits/poseidon.circom"; include "../../../node_modules/circomlib/circuits/mux1.circom"; +include "./safeOne.circom"; template ProfileID(){ signal input in; @@ -29,7 +30,7 @@ template SplitID() { signal output genesis; signal output checksum; - component bs = Num2Bits(254); + component bs = Num2Bits(248); bs.in <== id; // checksum bytes are swapped in ID. 31-th byte is first and 30-th is second. @@ -52,7 +53,7 @@ template SplitID() { typ <== typBits.out; // explicitly state that some of these signals are not used and it's ok - for (var i=0; i<254; i++) { + for (var i=0; i<248; i++) { _ <== bs.out[i]; } } diff --git a/circuits/lib/utils/safeOne.circom b/circuits/lib/utils/safeOne.circom index cc4ac635..12620725 100644 --- a/circuits/lib/utils/safeOne.circom +++ b/circuits/lib/utils/safeOne.circom @@ -18,6 +18,6 @@ template SafeOne() { signal tmp <== IsZero()(inputSignal); signal tmp2 <== NOT()(tmp); signal zero <== IsEqual()([tmp, tmp2]); - signal output one <== IsZero()(zero); + signal output {binary} one <== IsZero()(zero); zero * one === 0; } diff --git a/circuits/lib/utils/tags-managing.circom b/circuits/lib/utils/tags-managing.circom new file mode 100644 index 00000000..880a1f60 --- /dev/null +++ b/circuits/lib/utils/tags-managing.circom @@ -0,0 +1,256 @@ +/* + Copyright 2018 0KIMS association. + + This file is part of circom (Zero Knowledge Circuit Compiler). + + circom is a free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + circom is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU General Public License + along with circom. If not, see . +*/ +pragma circom 2.1.5; + +include "../../../node_modules/circomlib/circuits/bitify.circom"; +include "../../../node_modules/circomlib/circuits/comparators.circom"; + + +// The templates and functions in this file are general and work for any prime field + +// To consult the tags specifications check tags-specifications.circom + + +/* +*** AddBinaryTag(n): template that adds the constraints needed to ensure that a signal in is binary and adds the tag binary to the input + - Inputs: in -> field value + - Output: out -> same value as in, but including binary tag + satisfies tag binary + + Example: AddBinaryTag()(1) = 1 + Note: in case the input in is not binary then the generated system of constraints does not have any solution for that input. + For instance, AddBinaryTag()(10) -> no solution + +*/ + +template AddBinaryTag () { + signal input in; + signal output {binary} out; + + in * (in - 1) === 0; + out <== in; +} + +/* +*** AddBinaryArrayTag(n): template that adds the constraints needed to ensure that all signal of an array of n elements are binary and adds the tag binary to the input + - Inputs: in[n] -> array of n field elements + - Output: out[n] -> same value as in, but including binary tag + satisfies tag binary + + Example: AddBinaryArrayTag(2)([0,1]) = [0,1] + Note: in case the input contains a non binary element then the generated system of constraints does not have any solution for that input. + For instance, AddBinaryArrayTag(2)([0,10]) -> no solution + +*/ + +template AddBinaryArrayTag(n) { + signal input in[n]; + signal output {binary} out[n]; + + for (var i = 0; i < n; i++) { + in[i] * (in[i] - 1) === 0; + } + out <== in; +} + +/* +*** ForceBinary(n): template that adds the tag binary to a signal without adding the constraints needed to ensure that the signal is binary + - Inputs: in -> field value + - Output: out -> same value as in, but including binary tag + DOES NOT CHECK THAT out satisfies tag binary + + Example: ForceBinary()(5) = 5 + Note: the template does not check if the input actually satisfies the binary specification --> Use carefully, potentially dangerous template + For instance, ForceBinary()(10) = 10, including tag binary + +*/ + +template ForceBinary() { + signal input in; + signal output {binary} out; + + out <== in; +} + +/* +*** ForceBinaryArray(n): template that adds the tag binary to an array of n signals without adding the constraints needed to ensure that the signals are binary + - Inputs: in[n] -> array of field values + - Output: out[n] -> same value as in, but including binary tag + DOES NOT CHECK THAT out satisfies tag binary + + Example: ForceBinaryArray()([1,0]) = [1, 0], including tag binary + Note: the template does not check if the input actually satisfies the binary specification --> Use carefully, potentially dangerous template + For instance, ForceBinaryArray()([0, 10]) = [0, 10], including tag binary + +*/ + +template ForceBinaryArray(n) { + signal input in[n]; + signal output {binary} out[n]; + + out <== in; +} + +/* +*** AddMaxbitTag(n): template that adds the constraints needed to ensure that a signal can be expressed using n bits(that is value is in [0, 2**n)) and adds the tag maxbit = n to the input + - Inputs: in -> field value + - Output: out -> same value as in, but including maxbit tag with out.maxbit = n + satisfies tag out.maxbit = n + + Example: AddMaxbitTag(5)(14) = 14 + Note: in case the input in does not satisfy the specification of maxbit then the generated system of constraints does not have any solution for that input. + For instance, AddMaxbitTag(3)(100) -> no solution + +*/ + +template AddMaxbitTag(n) { + signal input in; + signal output {maxbit} out; + + _ <== Num2Bits(n)(in); + + out.maxbit = n; + out <== in; +} + +/* +*** AddMaxbitArrayTag(n): template that adds the constraints needed to ensure that the signals an array of length m can be expressed using n bits(that is, that is value is in [0, 2**n)) and adds the tag maxbit = n to the input + - Inputs: in[m] -> array of m field values + - Output: out[m] -> same values as in, but including maxbit tag with out.maxbit = n + satisfies tag out.maxbit = n + + Example: AddMaxbitTag(5, 2)([3,14]) = [3, 14] with tag maxbit = 5 + Note: in case the a signal of the input in does not satisfy the specification of maxbit then the generated system of constraints does not have any solution for that input. + For instance, AddMaxbitTag(3, 2)([3, 100]) -> no solution + +*/ + + +template AddMaxbitArrayTag(n,m) { + signal input in[m]; + signal output {maxbit} out[m]; + + out.maxbit = n; + + for (var i = 0; i < m; i++) { + _ <== Num2Bits(n)(in[i]); + } + + in ==> out; +} + +/* +*** ForceMaxbitTag(n): template that adds the tag maxbit = n to the input without adding any constraints to ensure that the input especification of the tag + - Inputs: in -> field value + - Output: out -> same value as in, but including maxbit tag with out.maxbit = n + DOES NOT CHECK THAT out satisfies tag out.maxbit = n + + Note: the template does not check if the input actually satisfies the maxbit specification --> Use carefully, potentially dangerous template + For instance, ForceMaxbitTag(2)(10) = 10, including tag maxbit = 2 + +*/ + + +template ForceMaxbit(n) { + signal input in; + signal output {maxbit} out; + + out.maxbit = n; + in ==> out; +} + +/* +*** ForceMaxbitArrayTag(n, m): template that adds the tag maxbit = n to the input that is an array of m signals without adding any constraints to ensure that the inputs especification of the tag + - Inputs: in[m] -> field value + - Output: out[m] -> same value as in, but including maxbit tag with out.maxbit = n + DOES NOT CHECK THAT out satisfies tag out.maxbit = n + + Note: the template does not check if the input actually satisfies the maxbit specification --> Use carefully, potentially dangerous template + For instance, ForceMaxbitTag(2,2)([0,10]) = [0,10], including tag maxbit = 2 + +*/ + +template ForceMaxbitArray(n,m) { + signal input in[m]; + signal output {maxbit} out[m]; + + out.maxbit = n; + + in ==> out; +} + + +/* +*** AddMaxValueTag(n): template that adds the constraints needed to ensure that a signal is smaller or equal than a given value n and adds the tag max = n to the input + - Inputs: in -> field value + - Output: out -> same value as in, but including max tag with out.max = n + satisfies tag out.max = n + + Example: AddMaxValueTag(15)(14) = 14 and can be satisfied + Note: in case the input in does not satisfy the specification of max then the generated system of constraints does not have any solution for that input. + For instance, AddMaxValueTag(3)(100) -> no solution + +*/ + +template AddMaxValueTag(n) { + signal input in; + signal output {max} out; + + signal {maxbit} aux[2]; + aux.maxbit = nbits(n); + aux[0] <== AddMaxbitTag(nbits(n))(in); // to ensure the correct size + aux[1] <== n; + + signal out1 <== LessEqThan(n)(aux); + out1 === 1; + out.max = n; + out <== in; +} + +/* +*** AddMaxAbsValueTag(n): template that adds the constraints needed to ensure that the absolute value of a signal is smaller or equal than a given value n and adds the tag max_abs = n to the input + - Inputs: in -> field value + - Output: out -> same value as in, but including max_abs tag with out.max_abs = n + satisfies tag out.max_abs = n + + Example: AddMaxValueTag(15)(-14) = 14 and can be satisfied + Note: in case the input in does not satisfy the specification of max_abs then the generated system of constraints does not have any solution for that input. + For instance, AddMaxAbsValueTag(33)(-100) -> no solution + +*/ + +template AddMaxAbsValueTag(n){ + signal input in; + signal output {max_abs} out; + + var needed_bits = nbits(2 * n); + + signal {maxbit} aux[2]; + aux.maxbit = needed_bits; + aux[0] <== AddMaxbitTag(needed_bits)(in + n); // to ensure that 0 <= aux[0] < 2**nbits(2 * n) + aux[1] <== 2 * n; + + signal out1 <== LessEqThan(n)(aux); // checks that 0 <= in + n <= 2 * n <==> -n <= in <= n + out1 === 1; + + out.max_abs = n; + out <== in; +} + + diff --git a/circuits/lib/utils/treeUtils.circom b/circuits/lib/utils/treeUtils.circom index 09101a4a..15292161 100644 --- a/circuits/lib/utils/treeUtils.circom +++ b/circuits/lib/utils/treeUtils.circom @@ -10,7 +10,7 @@ include "claimUtils.circom"; // checkClaimExists verifies that claim is included into the claim tree root template checkClaimExists(IssuerLevels) { - signal input enabled; + signal input {binary} enabled; signal input claimHi; signal input claimHv; signal input claimMTP[IssuerLevels]; @@ -30,7 +30,7 @@ template checkClaimExists(IssuerLevels) { } template checkClaimNotRevoked(treeLevels) { - signal input enabled; + signal input {binary} enabled; signal input claim[8]; signal input claimNonRevMTP[treeLevels]; signal input treeRoot; @@ -56,7 +56,7 @@ template checkClaimNotRevoked(treeLevels) { // checkIdenStateMatchesRoots checks that a hash of 3 tree // roots is equal to expected identity state template checkIdenStateMatchesRoots() { - signal input enabled; + signal input {binary} enabled; signal input claimsTreeRoot; signal input revTreeRoot; signal input rootsTreeRoot; @@ -76,7 +76,7 @@ template checkIdenStateMatchesRoots() { // verifyClaimIssuance verifies that claim is issued by the issuer template verifyClaimIssuance(IssuerLevels) { - signal input enabled; + signal input {binary} enabled; signal input claimHi; signal input claimHv; signal input claimIssuanceMtp[IssuerLevels]; @@ -105,7 +105,7 @@ template verifyClaimIssuance(IssuerLevels) { } template VerifyAuthClaimAndSignature(nLevels) { - signal input enabled; + signal input {binary} enabled; signal input claimsTreeRoot; signal input authClaimMtp[nLevels]; diff --git a/circuits/offchain/credentialAtomicQueryV3OffChain.circom b/circuits/offchain/credentialAtomicQueryV3OffChain.circom index 03b29aea..8406f7d5 100644 --- a/circuits/offchain/credentialAtomicQueryV3OffChain.circom +++ b/circuits/offchain/credentialAtomicQueryV3OffChain.circom @@ -11,10 +11,11 @@ include "../lib/query/processQueryWithModifiers.circom"; include "../lib/utils/nullify.circom"; include "../lib/utils/idUtils.circom"; include "../lib/utils/safeOne.circom"; +include "../lib/utils/tags-managing.circom"; template credentialAtomicQueryV3OffChain(issuerLevels, claimLevels, maxValueArraySize) { // common outputs for Sig and MTP - signal output merklized; + signal output {binary} merklized; signal output userID; // common inputs for Sig and MTP @@ -95,7 +96,7 @@ template credentialAtomicQueryV3OffChain(issuerLevels, claimLevels, maxValueArra signal output operatorOutput; // get safe one values to be used in ForceEqualIfEnabled - signal one <== SafeOne()(userGenesisID); + signal {binary} one <== SafeOne()(userGenesisID); ///////////////////////////////////////////////////////////////// // Claim Verification (id, schema, expiration, issuance, revocation) @@ -119,8 +120,8 @@ template credentialAtomicQueryV3OffChain(issuerLevels, claimLevels, maxValueArra // verify issuerClaim expiration time verifyExpirationTime()(issuerClaimHeader.claimFlags[3], issuerClaim, timestamp); // 322 constraints - signal isSig <== IsEqual()([proofType, 1]); - signal isMTP <== IsEqual()([proofType, 2]); + signal {binary} isSig <== IsEqual()([proofType, 1]); + signal {binary} isMTP <== IsEqual()([proofType, 2]); signal validProofType <== OR()(isSig, isMTP); ForceEqualIfEnabled()(one, [validProofType, 1]); @@ -172,9 +173,11 @@ template credentialAtomicQueryV3OffChain(issuerLevels, claimLevels, maxValueArra claimIssuanceIdenState <== issuerState ); // 11184 constraints + signal {binary} safeIsRevocationChecked <== AddBinaryTag()(isRevocationChecked); + // check claim is not revoked checkClaimNotRevoked(issuerLevels)( - enabled <== isRevocationChecked, + enabled <== safeIsRevocationChecked, claim <== issuerClaim, claimNonRevMTP <== issuerClaimNonRevMtp, noAux <== issuerClaimNonRevMtpNoAux, @@ -187,7 +190,7 @@ template credentialAtomicQueryV3OffChain(issuerLevels, claimLevels, maxValueArra // 1. if Sig proof is provided we need to check non revocation of authClaim always // AND non revocation of issuerClaim only if isRevocationChecked = 1 // 2. if MTP proof is provided we need to check non revocation of claim only if isRevocationChecked = 1 - signal checkIssuerClaimNonRevState <== OR()(isSig, isRevocationChecked); + signal {binary} checkIssuerClaimNonRevState <== OR()(isSig, safeIsRevocationChecked); // verify issuer state for claim non-revocation proof checkIdenStateMatchesRoots()( @@ -250,7 +253,7 @@ template credentialAtomicQueryV3OffChain(issuerLevels, claimLevels, maxValueArra } template sigFlow(issuerLevels) { - signal input enabled; + signal input {binary} enabled; signal input issuerAuthClaim[8]; signal input issuerAuthClaimNonRevMtp[issuerLevels]; signal input issuerAuthClaimNonRevMtpNoAux; diff --git a/circuits/onchain/credentialAtomicQueryV3OnChain.circom b/circuits/onchain/credentialAtomicQueryV3OnChain.circom index b3aca1b5..92548dfd 100644 --- a/circuits/onchain/credentialAtomicQueryV3OnChain.circom +++ b/circuits/onchain/credentialAtomicQueryV3OnChain.circom @@ -10,6 +10,7 @@ include "../lib/utils/idUtils.circom"; include "../lib/utils/spongeHash.circom"; include "../offchain/credentialAtomicQueryV3OffChain.circom"; include "../lib/utils/queryHash.circom"; +include "../lib/utils/tags-managing.circom"; /** credentialAtomicQueryV3OnChain.circom - query claim value and verify claim issuer signature or mtp: @@ -168,16 +169,18 @@ template credentialAtomicQueryV3OnChain(issuerLevels, claimLevels, maxValueArray // on root stored in the index or value slot // if it is not set verification is performed on according to the slotIndex. Value selected from the // provided slot. For example if slotIndex is `1` value gets from `i_1` slot. If `4` from `v_1`. - signal merklized; + signal {binary} merklized; ///////////////////////////////////////////////////////////////// // Auth check ///////////////////////////////////////////////////////////////// - ForceEqualIfEnabled()(NOT()(isBJJAuthEnabled), [profileNonce, 0]); + signal {binary} safeIsBJJAuthEnabled <== AddBinaryTag()(isBJJAuthEnabled); + + ForceEqualIfEnabled()(NOT()(safeIsBJJAuthEnabled), [profileNonce, 0]); checkAuthV2(idOwnershipLevels, onChainLevels)( - isBJJAuthEnabled, // enabled + safeIsBJJAuthEnabled, // enabled userGenesisID, userState, // user state userClaimsTreeRoot, diff --git a/test/circuits/utils/claimUtils_getClaimMerklizeRoot.circom b/test/circuits/utils/claimUtils_getClaimMerklizeRoot.circom index 98290d70..8ed00360 100644 --- a/test/circuits/utils/claimUtils_getClaimMerklizeRoot.circom +++ b/test/circuits/utils/claimUtils_getClaimMerklizeRoot.circom @@ -1,5 +1,19 @@ pragma circom 2.1.1; include "../../../circuits/lib/utils/claimUtils.circom"; +include "../../../circuits/lib/utils/tags-managing.circom"; -component main = getClaimMerklizeRoot(); +template wrappedGetClaimMerklizeRoot() { + signal input claim[8]; + signal input claimFlags[32]; + signal output flag; + signal output out; + + component check = getClaimMerklizeRoot(); + check.claim <== claim; + check.claimFlags <== AddBinaryArrayTag(32)(claimFlags); + flag <== check.flag; + out <== check.out; +} + +component main = wrappedGetClaimMerklizeRoot(); diff --git a/test/circuits/utils/utils_checkIdenStateMatchesRoots.circom b/test/circuits/utils/utils_checkIdenStateMatchesRoots.circom index 9a728498..df93fa85 100644 --- a/test/circuits/utils/utils_checkIdenStateMatchesRoots.circom +++ b/test/circuits/utils/utils_checkIdenStateMatchesRoots.circom @@ -2,5 +2,21 @@ pragma circom 2.1.1; include "../../../circuits/lib/utils/claimUtils.circom"; include "../../../circuits/lib/utils/treeUtils.circom"; +include "../../../circuits/lib/utils/tags-managing.circom"; -component main = checkIdenStateMatchesRoots(); +template wrappedCheckIdenStateMatchesRoots(){ + signal input enabled; + signal input claimsTreeRoot; + signal input revTreeRoot; + signal input rootsTreeRoot; + signal input expectedState; + + component check = checkIdenStateMatchesRoots(); + check.enabled <== AddBinaryTag()(enabled); + check.claimsTreeRoot <== claimsTreeRoot; + check.revTreeRoot <== revTreeRoot; + check.rootsTreeRoot <== rootsTreeRoot; + check.expectedState <== expectedState; +} + +component main = wrappedCheckIdenStateMatchesRoots(); diff --git a/test/circuits/utils/utils_getClaimSubjectOtherIden.circom b/test/circuits/utils/utils_getClaimSubjectOtherIden.circom index 015b9527..eb25e21c 100644 --- a/test/circuits/utils/utils_getClaimSubjectOtherIden.circom +++ b/test/circuits/utils/utils_getClaimSubjectOtherIden.circom @@ -1,5 +1,16 @@ pragma circom 2.1.1; -include "../../../circuits/lib/utils/claimUtilsWrappers.circom"; +include "../../../circuits/lib/utils/claimUtils.circom"; + +template getClaimSubjectOtherIdenWrapper() { + signal input claim[8]; + signal output id; + + // get header flags from claim. + component header = getClaimHeader(); + header.claim <== claim; + + id <== getClaimSubjectOtherIden()(claim, header.claimFlags); +} component main{public[claim]} = getClaimSubjectOtherIdenWrapper(); diff --git a/test/circuits/utils/utils_getSubjectLocation.circom b/test/circuits/utils/utils_getSubjectLocation.circom index 8b4f3710..4d39620b 100644 --- a/test/circuits/utils/utils_getSubjectLocation.circom +++ b/test/circuits/utils/utils_getSubjectLocation.circom @@ -1,5 +1,15 @@ pragma circom 2.1.1; include "../../../circuits/lib/utils/claimUtils.circom"; +include "../../../circuits/lib/utils/tags-managing.circom"; -component main = getSubjectLocation(); +template wrappedGetSubjectLocation() { + signal input claimFlags[32]; + signal output out; + + component check = getSubjectLocation(); + check.claimFlags <== AddBinaryArrayTag(32)(claimFlags); + out <== check.out; +} + +component main = wrappedGetSubjectLocation(); diff --git a/test/circuits/utils/utils_isExpirable.circom b/test/circuits/utils/utils_isExpirable.circom index 6b551081..d65e85c4 100644 --- a/test/circuits/utils/utils_isExpirable.circom +++ b/test/circuits/utils/utils_isExpirable.circom @@ -1,5 +1,15 @@ pragma circom 2.1.1; include "../../../circuits/lib/utils/claimUtils.circom"; +include "../../../circuits/lib/utils/tags-managing.circom"; -component main = isExpirable(); +template wrappedIsExpirable() { + signal input claimFlags[32]; + signal output out; + + component check = isExpirable(); + check.claimFlags <== AddBinaryArrayTag(32)(claimFlags); + out <== check.out; +} + +component main = wrappedIsExpirable(); diff --git a/test/circuits/utils/utils_isUpdatable.circom b/test/circuits/utils/utils_isUpdatable.circom index 1d04f3d7..28b2ca12 100644 --- a/test/circuits/utils/utils_isUpdatable.circom +++ b/test/circuits/utils/utils_isUpdatable.circom @@ -1,5 +1,15 @@ pragma circom 2.1.1; include "../../../circuits/lib/utils/claimUtils.circom"; +include "../../../circuits/lib/utils/tags-managing.circom"; -component main = isUpdatable(); +template wrappedIsUpdatable() { + signal input claimFlags[32]; + signal output out; + + component check = isUpdatable(); + check.claimFlags <== AddBinaryArrayTag(32)(claimFlags); + out <== check.out; +} + +component main = wrappedIsUpdatable(); diff --git a/test/circuits/utils/utils_verifyClaimSignature.circom b/test/circuits/utils/utils_verifyClaimSignature.circom index 2cf86a28..c34db683 100644 --- a/test/circuits/utils/utils_verifyClaimSignature.circom +++ b/test/circuits/utils/utils_verifyClaimSignature.circom @@ -1,5 +1,25 @@ pragma circom 2.1.1; include "../../../circuits/lib/utils/claimUtils.circom"; +include "../../../circuits/lib/utils/tags-managing.circom"; -component main = verifyClaimSignature(); +template wrappedVerifyClaimSignature(){ + signal input enabled; + signal input claimHash; + signal input sigR8x; + signal input sigR8y; + signal input sigS; + signal input pubKeyX; + signal input pubKeyY; + + component check = verifyClaimSignature(); + check.enabled <== AddBinaryTag()(enabled); + check.claimHash <== claimHash; + check.sigR8x <== sigR8x; + check.sigR8y <== sigR8y; + check.sigS <== sigS; + check.pubKeyX <== pubKeyX; + check.pubKeyY <== pubKeyY; +} + +component main = wrappedVerifyClaimSignature(); diff --git a/test/circuits/utils/utils_verifyExpirationTime.circom b/test/circuits/utils/utils_verifyExpirationTime.circom index 621cf4f5..1c0fbfb7 100644 --- a/test/circuits/utils/utils_verifyExpirationTime.circom +++ b/test/circuits/utils/utils_verifyExpirationTime.circom @@ -1,5 +1,17 @@ pragma circom 2.1.1; include "../../../circuits/lib/utils/claimUtils.circom"; +include "../../../circuits/lib/utils/tags-managing.circom"; -component main = verifyExpirationTime(); +template wrappedVerifyExpirationTime() { + signal input expirationFlag; // claimFlags[3] (expiration flag) is set + signal input claim[8]; + signal input timestamp; + + component check = verifyExpirationTime(); + check.expirationFlag <== AddBinaryTag()(expirationFlag); + check.claim <== claim; + check.timestamp <== timestamp; +} + +component main = wrappedVerifyExpirationTime();