diff --git a/README.md b/README.md index f2b900b..eac748c 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,9 @@ If you would like to get a grant to create PLUME applications or improve the lib ## Contributions -If you'd like to contribute, we offer $50 bounties in Eth/DAI for resolving any of the bugs in our issues! Each of them is quite small. That includes [#28](https://github.com/plume-sig/zk-nullifier-sig/issues/28), [#24](https://github.com/plume-sig/zk-nullifier-sig/issues/24), [#22](https://github.com/plume-sig/zk-nullifier-sig/issues/22), [#14](https://github.com/plume-sig/zk-nullifier-sig/issues/14),and [#13](https://github.com/plume-sig/zk-nullifier-sig/issues/13). +If you'd like to contribute, we offer $50 bounties in Eth/DAI for resolving any of the bugs in our issues! Each of them is quite small. That includes +[#28](https://github.com/plume-sig/zk-nullifier-sig/issues/28), [#24](https://github.com/plume-sig/zk-nullifier-sig/issues/24), +[#14](https://github.com/plume-sig/zk-nullifier-sig/issues/14),and [#13](https://github.com/plume-sig/zk-nullifier-sig/issues/13). ## Implementations diff --git a/circuits/test/vfy_nullifier.test.ts b/circuits/test/vfy_nullifier.test.ts index 3f6a38e..1afc1ed 100644 --- a/circuits/test/vfy_nullifier.test.ts +++ b/circuits/test/vfy_nullifier.test.ts @@ -2,7 +2,7 @@ import { join } from 'path'; import { wasm as wasm_tester } from 'circom_tester' import { describe, expect, test } from '@jest/globals'; import { hexToBigInt } from "../../javascript/src/utils/encoding"; -import { c_v1, c_v2, gPowR, hashMPk, hashMPkPowR, nullifier, s_v1, s_v2, testMessage, testPublicKey, testPublicKeyPoint, testR, testSecretKey } from "../../javascript/test/test_consts" +import { c_v1, c_v2, rPoint, hashMPk, hashedToCurveR, nullifier, s_v1, s_v2, testMessage, testPublicKey, testPublicKeyPoint, testR, testSecretKey } from "../../javascript/test/test_consts" import { Point } from "../../javascript/node_modules/@noble/secp256k1"; import { generate_inputs_from_array } from "secp256k1_hash_to_curve_circom/ts/generate_inputs"; import { bufToSha256PaddedBitArr } from "secp256k1_hash_to_curve_circom/ts/utils"; @@ -22,15 +22,17 @@ describe("Nullifier Circuit", () => { hexToBigInt(hashMPk.x.toString()), hexToBigInt(hashMPk.y.toString()) ) - const hash_to_curve_inputs = utils.stringifyBigInts(generate_inputs_from_array(message_bytes.concat(public_key_bytes))); + const hash_to_curve_inputs = utils.stringifyBigInts(generate_inputs_from_array( + message_bytes.concat(public_key_bytes) + )); var sha_preimage_points: Point[] = [ Point.BASE, testPublicKeyPoint, hashMPkPoint, nullifier, - gPowR, - hashMPkPowR, + rPoint, + hashedToCurveR, ] const v1_sha256_preimage_bits = bufToSha256PaddedBitArr(Buffer.from( @@ -107,12 +109,11 @@ describe("Nullifier Circuit", () => { // Main circuit inputs c: scalarToCircuitValue(hexToBigInt(c_v1)), s: scalarToCircuitValue(hexToBigInt(s_v1)), - msg: message_bytes, - public_key: pointToCircuitValue(testPublicKeyPoint), + plume_message: message_bytes, + pk: pointToCircuitValue(testPublicKeyPoint), nullifier: pointToCircuitValue(nullifier), ...htci, sha256_preimage_bit_length: v1_sha256_preimage_bit_length, - }) await circuit.checkConstraints(w) }) @@ -127,24 +128,29 @@ describe("Nullifier Circuit", () => { // Main circuit inputs c: scalarToCircuitValue(hexToBigInt(c_v2)), s: scalarToCircuitValue(hexToBigInt(s_v2)), - msg: message_bytes, - public_key: pointToCircuitValue(testPublicKeyPoint), + plume_message: message_bytes, + pk: pointToCircuitValue(testPublicKeyPoint), nullifier: pointToCircuitValue(nullifier), ...htci, }) await circuit.checkConstraints(w) - // assertOut builds a huge json string containing the whole witness and fails with "Cannot create a string longer than 0x1fffffe8 characters" - // Instead we just slice into the witness, and the outputs start at 1 (where 0 always equals 1 due to a property of the underlying proof system) - expect(w.slice(1, 5)).toEqual(pointToCircuitValue(gPowR)[0]) - expect(w.slice(5, 9)).toEqual(pointToCircuitValue(gPowR)[1]) - expect(w.slice(9, 13)).toEqual(pointToCircuitValue(hashMPkPowR)[0]) - expect(w.slice(13, 17)).toEqual(pointToCircuitValue(hashMPkPowR)[1]) + /* assertOut builds a huge json string containing the whole witness and fails + with "Cannot create a string longer than 0x1fffffe8 characters" */ + /* Instead we just slice into the witness, and the outputs start at 1 + (where 0 always equals 1 due to a property of the underlying proof system) */ + expect(w.slice(1, 5)).toEqual(pointToCircuitValue(rPoint)[0]) + expect(w.slice(5, 9)).toEqual(pointToCircuitValue(rPoint)[1]) + expect(w.slice(9, 13)).toEqual(pointToCircuitValue(hashedToCurveR)[0]) + expect(w.slice(13, 17)).toEqual(pointToCircuitValue(hashedToCurveR)[1]) // In v2 we check the challenge point c outside the circuit - // Note, in a real application you would get the nullifier, g^r, and h^r as public outputs/inputs of the proof - expect(createHash("sha256") - .update(concatUint8Arrays([nullifier.toRawBytes(true), gPowR.toRawBytes(true), hashMPkPowR.toRawBytes(true)])) - .digest('hex')).toEqual(c_v2) + /* Note, in a real application you would get the nullifier, + g^r, and h^r as public outputs/inputs of the proof */ + expect( + createHash("sha256").update(concatUint8Arrays([ + nullifier.toRawBytes(true), rPoint.toRawBytes(true), hashedToCurveR.toRawBytes(true) + ])).digest('hex') + ).toEqual(c_v2) }) // This tests that our circuit correctly computes g^s/(g^sk)^c = g^r, and that the first two equations are @@ -156,7 +162,7 @@ describe("Nullifier Circuit", () => { // Verify that gPowS/pkPowC = gPowR outside the circuit, as a sanity check const gPowS = Point.fromPrivateKey(s_v1); const pkPowC = testPublicKeyPoint.multiply(hexToBigInt(c_v1)) - expect(gPowS.add(pkPowC.negate()).equals(gPowR)).toBe(true); + expect(gPowS.add(pkPowC.negate()).equals(rPoint)).toBe(true); // Verify that circuit calculates g^s / pk^c = g^r const w = await circuit.calculateWitness({ @@ -165,7 +171,7 @@ describe("Nullifier Circuit", () => { c: scalarToCircuitValue(hexToBigInt(c_v1)), }) await circuit.checkConstraints(w) - await circuit.assertOut(w, {out: pointToCircuitValue(gPowR)}); + await circuit.assertOut(w, {out: pointToCircuitValue(rPoint)}); }); test("bigint <-> register conversion", async () => { diff --git a/circuits/verify_nullifier.circom b/circuits/verify_nullifier.circom index 52898f6..7af8af3 100644 --- a/circuits/verify_nullifier.circom +++ b/circuits/verify_nullifier.circom @@ -7,13 +7,13 @@ include "./node_modules/secp256k1_hash_to_curve_circom/circom/hash_to_curve.circ include "./node_modules/secp256k1_hash_to_curve_circom/circom/Sha256.circom"; include "./node_modules/circomlib/circuits/bitify.circom"; -// Verifies that a nullifier belongs to a specific public key +// Verifies that a nullifier belongs to a specific public key \ // This blog explains the intuition behind the construction https://blog.aayushg.com/posts/nullifier -template plume_v1(n, k, msg_length) { +template plume_v1(n, k, message_length) { signal input c[k]; signal input s[k]; - signal input msg[msg_length]; - signal input public_key[2][k]; + signal input plume_message[message_length]; + signal input pk[2][k]; signal input nullifier[2][k]; // precomputed values for the hash_to_curve component @@ -32,14 +32,14 @@ template plume_v1(n, k, msg_length) { // 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, msg_length); + component check_ec_equations = check_ec_equations(n, k, message_length); check_ec_equations.c <== c; check_ec_equations.s <== s; - check_ec_equations.public_key <== public_key; + check_ec_equations.pk <== pk; check_ec_equations.nullifier <== nullifier; - check_ec_equations.msg <== msg; + check_ec_equations.plume_message <== plume_message; check_ec_equations.q0_gx1_sqrt <== q0_gx1_sqrt; check_ec_equations.q0_gx2_sqrt <== q0_gx2_sqrt; @@ -63,11 +63,11 @@ template plume_v1(n, k, msg_length) { for (var i = 0; i < 2; i++) { for (var j = 0; j < k; j++) { c_sha256.coordinates[i][j] <== g[i][j]; - c_sha256.coordinates[2+i][j] <== public_key[i][j]; - c_sha256.coordinates[4+i][j] <== check_ec_equations.h[i][j]; + c_sha256.coordinates[2+i][j] <== pk[i][j]; + c_sha256.coordinates[4+i][j] <== check_ec_equations.hashed_to_curve[i][j]; c_sha256.coordinates[6+i][j] <== nullifier[i][j]; - c_sha256.coordinates[8+i][j] <== check_ec_equations.g_pow_r[i][j]; - c_sha256.coordinates[10+i][j] <== check_ec_equations.h_pow_r[i][j]; + c_sha256.coordinates[8+i][j] <== check_ec_equations.r_point[i][j]; + c_sha256.coordinates[10+i][j] <== check_ec_equations.hashed_to_curve_r[i][j]; } } @@ -90,17 +90,17 @@ template plume_v1(n, k, msg_length) { } // v2 is the same as v1, except that the sha256 check is done outside the circuit. -// We output g_pow_r and h_pow_r as public values so that the verifier can calculate the hash themselves. +// We output `r_point` ($g^r$) and `hashed_to_curve_r` ($hash^r$) as public values so that the verifier can calculate the hash themselves. // The change is explained here https://www.notion.so/PLUME-Discussion-6f4b7e7cf63e4e33976f6e697bf349ff -template plume_v2(n, k, msg_length) { +template plume_v2(n, k, message_length) { signal input c[k]; signal input s[k]; - signal input msg[msg_length]; - signal input public_key[2][k]; + signal input plume_message[message_length]; + signal input pk[2][k]; signal input nullifier[2][k]; - signal output g_pow_r[2][k]; - signal output h_pow_r[2][k]; + signal output r_point[2][k]; + signal output hashed_to_curve_r[2][k]; // precomputed values for the hash_to_curve component signal input q0_gx1_sqrt[4]; @@ -115,14 +115,14 @@ template plume_v2(n, k, msg_length) { signal input q1_x_mapped[4]; signal input q1_y_mapped[4]; - component check_ec_equations = check_ec_equations(n, k, msg_length); + component check_ec_equations = check_ec_equations(n, k, message_length); check_ec_equations.c <== c; check_ec_equations.s <== s; - check_ec_equations.public_key <== public_key; + check_ec_equations.pk <== pk; check_ec_equations.nullifier <== nullifier; - check_ec_equations.msg <== msg; + check_ec_equations.plume_message <== plume_message; check_ec_equations.q0_gx1_sqrt <== q0_gx1_sqrt; check_ec_equations.q0_gx2_sqrt <== q0_gx2_sqrt; @@ -136,20 +136,20 @@ template plume_v2(n, k, msg_length) { check_ec_equations.q1_x_mapped <== q1_x_mapped; check_ec_equations.q1_y_mapped <== q1_y_mapped; - h_pow_r <== check_ec_equations.h_pow_r; - g_pow_r <== check_ec_equations.g_pow_r; + hashed_to_curve_r <== check_ec_equations.hashed_to_curve_r; + r_point <== check_ec_equations.r_point; } -template check_ec_equations(n, k, msg_length) { +template check_ec_equations(n, k, message_length) { signal input c[k]; signal input s[k]; - signal input msg[msg_length]; - signal input public_key[2][k]; + signal input plume_message[message_length]; + signal input pk[2][k]; signal input nullifier[2][k]; - signal output g_pow_r[2][k]; - signal output h_pow_r[2][k]; - signal output h[2][k]; + signal output r_point[2][k]; + signal output hashed_to_curve_r[2][k]; + signal output hashed_to_curve[2][k]; // precomputed values for the hash_to_curve component signal input q0_gx1_sqrt[4]; @@ -170,58 +170,58 @@ template check_ec_equations(n, k, msg_length) { // Calculates g^s. Note, turning a private key to a public key is the same operation as // raising the generator g to some power, and we are *not* dealing with private keys in this circuit. - component g_pow_s = ECDSAPrivToPub(n, k); - g_pow_s.privkey <== s; + component s_point = ECDSAPrivToPub(n, k); + s_point.privkey <== s; - component g_pow_r_comp = a_div_b_pow_c(n, k); - g_pow_r_comp.a <== g_pow_s.pubkey; - g_pow_r_comp.b <== public_key; - g_pow_r_comp.c <== c; + component r_point_comp = a_div_b_pow_c(n, k); + r_point_comp.a <== s_point.pubkey; + r_point_comp.b <== pk; + r_point_comp.c <== c; // Calculate hash[m, pk]^r // hash[m, pk]^r = hash[m, pk]^s / (hash[m, pk]^sk)^c // Note this implicitly checks the second equation in the blog // Calculate hash[m, pk]^r - component h_comp = HashToCurve(msg_length + 33); - for (var i = 0; i < msg_length; i++) { - h_comp.msg[i] <== msg[i]; + component hash_to_curve = HashToCurve(message_length + 33); + for (var i = 0; i < message_length; i++) { + hash_to_curve.msg[i] <== plume_message[i]; } component pk_compressor = compress_ec_point(n, k); - pk_compressor.uncompressed <== public_key; + pk_compressor.uncompressed <== pk; for (var i = 0; i < 33; i++) { - h_comp.msg[msg_length + i] <== pk_compressor.compressed[i]; + hash_to_curve.msg[message_length + i] <== pk_compressor.compressed[i]; } // Input precalculated values into HashToCurve - h_comp.q0_gx1_sqrt <== q0_gx1_sqrt; - h_comp.q0_gx2_sqrt <== q0_gx2_sqrt; - h_comp.q0_y_pos <== q0_y_pos; - h_comp.q0_x_mapped <== q0_x_mapped; - h_comp.q0_y_mapped <== q0_y_mapped; - h_comp.q1_gx1_sqrt <== q1_gx1_sqrt; - h_comp.q1_gx2_sqrt <== q1_gx2_sqrt; - h_comp.q1_y_pos <== q1_y_pos; - h_comp.q1_x_mapped <== q1_x_mapped; - h_comp.q1_y_mapped <== q1_y_mapped; + hash_to_curve.q0_gx1_sqrt <== q0_gx1_sqrt; + hash_to_curve.q0_gx2_sqrt <== q0_gx2_sqrt; + hash_to_curve.q0_y_pos <== q0_y_pos; + hash_to_curve.q0_x_mapped <== q0_x_mapped; + hash_to_curve.q0_y_mapped <== q0_y_mapped; + hash_to_curve.q1_gx1_sqrt <== q1_gx1_sqrt; + hash_to_curve.q1_gx2_sqrt <== q1_gx2_sqrt; + hash_to_curve.q1_y_pos <== q1_y_pos; + hash_to_curve.q1_x_mapped <== q1_x_mapped; + hash_to_curve.q1_y_mapped <== q1_y_mapped; component h_pow_s = Secp256k1ScalarMult(n, k); h_pow_s.scalar <== s; - h_pow_s.point <== h_comp.out; + h_pow_s.point <== hash_to_curve.out; - component h_pow_r_comp = a_div_b_pow_c(n, k); - h_pow_r_comp.a <== h_pow_s.out; - h_pow_r_comp.b <== nullifier; - h_pow_r_comp.c <== c; + component hashed_to_curve_r_comp = a_div_b_pow_c(n, k); + hashed_to_curve_r_comp.a <== h_pow_s.out; + hashed_to_curve_r_comp.b <== nullifier; + hashed_to_curve_r_comp.c <== c; - h <== h_comp.out; + hashed_to_curve <== hash_to_curve.out; - h_pow_r <== h_pow_r_comp.out; + hashed_to_curve_r <== hashed_to_curve_r_comp.out; - g_pow_r <== g_pow_r_comp.out; + r_point <== r_point_comp.out; } template a_div_b_pow_c(n, k) { @@ -232,7 +232,7 @@ template a_div_b_pow_c(n, k) { // Calculates b^c. Note that the spec uses multiplicative notation to preserve intuitions about // discrete log, and these comments follow the spec to make comparison simpler. But the circom-ecdsa library uses - // additive notation. This is why we appear to calculate an expnentiation using a multiplication component. + // additive notation. This is why we appear to calculate an exponentiation using a multiplication component. component b_pow_c = Secp256k1ScalarMult(n, k); b_pow_c.scalar <== c; b_pow_c.point <== b; @@ -337,20 +337,23 @@ template compress_ec_point(n, k) { verify.compressed <== compressed; } -// We have a separate internal compression verification template for testing purposes. An adversarial prover -// can set any compressed values, so it's useful to be able to test adversarial inputs. +// We have a separate internal compression verification template for testing +// purposes. An adversarial prover can set any compressed values, so it's +// useful to be able to test adversarial inputs. template verify_ec_compression(n, k) { signal input uncompressed[2][k]; signal input compressed[33]; - // Get the bit string of the smallest register - // Make sure the least significant bit's evenness matches the evenness specified by the first byte in the compressed version + // Get the bit string of the smallest register \ + // Make sure the least significant bit's evenness matches the evenness + // specified by the first byte in the compressed version component num2bits = Num2Bits(n); num2bits.in <== uncompressed[1][0]; // Note, circom-ecdsa uses little endian, so we check the 0th register of the y value compressed[0] === num2bits.out[0] + 2; - // Make sure the compressed and uncompressed x coordinates represent the same number - // l_bytes is an algebraic expression for the bytes of each register + // Make sure the compressed and uncompressed x coordinates represent + // the same number \ + // `l_bytes` is an algebraic expression for the bytes of each register var l_bytes[k]; for (var i = 1; i < 33; i++) { var j = i - 1; // ignores the first byte specifying the compressed y coordinate @@ -360,9 +363,10 @@ template verify_ec_compression(n, k) { uncompressed[0] === l_bytes; } -// Equivalent to get_gx and get_gy in circom-ecdsa, except we also have values for n = 64, k = 4. -// This is necessary because hash_to_curve is only implemented for n = 64, k = 4 but circom-ecdsa -// only g's coordinates for n = 86, k = 3 +// Equivalent to get_gx and get_gy in circom-ecdsa, except we also have values +// for n = 64, k = 4. \ +// This is necessary because hash_to_curve is only implemented for n = 64, +// k = 4 but circom-ecdsa only g's coordinates for n = 86, k = 3 \ // TODO: merge this upstream into circom-ecdsa function get_genx(n, k) { assert((n == 86 && k == 3) || (n == 64 && k == 4)); diff --git a/javascript/.gitignore b/javascript/.gitignore index c48cdb3..33d0007 100644 --- a/javascript/.gitignore +++ b/javascript/.gitignore @@ -13,3 +13,6 @@ # node.js /node_modules /dist + +# https://github.com/plume-sig/zk-nullifier-sig/pull/66/#issuecomment-1780924701 +package-lock.json diff --git a/javascript/README.md b/javascript/README.md deleted file mode 100644 index 2f42ec4..0000000 --- a/javascript/README.md +++ /dev/null @@ -1 +0,0 @@ -# javascript-miracl diff --git a/javascript/src/signals.ts b/javascript/src/signals.ts index 2b6bcd2..71d9cfe 100644 --- a/javascript/src/signals.ts +++ b/javascript/src/signals.ts @@ -16,115 +16,108 @@ export enum PlumeVersion { V2, } -export function computeHashMPk( +export function computeHashToCurve( message: Uint8Array, - publicKey: Uint8Array + pk: Uint8Array ): HashedPoint { // Concatenate message and publicKey - const preimage = new Uint8Array(message.length + publicKey.length); + const preimage = new Uint8Array(message.length + pk.length); preimage.set(message); - preimage.set(publicKey, message.length); + preimage.set(pk, message.length); return hashToCurve(Array.from(preimage)); } export function computeC_V2( nullifier: Point, - gPowR: Point, - hashMPkPowR: Point + rPoint: Point, + hashedToCurveR: Point ) { const nullifierBytes = nullifier.toRawBytes(true); - const gPowRBytes = gPowR.toRawBytes(true); - const hashMPkPowRBytes = hashMPkPowR.toRawBytes(true); const preimage = concatUint8Arrays([ nullifierBytes, - gPowRBytes, - hashMPkPowRBytes, + rPoint.toRawBytes(true), + hashedToCurveR.toRawBytes(true), ]); return sha256.create().update(preimage).hex(); } export function computeC_V1( - publicKeyBytes: Uint8Array, - hashMPk: HashedPoint, + pkBytes: Uint8Array, + hashedToCurve: HashedPoint, nullifier: Point, - gPowR: Point, - hashMPkPowR: Point + rPoint: Point, + hashedToCurveR: Point ) { - const gBytes = Point.BASE.toRawBytes(true); - const hashMPkBytes = new Point( - hexToBigInt(hashMPk.x.toString()), - hexToBigInt(hashMPk.y.toString()) - ).toRawBytes(true); const nullifierBytes = nullifier.toRawBytes(true); - const gPowRBytes = gPowR.toRawBytes(true); - const hashMPkPowRBytes = hashMPkPowR.toRawBytes(true); const preimage = concatUint8Arrays([ - gBytes, - publicKeyBytes, - hashMPkBytes, + Point.BASE.toRawBytes(true), + pkBytes, + new Point( + hexToBigInt(hashedToCurve.x.toString()), + hexToBigInt(hashedToCurve.y.toString()) + ).toRawBytes(true), nullifierBytes, - gPowRBytes, - hashMPkPowRBytes, + rPoint.toRawBytes(true), + hashedToCurveR.toRawBytes(true), ]); return sha256.create().update(preimage).hex(); } -export function computeNullifer(hashMPk: HashedPoint, secretKey: Uint8Array) { - return multiplyPoint(hashMPk, secretKey); +export function computeNullifer(hashedToCurve: HashedPoint, sk: Uint8Array) { + return multiplyPoint(hashedToCurve, sk); } -export function computeGPowR(r: Uint8Array) { - return Point.fromPrivateKey(r); +export function computeRPoint(rScalar: Uint8Array) { + return Point.fromPrivateKey(rScalar); } -export function computeHashMPkPowR(hashMPk: HashedPoint, r: Uint8Array) { - return multiplyPoint(hashMPk, r); +export function computeHashToCurveR(hashedToCurve: HashedPoint, rScalar: Uint8Array) { + return multiplyPoint(hashedToCurve, rScalar); } -export function computeS(r: Uint8Array, secretKey: Uint8Array, c: string) { - const skC = (uint8ArrayToBigInt(secretKey) * hexToBigInt(c)) % CURVE.n; - return ((skC + uint8ArrayToBigInt(r)) % CURVE.n).toString(16); +export function computeS(rScalar: Uint8Array, sk: Uint8Array, c: string) { + return (((uint8ArrayToBigInt(sk) * hexToBigInt(c)) % CURVE.n + uint8ArrayToBigInt(rScalar)) % CURVE.n).toString(16); } /** * Computes and returns the Plume and other signals for the prover. * @param {string | Uint8Array} message - Message to sign, in either string or UTF-8 array format. - * @param {string | Uint8Array} secretKey - ECDSA secret key to sign with. - * @param {string| Uint8Array} r - Optional seed for randomness. + * @param {string | Uint8Array} sk - ECDSA secret key to sign with. + * @param {string| Uint8Array} rScalar - Optional seed for randomness. * @returns Object containing Plume and other signals - public key, s, c, gPowR, and hashMPKPowR. */ export function computeAllInputs( message: string | Uint8Array, - secretKey: string | Uint8Array, - r?: string | Uint8Array, + sk: string | Uint8Array, + rScalar?: string | Uint8Array, version: PlumeVersion = PlumeVersion.V2 ) { - const secretKeyBytes = - typeof secretKey === "string" ? hexToUint8Array(secretKey) : secretKey; + const skBytes = + typeof sk === "string" ? hexToUint8Array(sk) : sk; const messageBytes = typeof message === "string" ? messageToUint8Array(message) : message; - const publicKeyBytes = getPublicKey(secretKeyBytes, true); - let rBytes; - if (r) { - rBytes = typeof r === "string" ? hexToUint8Array(r) : r; + const pkBytes = getPublicKey(skBytes, true); + let rScalarBytes; + if (rScalar) { + rScalarBytes = typeof rScalar === "string" ? hexToUint8Array(rScalar) : rScalar; } else { - rBytes = utils.randomPrivateKey(); + rScalarBytes = utils.randomPrivateKey(); } - const hashMPK = computeHashMPk(messageBytes, publicKeyBytes); - const nullifier = computeNullifer(hashMPK, secretKeyBytes); - const hashMPKPowR = computeHashMPkPowR(hashMPK, rBytes); - const gPowR = computeGPowR(rBytes); + const hashedToCurve = computeHashToCurve(messageBytes, pkBytes); + const nullifier = computeNullifer(hashedToCurve, skBytes); + const hashedToCurveR = computeHashToCurveR(hashedToCurve, rScalarBytes); + const rPoint = computeRPoint(rScalarBytes); const c = version == PlumeVersion.V1 - ? computeC_V1(publicKeyBytes, hashMPK, nullifier, gPowR, hashMPKPowR) - : computeC_V2(nullifier, gPowR, hashMPKPowR); - const s = computeS(rBytes, secretKeyBytes, c); + ? computeC_V1(pkBytes, hashedToCurve, nullifier, rPoint, hashedToCurveR) + : computeC_V2(nullifier, rPoint, hashedToCurveR); + const s = computeS(rScalarBytes, skBytes, c); return { plume: nullifier, s, - publicKey: publicKeyBytes, + pk: pkBytes, c, - gPowR, - hashMPKPowR, + rPoint, + hashedToCurveR, }; } diff --git a/javascript/test/signals.test.ts b/javascript/test/signals.test.ts index 6a411fa..68fbc11 100644 --- a/javascript/test/signals.test.ts +++ b/javascript/test/signals.test.ts @@ -1,8 +1,8 @@ import { hashMPk, nullifier, - hashMPkPowR, - gPowR, + hashedToCurveR, + rPoint, c_v1, s_v1, c_v2, @@ -36,17 +36,17 @@ describe("signals", () => { }); describe("Plume V1", () => { it("generates c and intermediate values correctly", () => { - expect(hashMPkPowR.x.toString(16)).toEqual( + expect(hashedToCurveR.x.toString(16)).toEqual( "6d017c6f63c59fa7a5b1e9a654e27d2869579f4d152131db270558fccd27b97c" ); - expect(hashMPkPowR.y.toString(16)).toEqual( + expect(hashedToCurveR.y.toString(16)).toEqual( "586c43fb5c99818c564a8f80a88a65f83e3f44d3c6caf5a1a4e290b777ac56ed" ); - expect(gPowR.x.toString(16)).toEqual( + expect(rPoint.x.toString(16)).toEqual( "9d8ca4350e7e2ad27abc6d2a281365818076662962a28429590e2dc736fe9804" ); - expect(gPowR.y.toString(16)).toEqual( + expect(rPoint.y.toString(16)).toEqual( "ff08c30b8afd4e854623c835d9c3aac6bcebe45112472d9b9054816a7670c5a1" ); @@ -62,17 +62,17 @@ describe("signals", () => { }); it("generates all signals", () => { - const { plume, s, publicKey, c, gPowR, hashMPKPowR } = computeAllInputs( + const { plume, s, pk, c, rPoint, hashedToCurveR } = computeAllInputs( testMessage, testSecretKey, testR, PlumeVersion.V1, ); - expect(publicKey).toEqual(testPublicKey); - expect(gPowR.x.toString(16)).toEqual( + expect(pk).toEqual(testPublicKey); + expect(rPoint.x.toString(16)).toEqual( "9d8ca4350e7e2ad27abc6d2a281365818076662962a28429590e2dc736fe9804" ); - expect(gPowR.y.toString(16)).toEqual( + expect(rPoint.y.toString(16)).toEqual( "ff08c30b8afd4e854623c835d9c3aac6bcebe45112472d9b9054816a7670c5a1" ); expect(plume.x.toString(16)).toEqual( @@ -81,7 +81,7 @@ describe("signals", () => { expect(plume.y.toString(16)).toEqual( "6a2f41488d58f33ae46edd2188e111609f9f3ae67ea38fa891d6087fe59ecb73" ); - expect(hashMPKPowR.x.toString(16)).toEqual( + expect(hashedToCurveR.x.toString(16)).toEqual( "6d017c6f63c59fa7a5b1e9a654e27d2869579f4d152131db270558fccd27b97c" ); expect(c).toEqual( @@ -90,7 +90,7 @@ describe("signals", () => { expect(s).toEqual( "e69f027d84cb6fe5f761e333d12e975fb190d163e8ea132d7de0bd6079ba28ca" ); - expect(hashMPKPowR.y.toString(16)).toEqual( + expect(hashedToCurveR.y.toString(16)).toEqual( "586c43fb5c99818c564a8f80a88a65f83e3f44d3c6caf5a1a4e290b777ac56ed" ); }); @@ -98,17 +98,17 @@ describe("signals", () => { describe("Plume V2", () => { it("generates c and intermediate values correctly", () => { - expect(hashMPkPowR.x.toString(16)).toEqual( + expect(hashedToCurveR.x.toString(16)).toEqual( "6d017c6f63c59fa7a5b1e9a654e27d2869579f4d152131db270558fccd27b97c" ); - expect(hashMPkPowR.y.toString(16)).toEqual( + expect(hashedToCurveR.y.toString(16)).toEqual( "586c43fb5c99818c564a8f80a88a65f83e3f44d3c6caf5a1a4e290b777ac56ed" ); - expect(gPowR.x.toString(16)).toEqual( + expect(rPoint.x.toString(16)).toEqual( "9d8ca4350e7e2ad27abc6d2a281365818076662962a28429590e2dc736fe9804" ); - expect(gPowR.y.toString(16)).toEqual( + expect(rPoint.y.toString(16)).toEqual( "ff08c30b8afd4e854623c835d9c3aac6bcebe45112472d9b9054816a7670c5a1" ); @@ -124,17 +124,17 @@ describe("signals", () => { }); it("generates all signals", () => { - const { plume, s, publicKey, c, gPowR, hashMPKPowR } = computeAllInputs( + const { plume, s, pk, c, rPoint, hashedToCurveR } = computeAllInputs( testMessage, testSecretKey, testR, PlumeVersion.V2 ); - expect(publicKey).toEqual(testPublicKey); - expect(gPowR.x.toString(16)).toEqual( + expect(pk).toEqual(testPublicKey); + expect(rPoint.x.toString(16)).toEqual( "9d8ca4350e7e2ad27abc6d2a281365818076662962a28429590e2dc736fe9804" ); - expect(gPowR.y.toString(16)).toEqual( + expect(rPoint.y.toString(16)).toEqual( "ff08c30b8afd4e854623c835d9c3aac6bcebe45112472d9b9054816a7670c5a1" ); expect(plume.x.toString(16)).toEqual( @@ -143,7 +143,7 @@ describe("signals", () => { expect(plume.y.toString(16)).toEqual( "6a2f41488d58f33ae46edd2188e111609f9f3ae67ea38fa891d6087fe59ecb73" ); - expect(hashMPKPowR.x.toString(16)).toEqual( + expect(hashedToCurveR.x.toString(16)).toEqual( "6d017c6f63c59fa7a5b1e9a654e27d2869579f4d152131db270558fccd27b97c" ); expect(c).toEqual( @@ -152,7 +152,7 @@ describe("signals", () => { expect(s).toEqual( "528e8fbb6452f82200797b1a73b2947a92524bd611085a920f1177cb8098136b" ); - expect(hashMPKPowR.y.toString(16)).toEqual( + expect(hashedToCurveR.y.toString(16)).toEqual( "586c43fb5c99818c564a8f80a88a65f83e3f44d3c6caf5a1a4e290b777ac56ed" ); }); diff --git a/javascript/test/test_consts.ts b/javascript/test/test_consts.ts index f79f24a..03a7bd7 100644 --- a/javascript/test/test_consts.ts +++ b/javascript/test/test_consts.ts @@ -2,9 +2,9 @@ import { CURVE, getPublicKey, Point } from "@noble/secp256k1"; import { computeC_V1, computeC_V2, - computeGPowR, - computeHashMPk, - computeHashMPkPowR, + computeRPoint, + computeHashToCurve, + computeHashToCurveR, computeNullifer, computeS, } from "../src/signals"; @@ -22,18 +22,18 @@ export const testR = hexToUint8Array( ); export const testMessageString = "An example app message string"; export const testMessage = messageToUint8Array(testMessageString); -export const hashMPk = computeHashMPk(testMessage, Buffer.from(testPublicKey)); +export const hashMPk = computeHashToCurve(testMessage, Buffer.from(testPublicKey)); export const nullifier = computeNullifer(hashMPk, testSecretKey); -export const hashMPkPowR = computeHashMPkPowR(hashMPk, testR); -export const gPowR = computeGPowR(testR); +export const hashedToCurveR = computeHashToCurveR(hashMPk, testR); +export const rPoint = computeRPoint(testR); export const c_v1 = computeC_V1( testPublicKey, hashMPk, nullifier as unknown as Point, - gPowR, - hashMPkPowR + rPoint, + hashedToCurveR ); export const s_v1 = computeS(testR, testSecretKey, c_v1); -export const c_v2 = computeC_V2(nullifier, gPowR, hashMPkPowR); +export const c_v2 = computeC_V2(nullifier, rPoint, hashedToCurveR); export const s_v2 = computeS(testR, testSecretKey, c_v2); diff --git a/rust-arkworks/src/lib.rs b/rust-arkworks/src/lib.rs index ec1acc9..904ed3e 100644 --- a/rust-arkworks/src/lib.rs +++ b/rust-arkworks/src/lib.rs @@ -1,7 +1,5 @@ mod error; mod hash_to_curve; -#[cfg(test)] -mod tests; pub mod sig { use crate::error::CryptoError; @@ -50,35 +48,35 @@ pub mod sig { } fn compute_c_v1( - g: &GroupAffine

