diff --git a/circuits/test/vfy_nullifier.test.ts b/circuits/test/vfy_nullifier.test.ts index 1afc1ed..6b73b67 100644 --- a/circuits/test/vfy_nullifier.test.ts +++ b/circuits/test/vfy_nullifier.test.ts @@ -35,10 +35,6 @@ describe("Nullifier Circuit", () => { hashedToCurveR, ] - const v1_sha256_preimage_bits = bufToSha256PaddedBitArr(Buffer.from( - concatUint8Arrays(sha_preimage_points.map((point) => point.toRawBytes(true))) - )); - const v1_sha256_preimage_bit_length = parseInt(v1_sha256_preimage_bits.slice(-64), 2) const v1_binary_c = BigInt("0x" + c_v1).toString(2).split('').map(Number); @@ -63,7 +59,7 @@ describe("Nullifier Circuit", () => { const p = join(__dirname, '12_point_sha_256_test.circom') const circuit = await wasm_tester(p, {"json":true, "sym": true}) - const w = await circuit.calculateWitness({coordinates, preimage_bit_length: v1_sha256_preimage_bit_length}, true) + const w = await circuit.calculateWitness({coordinates}, true) await circuit.checkConstraints(w); await circuit.assertOut(w, {out: v1_binary_c}) }) @@ -113,7 +109,6 @@ describe("Nullifier Circuit", () => { pk: pointToCircuitValue(testPublicKeyPoint), nullifier: pointToCircuitValue(nullifier), ...htci, - sha256_preimage_bit_length: v1_sha256_preimage_bit_length, }) await circuit.checkConstraints(w) }) diff --git a/circuits/verify_nullifier.circom b/circuits/verify_nullifier.circom index 7af8af3..af64e29 100644 --- a/circuits/verify_nullifier.circom +++ b/circuits/verify_nullifier.circom @@ -6,6 +6,7 @@ include "./node_modules/circom-ecdsa/circuits/secp256k1_func.circom"; include "./node_modules/secp256k1_hash_to_curve_circom/circom/hash_to_curve.circom"; include "./node_modules/secp256k1_hash_to_curve_circom/circom/Sha256.circom"; include "./node_modules/circomlib/circuits/bitify.circom"; +include "./node_modules/circomlib/circuits/comparators.circom"; // Verifies that a nullifier belongs to a specific public key \ // This blog explains the intuition behind the construction https://blog.aayushg.com/posts/nullifier @@ -29,9 +30,6 @@ template plume_v1(n, k, message_length) { signal input q1_x_mapped[4]; signal input q1_y_mapped[4]; - // precomputed value for the sha256 component. TODO: calculate internally in circom to simplify API - signal input sha256_preimage_bit_length; - component check_ec_equations = check_ec_equations(n, k, message_length); check_ec_equations.c <== c; @@ -58,7 +56,6 @@ template plume_v1(n, k, message_length) { var g[2][100]; g[0] = get_genx(n, k); g[1] = get_geny(n, k); - c_sha256.preimage_bit_length <== sha256_preimage_bit_length; for (var i = 0; i < 2; i++) { for (var j = 0; j < k; j++) { @@ -259,7 +256,6 @@ template a_div_b_pow_c(n, k) { template sha256_12_coordinates(n, k) { signal input coordinates[12][k]; - signal input preimage_bit_length; signal output out[256]; // compress coordinates @@ -270,16 +266,27 @@ template sha256_12_coordinates(n, k) { compressors[i].uncompressed[1] <== coordinates[2*i + 1]; } + // preimage bit length accumulator + var preimage_bit_len = 0; + // decompose coordinates inputs into binary component binary[6*33]; for (var i = 0; i < 6; i++) { // for each compressor for (var j = 0; j < 33; j++) { // for each byte binary[33*i + j] = Num2Bits(8); binary[33*i + j].in <== compressors[i].compressed[j]; + preimage_bit_len += 1; } } var message_bits = 6*33*8; // 6 compressed coordinates of 33 bytes + + // check whether the preimage bit length is equal to the message bits + component is_eq = IsEqual(); + is_equal.in[0] <== message_bits; + is_equal.in[1] <== preimage_bit_len; + is_equal.out === 1; + var total_bits = (message_bits \ 512) * 512; if (message_bits % 512 != 0) { total_bits += 512; @@ -296,7 +303,7 @@ template sha256_12_coordinates(n, k) { sha256.msg[i] <== 0; } - // Message is padded with 1, a series of 0s, then the bit length of the message https://en.wikipedia.org/wiki/SHA-2#Pseudocode:~:text=append%20a%20single%20%271%27%20bit + // Message is padded with 1, a series of 0s, then the bit length of the message https://en.wikipedia.org/wiki/SHA-2#Pseudocode // TODO: move padding calculating into upstream repo to simplify API for (var i = 0; i < total_bits - 64; i++) { if (i == 1584) { @@ -307,7 +314,7 @@ template sha256_12_coordinates(n, k) { } component bit_length_binary = Num2Bits(64); - bit_length_binary.in <== preimage_bit_length; + bit_length_binary.in <== preimage_bit_len; for (var i = 0; i < 64; i++) { sha256.padded_bits[total_bits - i - 1] <== bit_length_binary.out[i]; } diff --git a/rust-k256/Cargo.toml b/rust-k256/Cargo.toml index 87165e9..860a5e6 100644 --- a/rust-k256/Cargo.toml +++ b/rust-k256/Cargo.toml @@ -13,8 +13,7 @@ hex-literal = "0.3.4" hash2field = "0.4.0" num-bigint = "0.4.3" num-integer = "0.1.45" -k256 = {version = "0.11.3", features = ["arithmetic", "hash2curve", "expose-field", "sha2"]} -elliptic-curve = { version = "0.12.2", features = ["arithmetic"]} +k256 = {version = "0.13.2", features = ["arithmetic", "hash2curve", "expose-field", "sha2"]} [dev-dependencies] hex = "0.4.3" diff --git a/rust-k256/src/lib.rs b/rust-k256/src/lib.rs index a92153d..0b4ccda 100644 --- a/rust-k256/src/lib.rs +++ b/rust-k256/src/lib.rs @@ -1,19 +1,16 @@ // #![feature(generic_const_expr)] // #![allow(incomplete_features)] -use elliptic_curve::bigint::ArrayEncoding; -use elliptic_curve::hash2curve::{ExpandMsgXmd, GroupDigest}; -use elliptic_curve::ops::Reduce; -use elliptic_curve::sec1::ToEncodedPoint; use k256::{ // ecdsa::{signature::Signer, Signature, SigningKey}, elliptic_curve::group::ff::PrimeField, + elliptic_curve::hash2curve::{ExpandMsgXmd, GroupDigest}, + elliptic_curve::sec1::ToEncodedPoint, sha2::{digest::Output, Digest, Sha256}, FieldBytes, ProjectivePoint, Scalar, Secp256k1, - U256, }; // requires 'getrandom' feature const L: usize = 48; @@ -73,7 +70,7 @@ fn hash_to_curve(m: &[u8], pk: &ProjectivePoint) -> ProjectivePoint { let pt: ProjectivePoint = Secp256k1::hash_from_bytes::>( &[[m, &encode_pt(pk).unwrap()].concat().as_slice()], //b"CURVE_XMD:SHA-256_SSWU_RO_", - DST, + &[DST], ) .unwrap(); pt @@ -106,7 +103,7 @@ impl PlumeSignature<'_> { // don't forget to check `c` is `Output` in the #API let c = Output::::from_slice(self.c); // TODO should we allow `c` input greater than BaseField::MODULUS? - let c_scalar = &Scalar::from_uint_reduced(U256::from_be_byte_array(c.to_owned())); + let c_scalar = &Scalar::from_repr(c.to_owned()).unwrap(); /* @skaunov would be glad to discuss with @Divide-By-0 excessive of the following check. Though I should notice that it at least doesn't breaking anything. */ if c_scalar.is_zero().into() { @@ -210,7 +207,7 @@ mod tests { let pt: ProjectivePoint = Secp256k1::hash_from_bytes::>( &[s], //b"CURVE_XMD:SHA-256_SSWU_RO_" - DST, + &[DST], ) .unwrap(); pt @@ -302,7 +299,7 @@ mod tests { }; dbg!(&c, version); - let c_scalar = &Scalar::from_uint_reduced(U256::from_be_byte_array(c.clone())); + let c_scalar = &Scalar::from_repr(c.clone()).unwrap(); // This value is part of the discrete log equivalence (DLEQ) proof. let r_sk_c = r + sk * c_scalar;