Skip to content

Commit 5f7a716

Browse files
committed
add dleq implementation
- modify secp256k1-zkp's dleq implementation to be consistent with BIP 374. - use BIP374 notations. - add DLEQ tests
1 parent 86d8507 commit 5f7a716

File tree

3 files changed

+345
-0
lines changed

3 files changed

+345
-0
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
include_HEADERS += include/secp256k1_silentpayments.h
22
noinst_HEADERS += src/modules/silentpayments/main_impl.h
3+
noinst_HEADERS += src/modules/silentpayments/dleq_impl.h
34
noinst_HEADERS += src/modules/silentpayments/bench_impl.h
45
noinst_HEADERS += src/modules/silentpayments/tests_impl.h
56
noinst_HEADERS += src/modules/silentpayments/vectors.h
Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
#ifndef SECP256K1_DLEQ_IMPL_H
2+
#define SECP256K1_DLEQ_IMPL_H
3+
4+
/* Initializes SHA256 with fixed midstate. This midstate was computed by applying
5+
* SHA256 to SHA256("BIP0374/aux")||SHA256("BIP0374/aux"). */
6+
static void secp256k1_nonce_function_bip374_sha256_tagged_aux(secp256k1_sha256 *sha) {
7+
secp256k1_sha256_initialize(sha);
8+
sha->s[0] = 0x48479343ul;
9+
sha->s[1] = 0xa9eb648cul;
10+
sha->s[2] = 0x58952fe4ul;
11+
sha->s[3] = 0x4772d3b2ul;
12+
sha->s[4] = 0x977ab0a0ul;
13+
sha->s[5] = 0xcb8e2740ul;
14+
sha->s[6] = 0x60bb4b81ul;
15+
sha->s[7] = 0x68a41b66ul;
16+
17+
sha->bytes = 64;
18+
}
19+
20+
/* Initializes SHA256 with fixed midstate. This midstate was computed by applying
21+
* SHA256 to SHA256("BIP0374/nonce")||SHA256("BIP0374/nonce"). */
22+
static void secp256k1_nonce_function_bip374_sha256_tagged(secp256k1_sha256 *sha) {
23+
secp256k1_sha256_initialize(sha);
24+
sha->s[0] = 0xa810fc87ul;
25+
sha->s[1] = 0x3b4a4d2aul;
26+
sha->s[2] = 0xe302cfb4ul;
27+
sha->s[3] = 0x322df1a0ul;
28+
sha->s[4] = 0xd2e7fb82ul;
29+
sha->s[5] = 0x7808570dul;
30+
sha->s[6] = 0x9c33e0cdul;
31+
sha->s[7] = 0x2dfbf7f6ul;
32+
33+
sha->bytes = 64;
34+
}
35+
36+
/* Initializes SHA256 with fixed midstate. This midstate was computed by applying
37+
* SHA256 to SHA256("BIP0374/challenge")||SHA256("BIP0374/challenge"). */
38+
static void secp256k1_dleq_sha256_tagged(secp256k1_sha256 *sha) {
39+
secp256k1_sha256_initialize(sha);
40+
sha->s[0] = 0x24f1c9c7ul;
41+
sha->s[1] = 0xd1538c75ul;
42+
sha->s[2] = 0xc9874ae8ul;
43+
sha->s[3] = 0x6566de76ul;
44+
sha->s[4] = 0x487843c9ul;
45+
sha->s[5] = 0xc13d8026ul;
46+
sha->s[6] = 0x39a2f3eful;
47+
sha->s[7] = 0x2ad0fcb3ul;
48+
49+
sha->bytes = 64;
50+
}
51+
52+
static int secp256k1_dleq_hash_point(secp256k1_sha256 *sha, secp256k1_ge *p) {
53+
unsigned char buf[33];
54+
size_t size = 33;
55+
if (!secp256k1_eckey_pubkey_serialize(p, buf, &size, 1)) {
56+
return 0;
57+
}
58+
secp256k1_sha256_write(sha, buf, size);
59+
return 1;
60+
}
61+
62+
static void secp256k1_nonce_function_dleq(unsigned char *nonce32, const unsigned char *msg, size_t msglen, const unsigned char *key32, const unsigned char *aux_rand32) {
63+
secp256k1_sha256 sha;
64+
unsigned char masked_key[32];
65+
int i;
66+
67+
if (aux_rand32 != NULL) {
68+
secp256k1_nonce_function_bip374_sha256_tagged_aux(&sha);
69+
secp256k1_sha256_write(&sha, aux_rand32, 32);
70+
secp256k1_sha256_finalize(&sha, masked_key);
71+
for (i = 0; i < 32; i++) {
72+
masked_key[i] ^= key32[i];
73+
}
74+
} else {
75+
/* Precomputed TaggedHash("BIP0374/aux", 0x0000...00); */
76+
static const unsigned char ZERO_MASK[32] = {
77+
38, 255, 199, 133, 21, 94, 75, 99,
78+
18, 166, 0, 53, 197, 146, 253, 84,
79+
197, 228, 235, 145, 124, 59, 203, 21,
80+
66, 88, 250, 253, 207, 123, 43, 55
81+
};
82+
for (i = 0; i < 32; i++) {
83+
masked_key[i] = key32[i] ^ ZERO_MASK[i];
84+
}
85+
}
86+
87+
secp256k1_nonce_function_bip374_sha256_tagged(&sha);
88+
/* Hash masked-key||msg using the tagged hash as per the spec */
89+
secp256k1_sha256_write(&sha, masked_key, 32);
90+
secp256k1_sha256_write(&sha, msg, msglen);
91+
secp256k1_sha256_finalize(&sha, nonce32);
92+
}
93+
94+
/* Generates a nonce as defined in BIP0374 */
95+
static int secp256k1_dleq_nonce(secp256k1_scalar *k, const unsigned char *a32, const unsigned char *A_33, const unsigned char *C_33, const unsigned char *aux_rand32) {
96+
unsigned char buf[66];
97+
unsigned char nonce[32];
98+
99+
memcpy(buf, A_33, 33);
100+
memcpy(buf + 33, C_33, 33);
101+
secp256k1_nonce_function_dleq(nonce, buf, 66, a32, aux_rand32);
102+
103+
secp256k1_scalar_set_b32(k, nonce, NULL);
104+
if (secp256k1_scalar_is_zero(k)) {
105+
return 0;
106+
}
107+
108+
return 1;
109+
}
110+
111+
/* Generates a challenge as defined in BIP0374 */
112+
static void secp256k1_dleq_challenge(secp256k1_scalar *e, secp256k1_ge *B, secp256k1_ge *R1, secp256k1_ge *R2, secp256k1_ge *A, secp256k1_ge *C, const unsigned char *m) {
113+
unsigned char buf[32];
114+
secp256k1_sha256 sha;
115+
secp256k1_ge generator_point = secp256k1_ge_const_g;
116+
117+
secp256k1_dleq_sha256_tagged(&sha);
118+
secp256k1_dleq_hash_point(&sha, A);
119+
secp256k1_dleq_hash_point(&sha, B);
120+
secp256k1_dleq_hash_point(&sha, C);
121+
secp256k1_dleq_hash_point(&sha, &generator_point);
122+
secp256k1_dleq_hash_point(&sha, R1);
123+
secp256k1_dleq_hash_point(&sha, R2);
124+
if (m) secp256k1_sha256_write(&sha, m, 32);
125+
secp256k1_sha256_finalize(&sha, buf);
126+
127+
secp256k1_scalar_set_b32(e, buf, NULL);
128+
}
129+
130+
/* Generate points from scalar a such that A = a*G and C = a*B */
131+
static void secp256k1_dleq_pair(const secp256k1_ecmult_gen_context *ecmult_gen_ctx, secp256k1_ge *A, secp256k1_ge *C, const secp256k1_scalar *a, const secp256k1_ge *B) {
132+
secp256k1_gej Aj, Cj;
133+
134+
secp256k1_ecmult_gen(ecmult_gen_ctx, &Aj, a);
135+
secp256k1_ge_set_gej(A, &Aj);
136+
secp256k1_ecmult_const(&Cj, B, a);
137+
secp256k1_ge_set_gej(C, &Cj);
138+
}
139+
140+
/* DLEQ Proof Generation
141+
*
142+
* For given elliptic curve points A, B, C, and G, the prover generates a proof to prove knowledge of a scalar a such
143+
* that A = a⋅G and C = a⋅B without revealing anything about a.
144+
*
145+
* Returns: 1 if proof creation was successful. 0 if an error occurred.
146+
* Out: scalar e: part of proof = bytes(32, e) || bytes(32, s).
147+
* scalar s: other part of proof = bytes(32, e) || bytes(32, s).
148+
* In: a : scalar a to be proven that both A and C were generated from
149+
* B : point on the curve
150+
* A : point on the curve(a⋅G) generated from a
151+
* C : point on the curve(a⋅B) generated from a
152+
* aux_rand32 : pointer to 32-byte auxiliary randomness used to generate the nonce in secp256k1_nonce_function_dleq.
153+
* m : an optional message
154+
* */
155+
static int secp256k1_dleq_prove(const secp256k1_context *ctx, secp256k1_scalar *s, secp256k1_scalar *e, const secp256k1_scalar *a, secp256k1_ge *B, secp256k1_ge *A, secp256k1_ge *C, const unsigned char *aux_rand32, const unsigned char *m) {
156+
secp256k1_ge R1, R2;
157+
secp256k1_scalar k = { 0 };
158+
unsigned char a32[32];
159+
unsigned char A_33[33];
160+
unsigned char B_33[33];
161+
unsigned char C_33[33];
162+
int ret = 1;
163+
size_t pubkey_size = 33;
164+
165+
secp256k1_scalar_get_b32(a32, a);
166+
if (!secp256k1_eckey_pubkey_serialize(B, B_33, &pubkey_size, 1)) {
167+
return 0;
168+
}
169+
if (!secp256k1_eckey_pubkey_serialize(A, A_33, &pubkey_size, 1)) {
170+
return 0;
171+
}
172+
if (!secp256k1_eckey_pubkey_serialize(C, C_33, &pubkey_size, 1)) {
173+
return 0;
174+
}
175+
ret &= secp256k1_dleq_nonce(&k, a32, A_33, C_33, aux_rand32);
176+
177+
/* R1 = k*G, R2 = k*B */
178+
secp256k1_dleq_pair(&ctx->ecmult_gen_ctx, &R1, &R2, &k, B);
179+
/* We declassify the non-secret values R1 and R2 to allow using them as
180+
* branch points. */
181+
secp256k1_declassify(ctx, &R1, sizeof(R1));
182+
secp256k1_declassify(ctx, &R2, sizeof(R2));
183+
184+
/* e = tagged hash(A, B, C, R1, R2) */
185+
/* s = k + e * a */
186+
secp256k1_dleq_challenge(e, B, &R1, &R2, A, C, m);
187+
secp256k1_scalar_mul(s, e, a);
188+
secp256k1_scalar_add(s, s, &k);
189+
190+
secp256k1_scalar_clear(&k);
191+
return ret;
192+
}
193+
194+
/* DLEQ Proof Verification
195+
*
196+
* Verifies the proof. If the following algorithm succeeds, the points A and C were both generated from the same scalar.
197+
* The former from multiplying by G, and the latter from multiplying by B.
198+
*
199+
* Returns: 1 if proof verification was successful. 0 if an error occurred.
200+
* In: proof : proof bytes(32, e) || bytes(32, s) consists of scalar e and scalar s
201+
* A : point on the curve(a⋅G) computed from a
202+
* B : point on the curve
203+
* C : point on the curve(a⋅B) computed from a
204+
* m : optional message
205+
* */
206+
static int secp256k1_dleq_verify(secp256k1_scalar *s, secp256k1_scalar *e, secp256k1_ge *A, secp256k1_ge *B, secp256k1_ge *C, const unsigned char *m) {
207+
secp256k1_scalar e_neg;
208+
secp256k1_scalar e_expected;
209+
secp256k1_gej Bj;
210+
secp256k1_gej Aj, Cj;
211+
secp256k1_gej R1j, R2j;
212+
secp256k1_ge R1, R2;
213+
secp256k1_gej tmpj;
214+
215+
secp256k1_gej_set_ge(&Aj, A);
216+
secp256k1_gej_set_ge(&Cj, C);
217+
218+
secp256k1_scalar_negate(&e_neg, e);
219+
/* R1 = s*G - e*A */
220+
secp256k1_ecmult(&R1j, &Aj, &e_neg, s);
221+
/* R2 = s*B - e*C */
222+
secp256k1_ecmult(&tmpj, &Cj, &e_neg, &secp256k1_scalar_zero);
223+
secp256k1_gej_set_ge(&Bj, B);
224+
secp256k1_ecmult(&R2j, &Bj, s, &secp256k1_scalar_zero);
225+
secp256k1_gej_add_var(&R2j, &R2j, &tmpj, NULL);
226+
227+
secp256k1_ge_set_gej(&R1, &R1j);
228+
secp256k1_ge_set_gej(&R2, &R2j);
229+
secp256k1_dleq_challenge(&e_expected, B, &R1, &R2, A, C, m);
230+
231+
secp256k1_scalar_add(&e_expected, &e_expected, &e_neg);
232+
return secp256k1_scalar_is_zero(&e_expected);
233+
}
234+
235+
#endif