, + g_point: &GroupAffine

, pk: &GroupAffine

, - h: &GroupAffine

, - nul: &GroupAffine

, - g_r: &GroupAffine

, - z: &GroupAffine

, + hashed_to_curve: &GroupAffine

, + nullifier: &GroupAffine

, + r_point: &GroupAffine

, + hashed_to_curve_r: &GroupAffine

, ) -> Output { // Compute c = sha512([g, pk, h, nul, g^r, z]) - let g_bytes = affine_to_bytes::

(g); - let pk_bytes = affine_to_bytes::

(pk); - let h_bytes = affine_to_bytes::

(h); - let nul_bytes = affine_to_bytes::

(nul); - let g_r_bytes = affine_to_bytes::

(g_r); - let z_bytes = affine_to_bytes::

(z); - - let c_preimage_vec = [g_bytes, pk_bytes, h_bytes, nul_bytes, g_r_bytes, z_bytes].concat(); + let c_preimage_vec = [ + affine_to_bytes::

(g_point), + affine_to_bytes::

(pk), + affine_to_bytes::

(hashed_to_curve), + affine_to_bytes::

(nullifier), + affine_to_bytes::

(r_point), + affine_to_bytes::

(hashed_to_curve_r) + ].concat(); Sha256::digest(c_preimage_vec.as_slice()) } fn compute_c_v2( - nul: &GroupAffine

, - g_r: &GroupAffine

, - z: &GroupAffine

, + nullifier: &GroupAffine

, + r_point: &GroupAffine

, + hashed_to_curve_r: &GroupAffine

, ) -> Output { // Compute c = sha512([nul, g^r, z]) - let nul_bytes = affine_to_bytes::

(nul); - let g_r_bytes = affine_to_bytes::

(g_r); - let z_bytes = affine_to_bytes::

(z); + let nul_bytes = affine_to_bytes::

(nullifier); + let g_r_bytes = affine_to_bytes::

(r_point); + let z_bytes = affine_to_bytes::

(hashed_to_curve_r); let c_preimage_vec = [nul_bytes, g_r_bytes, z_bytes].concat(); @@ -112,7 +110,7 @@ pub mod sig { pp: &Self::Parameters, keypair: (&Self::PublicKey, &Self::SecretKey), message: Self::Message, - r: Self::SecretKey, + r_scalar: Self::SecretKey, version: PlumeVersion, ) -> Result; @@ -132,7 +130,7 @@ pub mod sig { ark_serialize_derive::CanonicalDeserialize, )] pub struct Parameters { - pub g: GroupAffine

, + pub g_point: GroupAffine

, } #[derive( @@ -142,11 +140,11 @@ pub mod sig { ark_serialize_derive::CanonicalDeserialize, )] pub struct Signature { - pub z: GroupAffine

