All modules naming is aligned. + refactoring&tests

[Discussion] results are incorporated. Naming groomed repository wide. \
Fun fact: I already was confused by that "hash2" naming, lol.

Note that `fn verify_signals` barely [benefit]
from renaming at all currently.

[Discussion]: 251fba6902 (commitcomment-130400727)
[benefit]: https://github.com/plume-sig/zk-nullifier-sig/issues/61
This commit is contained in:
skaunov
2023-10-16 14:39:14 +03:00
parent f4b53bc183
commit da9f5fb288
10 changed files with 4216 additions and 438 deletions

View File

@@ -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 () => {

View File

@@ -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
@@ -36,10 +36,10 @@ template plume_v1(n, k, msg_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];
@@ -119,10 +119,10 @@ template plume_v2(n, k, msg_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));

View File

@@ -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,
};
}

View File

@@ -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"
);
});

View File

@@ -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);

File diff suppressed because it is too large Load Diff

View File

@@ -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<P: SWModelParameters>(
g: &GroupAffine<P>,
g_point: &GroupAffine<P>,
pk: &GroupAffine<P>,
h: &GroupAffine<P>,
nul: &GroupAffine<P>,
g_r: &GroupAffine<P>,
z: &GroupAffine<P>,
hashed_to_curve: &GroupAffine<P>,
nullifier: &GroupAffine<P>,
r_point: &GroupAffine<P>,
hashed_to_curve_r: &GroupAffine<P>,
) -> Output<Sha256> {
// Compute c = sha512([g, pk, h, nul, g^r, z])
let g_bytes = affine_to_bytes::<P>(g);
let pk_bytes = affine_to_bytes::<P>(pk);
let h_bytes = affine_to_bytes::<P>(h);
let nul_bytes = affine_to_bytes::<P>(nul);
let g_r_bytes = affine_to_bytes::<P>(g_r);
let z_bytes = affine_to_bytes::<P>(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::<P>(g_point),
affine_to_bytes::<P>(pk),
affine_to_bytes::<P>(hashed_to_curve),
affine_to_bytes::<P>(nullifier),
affine_to_bytes::<P>(r_point),
affine_to_bytes::<P>(hashed_to_curve_r)
].concat();
Sha256::digest(c_preimage_vec.as_slice())
}
fn compute_c_v2<P: SWModelParameters>(
nul: &GroupAffine<P>,
g_r: &GroupAffine<P>,
z: &GroupAffine<P>,
nullifier: &GroupAffine<P>,
r_point: &GroupAffine<P>,
hashed_to_curve_r: &GroupAffine<P>,
) -> Output<Sha256> {
// Compute c = sha512([nul, g^r, z])
let nul_bytes = affine_to_bytes::<P>(nul);
let g_r_bytes = affine_to_bytes::<P>(g_r);
let z_bytes = affine_to_bytes::<P>(z);
let nul_bytes = affine_to_bytes::<P>(nullifier);
let g_r_bytes = affine_to_bytes::<P>(r_point);
let z_bytes = affine_to_bytes::<P>(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<Self::Signature, CryptoError>;
@@ -132,7 +130,7 @@ pub mod sig {
ark_serialize_derive::CanonicalDeserialize,
)]
pub struct Parameters<P: SWModelParameters> {
pub g: GroupAffine<P>,
pub g_point: GroupAffine<P>,
}
#[derive(
@@ -142,11 +140,11 @@ pub mod sig {
ark_serialize_derive::CanonicalDeserialize,
)]
pub struct Signature<P: SWModelParameters> {
pub z: GroupAffine<P>,
pub g_r: GroupAffine<P>,
pub hashed_to_curve_r: GroupAffine<P>,
pub r_point: GroupAffine<P>,
pub s: P::ScalarField,
pub c: P::ScalarField,
pub nul: GroupAffine<P>,
pub nullifier: GroupAffine<P>,
}
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<Self::Signature, CryptoError> {
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::<C, Fq, P>(&keypair.0, &message).unwrap();
let hashed_to_curve = compute_h::<C, Fq, P>(&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::<P>(&g, keypair.0, &h, &nul, &g_r, &z),
PlumeVersion::V2 => compute_c_v2(&nul, &g_r, &z),
PlumeVersion::V1 => compute_c_v1::<P>(&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<Self::Signature, CryptoError> {
// 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<bool, CryptoError> {
// Compute h = htc([m, pk])
let h = compute_h::<C, Fq, P>(pk, message).unwrap();
let hashed_to_curve = compute_h::<C, Fq, P>(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::<P>(&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::<P>(&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;

View File

@@ -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::<Secp256k1Parameters>::from(pk_projective);

View File

@@ -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"

View File

@@ -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>(_: &T) {
println!("{}", std::any::type_name::<T>());
}
// 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<Sha256> {
fn c_sha256_vec_signal(values: &[ProjectivePoint]) -> Output<Sha256> {
let preimage_vec = values
.iter()
.map(|value| encode_pt(*value).unwrap())
@@ -59,7 +42,7 @@ fn sha256hash_vec_signal(values: &[ProjectivePoint]) -> Output<Sha256> {
.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::<ExpandMsgXmd<Sha256>>(
&[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::<ExpandMsgXmd<Sha256>>(
&[[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<Sha256>,
r_sk_c: &Scalar,
g_r_option: &Option<ProjectivePoint>,
hash_m_pk_pow_r_option: &Option<ProjectivePoint>,
s: &Scalar,
r_point_optional: &Option<ProjectivePoint>,
hashed_to_curve_optional: &Option<ProjectivePoint>,
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::<ExpandMsgXmd<Sha256>>(
&[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;