src/modules/silentpayments/tests_impl.h

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#define SECP256K1_MODULE_SILENTPAYMENTS_TESTS_H
88

99
#include "../../../include/secp256k1_silentpayments.h"
10+
#include "../../../src/modules/silentpayments/dleq_impl.h"
1011
#include "../../../src/modules/silentpayments/vectors.h"
1112
#include "include/secp256k1.h"
1213

@@ -598,12 +599,120 @@ void run_silentpayments_test_vectors(void) {
598599
}
599600
}
600601

602+
static void dleq_nonce_bitflip(unsigned char **args, size_t n_flip, size_t n_bytes) {
603+
secp256k1_scalar k1, k2;
604+
CHECK(secp256k1_dleq_nonce(&k1, args[0], args[1], args[2], args[3]) == 1);
605+
testrand_flip(args[n_flip], n_bytes);
606+
CHECK(secp256k1_dleq_nonce(&k2, args[0], args[1], args[2], args[3]) == 1);
607+
CHECK(secp256k1_scalar_eq(&k1, &k2) == 0);
608+
}
609+
610+
static void dleq_tests(void) {
611+
secp256k1_scalar s, e, a, k;
612+
secp256k1_ge A, B, C;
613+
unsigned char *args[4];
614+
unsigned char a32[32];
615+
unsigned char A_33[33];
616+
unsigned char C_33[33];
617+
unsigned char aux_rand[32];
618+
unsigned char msg[32];
619+
unsigned char proof_64[64] = {0};
620+
int i;
621+
size_t pubkey_size = 33;
622+
int overflow;
623+
secp256k1_sha256 sha;
624+
secp256k1_sha256 sha_optimized;
625+
unsigned char aux_tag[] = {'B', 'I', 'P', '0', '3', '7', '4', '/', 'a', 'u', 'x'};
626+
unsigned char tag[] = {'B', 'I', 'P', '0', '3', '7', '4', '/', 'n', 'o', 'n', 'c', 'e'};
627+
unsigned char challenge_tag[] = {'B', 'I', 'P', '0', '3', '7', '4', '/', 'c', 'h', 'a', 'l', 'l', 'e', 'n', 'g', 'e'};
628+
629+
/* Check that hash initialized by secp256k1_nonce_function_bip374_sha256_tagged_aux has the expected state. */
630+
secp256k1_sha256_initialize_tagged(&sha, aux_tag, sizeof(aux_tag));
631+
secp256k1_nonce_function_bip374_sha256_tagged_aux(&sha_optimized);
632+
test_sha256_eq(&sha, &sha_optimized);
633+
634+
/* Check that hash initialized by secp256k1_nonce_function_bip374_sha256_tagged has the expected state. */
635+
secp256k1_sha256_initialize_tagged(&sha, tag, sizeof(tag));
636+
secp256k1_nonce_function_bip374_sha256_tagged(&sha_optimized);
637+
test_sha256_eq(&sha, &sha_optimized);
638+
639+
/* Check that hash initialized by secp256k1_dleq_sha256_tagged has the expected state. */
640+
secp256k1_sha256_initialize_tagged(&sha, challenge_tag, sizeof(challenge_tag));
641+
secp256k1_dleq_sha256_tagged(&sha_optimized);
642+
test_sha256_eq(&sha, &sha_optimized);
643+
644+
for (i = 0; i < COUNT; i++) {
645+
testutil_random_ge_test(&B);
646+
testutil_random_scalar_order(&a);
647+
testrand256(aux_rand);
648+
testrand_bytes_test(msg, sizeof(msg));
649+
secp256k1_dleq_pair(&CTX->ecmult_gen_ctx, &A, &C, &a, &B);
650+
CHECK(secp256k1_dleq_prove(CTX, &s, &e, &a, &B, &A, &C, aux_rand, (i & 1) ? msg : NULL) == 1);
651+
CHECK(secp256k1_dleq_verify(&s, &e, &A, &B, &C, (i & 1) ? msg : NULL) == 1);
652+
secp256k1_scalar_set_b32(&s, proof_64, &overflow);
653+
VERIFY_CHECK(overflow == 0);
654+
secp256k1_scalar_set_b32(&e, proof_64 + 32, &overflow);
655+
VERIFY_CHECK(overflow == 0);
656+
}
657+
658+
{
659+
secp256k1_scalar tmp;
660+
secp256k1_scalar_set_int(&tmp, 1);
661+
CHECK(secp256k1_dleq_verify(&tmp, &e, &A, &B, &C, msg) == 0);
662+
CHECK(secp256k1_dleq_verify(&s, &tmp, &A, &B, &C, msg) == 0);
663+
}
664+
{
665+
secp256k1_ge p_tmp;
666+
testutil_random_ge_test(&p_tmp);
667+
CHECK(secp256k1_dleq_verify(&s, &e, &p_tmp, &B, &C, msg) == 0);
668+
CHECK(secp256k1_dleq_verify(&s, &e, &A, &p_tmp, &C, msg) == 0);
669+
CHECK(secp256k1_dleq_verify(&s, &e, &A, &B, &p_tmp, msg) == 0);
670+
}
671+
{
672+
secp256k1_ge p_inf;
673+
secp256k1_ge_set_infinity(&p_inf);
674+
CHECK(secp256k1_dleq_prove(CTX, &s, &e, &a, &p_inf, &A, &C, aux_rand, msg) == 0);
675+
CHECK(secp256k1_dleq_prove(CTX, &s, &e, &a, &B, &p_inf, &C, aux_rand, msg) == 0);
676+
CHECK(secp256k1_dleq_prove(CTX, &s, &e, &a, &B, &A, &p_inf, aux_rand, msg) == 0);
677+
}
678+
679+
/* Nonce tests */
680+
secp256k1_scalar_get_b32(a32, &a);
681+
CHECK(secp256k1_eckey_pubkey_serialize(&A, A_33, &pubkey_size, 1));
682+
CHECK(secp256k1_eckey_pubkey_serialize(&C, C_33, &pubkey_size, 1));
683+
CHECK(secp256k1_dleq_nonce(&k, a32, A_33, C_33, aux_rand) == 1);
684+
685+
testrand_bytes_test(a32, sizeof(a32));
686+
testrand_bytes_test(A_33, sizeof(A_33));
687+
testrand_bytes_test(C_33, sizeof(C_33));
688+
testrand_bytes_test(aux_rand, sizeof(aux_rand));
689+
690+
/* Check that a bitflip in an argument results in different nonces. */
691+
args[0] = a32;
692+
args[1] = A_33;
693+
args[2] = C_33;
694+
args[3] = aux_rand;
695+
for (i = 0; i < COUNT; i++) {
696+
dleq_nonce_bitflip(args, 0, sizeof(a32));
697+
dleq_nonce_bitflip(args, 1, sizeof(A_33));
698+
/* Flip C */
699+
dleq_nonce_bitflip(args, 2, sizeof(C_33));
700+
/* Flip C again */
701+
dleq_nonce_bitflip(args, 2, sizeof(C_33));
702+
dleq_nonce_bitflip(args, 3, sizeof(aux_rand));
703+
}
704+
705+
/* NULL aux_rand argument is allowed.*/
706+
CHECK(secp256k1_dleq_nonce(&k, a32, A_33, C_33, NULL) == 1);
707+
}
708+
601709
void run_silentpayments_tests(void) {
602710
test_recipient_sort();
603711
test_send_api();
604712
test_label_api();
605713
test_recipient_api();
606714
run_silentpayments_test_vectors();
715+
dleq_tests();
607716
}
608717

609718
#endif

0 commit comments

Comments
 (0)