, - pub g_r: GroupAffine

, + pub hashed_to_curve_r: GroupAffine

, + pub r_point: GroupAffine

, pub s: P::ScalarField, pub c: P::ScalarField, - pub nul: GroupAffine

, + pub nullifier: GroupAffine

, } impl<'a, C: ProjectiveCurve, Fq: PrimeField, P: SWModelParameters> @@ -163,7 +161,7 @@ pub mod sig { rng: &mut R, ) -> Result<(Self::PublicKey, Self::SecretKey), CryptoError> { let secret_key = Self::SecretKey::rand(rng).into(); - let public_key = pp.g.mul(secret_key).into(); + let public_key = pp.g_point.mul(secret_key).into(); Ok((public_key, secret_key)) } @@ -171,39 +169,39 @@ pub mod sig { pp: &Self::Parameters, keypair: (&Self::PublicKey, &Self::SecretKey), message: Self::Message, - r: P::ScalarField, + r_scalar: P::ScalarField, version: PlumeVersion, ) -> Result { - let g = pp.g; - let g_r = g.mul(r).into_affine(); + let g_point = pp.g_point; + let r_point = g_point.mul(r_scalar).into_affine(); // Compute h = htc([m, pk]) - let h = compute_h::(&keypair.0, &message).unwrap(); + let hashed_to_curve = compute_h::(&keypair.0, &message).unwrap(); // Compute z = h^r - let z = h.mul(r).into_affine(); + let hashed_to_curve_r = hashed_to_curve.mul(r_scalar).into_affine(); // Compute nul = h^sk - let nul = h.mul(*keypair.1).into_affine(); + let nullifier = hashed_to_curve.mul(*keypair.1).into_affine(); // Compute c = sha512([g, pk, h, nul, g^r, z]) let c = match version { - PlumeVersion::V1 => compute_c_v1::

(&g, keypair.0, &h, &nul, &g_r, &z), - PlumeVersion::V2 => compute_c_v2(&nul, &g_r, &z), + PlumeVersion::V1 => compute_c_v1::

(&g_point, keypair.0, &hashed_to_curve, &nullifier, &r_point, &hashed_to_curve_r), + PlumeVersion::V2 => compute_c_v2(&nullifier, &r_point, &hashed_to_curve_r), }; let c_scalar = P::ScalarField::from_be_bytes_mod_order(c.as_ref()); // Compute s = r + sk ⋅ c let sk_c = keypair.1.into_repr().into() * c_scalar.into_repr().into(); - let s = r.into_repr().into() + sk_c; + let s = r_scalar.into_repr().into() + sk_c; let s_scalar = P::ScalarField::from(s); let signature = Signature { - z, + hashed_to_curve_r, s: s_scalar, - g_r, + r_point, c: c_scalar, - nul, + nullifier, }; Ok(signature) } @@ -216,9 +214,9 @@ pub mod sig { version: PlumeVersion, ) -> Result { // Pick a random r from Fp - let r: P::ScalarField = Self::SecretKey::rand(rng).into(); + let r_scalar: P::ScalarField = Self::SecretKey::rand(rng).into(); - Self::sign_with_r(pp, keypair, message, r, version) + Self::sign_with_r(pp, keypair, message, r_scalar, version) } fn verify_non_zk( @@ -229,32 +227,32 @@ pub mod sig { version: PlumeVersion, ) -> Result { // Compute h = htc([m, pk]) - let h = compute_h::(pk, message).unwrap(); + let hashed_to_curve = compute_h::(pk, message).unwrap(); // TODO [replace SHA-512](https://github.com/plume-sig/zk-nullifier-sig/issues/39#issuecomment-1732497672) // Compute c' = sha512([g, pk, h, nul, g^r, z]) for v1 // c' = sha512([nul, g^r, z]) for v2 let c = match version { - PlumeVersion::V1 => compute_c_v1::

(&pp.g, pk, &h, &sig.nul, &sig.g_r, &sig.z), - PlumeVersion::V2 => compute_c_v2(&sig.nul, &sig.g_r, &sig.z), + PlumeVersion::V1 => compute_c_v1::

(&pp.g_point, pk, &hashed_to_curve, &sig.nullifier, &sig.r_point, &sig.hashed_to_curve_r), + PlumeVersion::V2 => compute_c_v2(&sig.nullifier, &sig.r_point, &sig.hashed_to_curve_r), }; let c_scalar = P::ScalarField::from_be_bytes_mod_order(c.as_ref()); // Reject if g^s ⋅ pk^{-c} != g^r - let g_s = pp.g.mul(sig.s); + let g_s = pp.g_point.mul(sig.s); let pk_c = pk.mul(sig.c); let g_s_pk_c = g_s - pk_c; - if sig.g_r != g_s_pk_c { + if sig.r_point != g_s_pk_c { return Ok(false); } // Reject if h^s ⋅ nul^{-c} = z - let h_s = h.mul(sig.s); - let nul_c = sig.nul.mul(sig.c); + let h_s = hashed_to_curve.mul(sig.s); + let nul_c = sig.nullifier.mul(sig.c); let h_s_nul_c = h_s - nul_c; - if sig.z != h_s_nul_c { + if sig.hashed_to_curve_r != h_s_nul_c { return Ok(false); } @@ -267,3 +265,6 @@ pub mod sig { } } } + +#[cfg(test)] +mod tests; \ No newline at end of file diff --git a/rust-arkworks/src/tests.rs b/rust-arkworks/src/tests.rs index 273a8a0..88a9066 100644 --- a/rust-arkworks/src/tests.rs +++ b/rust-arkworks/src/tests.rs @@ -89,7 +89,7 @@ pub fn hardcoded_msg() -> String { #[test] pub fn test_keygen() { let (mut rng, g) = test_template(); - let pp = Parameters{ g }; + let pp = Parameters{ g_point: g }; let (pk, sk) = Scheme::keygen(&pp, &mut rng).unwrap(); @@ -100,7 +100,7 @@ pub fn test_keygen() { #[test] pub fn test_sign_and_verify() { let (mut rng, g) = test_template(); - let pp = Parameters{ g }; + let pp = Parameters{ g_point: g }; let message = b"Message"; let keypair = Scheme::keygen(&pp, &mut rng).unwrap(); @@ -217,7 +217,7 @@ pub fn test_against_zk_nullifier_sig_c_and_s() { let message = message.as_bytes(); let sk = hex_to_fr(&hardcoded_sk()); let (_, g) = test_template(); - let pp = Parameters{ g }; + let pp = Parameters{ g_point: g }; let pk_projective = g.mul(sk); let pk = GroupAffine::::from(pk_projective); diff --git a/rust-k256/Cargo.toml b/rust-k256/Cargo.toml index 28b0d8d..1f5c70c 100644 --- a/rust-k256/Cargo.toml +++ b/rust-k256/Cargo.toml @@ -16,4 +16,6 @@ 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"]} + +[dev-dependencies] hex = "0.4.3" diff --git a/rust-k256/src/lib.rs b/rust-k256/src/lib.rs index 3daeb14..3cf94a8 100644 --- a/rust-k256/src/lib.rs +++ b/rust-k256/src/lib.rs @@ -8,7 +8,6 @@ use elliptic_curve::bigint::ArrayEncoding; use elliptic_curve::hash2curve::{ExpandMsgXmd, GroupDigest}; use elliptic_curve::ops::Reduce; use elliptic_curve::sec1::ToEncodedPoint; -use hex_literal::hex; use k256::U256; use k256::{ // ecdsa::{signature::Signer, Signature, SigningKey}, @@ -35,23 +34,7 @@ fn print_type_of(_: &T) { println!("{}", std::any::type_name::()); } -// Generates a deterministic secret key for deterministic testing. Should be replaced by random oracle in production deployments. -fn gen_test_scalar_sk() -> Scalar { - Scalar::from_repr( - hex!("519b423d715f8b581f4fa8ee59f4771a5b44c8130b4e3eacca54a56dda72b464").into(), - ) - .unwrap() -} - -// Generates a deterministic r for deterministic testing. Should be replaced by random oracle in production deployments. -fn gen_test_scalar_r() -> Scalar { - Scalar::from_repr( - hex!("93b9323b629f251b8f3fc2dd11f4672c5544e8230d493eceea98a90bda789808").into(), - ) - .unwrap() -} - -fn sha256hash_vec_signal(values: &[ProjectivePoint]) -> Output { +fn c_sha256_vec_signal(values: &[ProjectivePoint]) -> Output { let preimage_vec = values .iter() .map(|value| encode_pt(*value).unwrap()) @@ -59,7 +42,7 @@ fn sha256hash_vec_signal(values: &[ProjectivePoint]) -> Output { .concat(); let mut sha256_hasher = Sha256::new(); sha256_hasher.update(preimage_vec.as_slice()); - sha256_hasher.finalize() //256 bit hash + sha256_hasher.finalize() } fn sha256hash6signals( @@ -89,19 +72,8 @@ fn sha256hash6signals( Scalar::from_repr(c_bytes).unwrap() } -// Calls the hash to curve function for secp256k1, and returns the result as a ProjectivePoint -fn hash_to_secp(s: &[u8]) -> ProjectivePoint { - let pt: ProjectivePoint = Secp256k1::hash_from_bytes::>( - &[s], - //b"CURVE_XMD:SHA-256_SSWU_RO_" - DST, - ) - .unwrap(); - pt -} - // Hashes two values to the curve -fn hash_m_pk_to_secp(m: &[u8], pk: &ProjectivePoint) -> ProjectivePoint { +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_", @@ -121,59 +93,68 @@ enum PlumeVersion { // hash[m, gsk]^[r + sk * c] / (hash[m, pk]^sk)^c = hash[m, pk]^r // c = hash2(g, g^sk, hash[m, g^sk], hash[m, pk]^sk, gr, hash[m, pk]^r) fn verify_signals( - m: &[u8], + message: &[u8], pk: &ProjectivePoint, nullifier: &ProjectivePoint, c: &Output, - r_sk_c: &Scalar, - g_r_option: &Option, - hash_m_pk_pow_r_option: &Option, + s: &Scalar, + r_point_optional: &Option, + hashed_to_curve_optional: &Option, version: PlumeVersion, ) -> bool { - let mut verified: bool = true; + let mut verified: bool = true; // looks like antipattern to @skaunov; also see #22 // The base point or generator of the curve. let g = &ProjectivePoint::GENERATOR; // hash[m, pk] - let hash_m_pk = &hash_m_pk_to_secp(m, pk); + let hashed_to_curve_computed = &hash_to_curve(message, pk); // Check whether g^r equals g^s * pk^{-c} - let g_r: ProjectivePoint; + let r_point_computed: ProjectivePoint; // TODO should we use non-zero `Scalar`? let c_scalar = &Scalar::from_uint_reduced(U256::from_be_byte_array(*c)); - match *g_r_option { + match *r_point_optional { Some(_g_r_value) => { - if (g * r_sk_c - pk * c_scalar) != _g_r_value { + if (g * s - pk * c_scalar) != _g_r_value { verified = false; } } None => println!("g^r not provided, check skipped"), } - g_r = g * r_sk_c - pk * c_scalar; + r_point_computed = g * s - pk * c_scalar; // Check whether h^r equals h^{r + sk * c} * nullifier^{-c} - let hash_m_pk_pow_r: ProjectivePoint; - match *hash_m_pk_pow_r_option { + let hashed_to_curve_r_computed: ProjectivePoint; + match *hashed_to_curve_optional { Some(_hash_m_pk_pow_r_value) => { - if (hash_m_pk * r_sk_c - nullifier * c_scalar) != _hash_m_pk_pow_r_value { + if (hashed_to_curve_computed * s - nullifier * c_scalar) != _hash_m_pk_pow_r_value { verified = false; } } None => println!("hash_m_pk_pow_r not provided, check skipped"), } - hash_m_pk_pow_r = hash_m_pk * r_sk_c - nullifier * c_scalar; + hashed_to_curve_r_computed = hashed_to_curve_computed * s - nullifier * c_scalar; // Check if the given hash matches match version { PlumeVersion::V1 => { - if sha256hash_vec_signal(&[*g, *pk, *hash_m_pk, *nullifier, g_r, hash_m_pk_pow_r]) != *c + if c_sha256_vec_signal(&[ + *g, + *pk, + *hashed_to_curve_computed, + *nullifier, + r_point_computed, + hashed_to_curve_r_computed, + ]) != *c { verified = false; } } PlumeVersion::V2 => { - if sha256hash_vec_signal(&[*nullifier, g_r, hash_m_pk_pow_r]) != *c { + if c_sha256_vec_signal(&[*nullifier, r_point_computed, hashed_to_curve_r_computed]) + != *c + { verified = false; } } @@ -203,10 +184,38 @@ fn byte_array_to_scalar(bytes: &[u8]) -> Scalar { #[cfg(test)] mod tests { use super::*; - - use helpers::test_gen_signals; + + use helpers::{gen_test_scalar_sk, hash_to_secp, test_gen_signals}; mod helpers { use super::*; + use hex_literal::hex; + + // Generates a deterministic secret key for deterministic testing. Should be replaced by random oracle in production deployments. + pub fn gen_test_scalar_sk() -> Scalar { + Scalar::from_repr( + hex!("519b423d715f8b581f4fa8ee59f4771a5b44c8130b4e3eacca54a56dda72b464").into(), + ) + .unwrap() + } + + // Generates a deterministic r for deterministic testing. Should be replaced by random oracle in production deployments. + fn gen_test_scalar_r() -> Scalar { + Scalar::from_repr( + hex!("93b9323b629f251b8f3fc2dd11f4672c5544e8230d493eceea98a90bda789808").into(), + ) + .unwrap() + } + + // Calls the hash to curve function for secp256k1, and returns the result as a ProjectivePoint + pub fn hash_to_secp(s: &[u8]) -> ProjectivePoint { + let pt: ProjectivePoint = Secp256k1::hash_from_bytes::>( + &[s], + //b"CURVE_XMD:SHA-256_SSWU_RO_" + DST, + ) + .unwrap(); + pt + } // These generate test signals as if it were passed from a secure enclave to wallet. Note that leaking these signals would leak pk, but not sk. // Outputs these 6 signals, in this order @@ -243,7 +252,7 @@ mod tests { let g_r = &g * &r; // hash[m, pk] - let hash_m_pk = hash_m_pk_to_secp(m, &pk); + let hash_m_pk = hash_to_curve(m, &pk); println!( "h.x: {:?}", @@ -283,11 +292,11 @@ mod tests { // The Fiat-Shamir type step. let c = match version { PlumeVersion::V1 => { - sha256hash_vec_signal(&[g, pk, hash_m_pk, nullifier, g_r, hash_m_pk_pow_r]) + c_sha256_vec_signal(&[g, pk, hash_m_pk, nullifier, g_r, hash_m_pk_pow_r]) } - PlumeVersion::V2 => sha256hash_vec_signal(&[nullifier, g_r, hash_m_pk_pow_r]), + PlumeVersion::V2 => c_sha256_vec_signal(&[nullifier, g_r, hash_m_pk_pow_r]), }; - + let c_scalar = &Scalar::from_uint_reduced(U256::from_be_byte_array(c.clone())); // This value is part of the discrete log equivalence (DLEQ) proof. let r_sk_c = r + sk * c_scalar;