mpt-crypto
Confidential Multi-Purpose Tokens Cryptographic Library
Loading...
Searching...
No Matches
proof_same_plaintext_multi_shared_r.c
Go to the documentation of this file.
1
42#include "secp256k1_mpt.h"
43#include <openssl/sha.h>
44#include <openssl/rand.h>
45#include <string.h>
46#include <stdlib.h>
47
48/* --- Internal Helpers --- */
49
50static int pubkey_equal(const secp256k1_context* ctx, const secp256k1_pubkey* pk1, const secp256k1_pubkey* pk2) {
51 return secp256k1_ec_pubkey_cmp(ctx, pk1, pk2) == 0;
52}
53
54static int generate_random_scalar(const secp256k1_context* ctx, unsigned char* scalar) {
55 do {
56 if (RAND_bytes(scalar, 32) != 1) return 0;
57 } while (!secp256k1_ec_seckey_verify(ctx, scalar));
58 return 1;
59}
60
61size_t secp256k1_mpt_proof_equality_shared_r_size(size_t n_recipients) {
62 // Tr (33) + N * Tm_i (33*N) + sm (32) + sr (32)
63 return (33 * (n_recipients + 1)) + 64;
64}
65
66/*
67 * Hash( Domain || C1 || {C2_i, Pk_i} || Tr || {Tm_i} || ContextID )
68 */
70 const secp256k1_context* ctx,
71 unsigned char* e_out,
72 size_t n,
73 const secp256k1_pubkey* C1,
74 const secp256k1_pubkey* C2_vec,
75 const secp256k1_pubkey* Pk_vec,
76 const secp256k1_pubkey* Tr,
77 const secp256k1_pubkey* Tm_vec,
78 const unsigned char* context_id
79) {
80 SHA256_CTX sha;
81 unsigned char buf[33];
82 unsigned char h[32];
83 size_t len;
84 size_t i;
85 const char* domain = "MPT_POK_SAME_PLAINTEXT_SHARED_R";
86
87 SHA256_Init(&sha);
88 SHA256_Update(&sha, domain, strlen(domain));
89
90 /* 1. Shared C1 */
91 len = 33; secp256k1_ec_pubkey_serialize(ctx, buf, &len, C1, SECP256K1_EC_COMPRESSED);
92 SHA256_Update(&sha, buf, 33);
93
94 /* 2. Pairs {C2_i, Pk_i} */
95 for (i = 0; i < n; i++) {
96 len = 33; secp256k1_ec_pubkey_serialize(ctx, buf, &len, &C2_vec[i], SECP256K1_EC_COMPRESSED);
97 SHA256_Update(&sha, buf, 33);
98 len = 33; secp256k1_ec_pubkey_serialize(ctx, buf, &len, &Pk_vec[i], SECP256K1_EC_COMPRESSED);
99 SHA256_Update(&sha, buf, 33);
100 }
101
102 /* 3. Commitment Tr */
103 len = 33; secp256k1_ec_pubkey_serialize(ctx, buf, &len, Tr, SECP256K1_EC_COMPRESSED);
104 SHA256_Update(&sha, buf, 33);
105
106 /* 4. Commitments {Tm_i} */
107 for (i = 0; i < n; i++) {
108 len = 33; secp256k1_ec_pubkey_serialize(ctx, buf, &len, &Tm_vec[i], SECP256K1_EC_COMPRESSED);
109 SHA256_Update(&sha, buf, 33);
110 }
111
112 /* 5. Transaction Context */
113 if (context_id) {
114 SHA256_Update(&sha, context_id, 32);
115 }
116
117 SHA256_Final(h, &sha);
119}
120
121/* --- Public API --- */
122
124 const secp256k1_context* ctx,
125 unsigned char* proof_out, // Caller MUST allocate secp256k1_mpt_proof_equality_shared_r_size(n)
126 uint64_t amount,
127 const unsigned char* r_shared,
128 size_t n,
129 const secp256k1_pubkey* C1,
130 const secp256k1_pubkey* C2_vec,
131 const secp256k1_pubkey* Pk_vec,
132 const unsigned char* context_id
133) {
134 /* Local Variables */
135 unsigned char k_m[32], k_r[32];
136 unsigned char m_scalar[32] = {0};
137 unsigned char e[32];
138 unsigned char s_m[32], s_r[32];
139 unsigned char term[32];
140
141 secp256k1_pubkey Tr;
142 secp256k1_pubkey* Tm_vec = NULL;
143 int ok = 0;
144 size_t i;
145 unsigned char* ptr = proof_out;
146 size_t len;
147
148 /* 0. Validate Witness */
149 if (!secp256k1_ec_seckey_verify(ctx, r_shared)) return 0;
150
151 /* Allocate memory for commitments */
152 if (n > 0) {
153 Tm_vec = (secp256k1_pubkey*)malloc(sizeof(secp256k1_pubkey) * n);
154 if (!Tm_vec) return 0;
155 }
156
157 /* 1. Prepare Witness */
158 for (i = 0; i < 8; i++) {
159 m_scalar[31 - i] = (amount >> (i * 8)) & 0xFF;
160 }
161
162 /* 2. Sample Random Nonces */
163 if (!generate_random_scalar(ctx, k_m)) goto cleanup;
164 if (!generate_random_scalar(ctx, k_r)) goto cleanup;
165
166 /* 3. Compute Commitments */
167
168 /* Tr = kr * G */
169 if (!secp256k1_ec_pubkey_create(ctx, &Tr, k_r)) goto cleanup;
170
171 /* Tm_i = km * G + kr * Pk_i */
172 secp256k1_pubkey kmG;
173 if (!secp256k1_ec_pubkey_create(ctx, &kmG, k_m)) goto cleanup;
174
175 for (i = 0; i < n; i++) {
176 secp256k1_pubkey krPk = Pk_vec[i];
177 if (!secp256k1_ec_pubkey_tweak_mul(ctx, &krPk, k_r)) goto cleanup; // kr * Pk
178
179 const secp256k1_pubkey* pts[2] = {&kmG, &krPk};
180 if (!secp256k1_ec_pubkey_combine(ctx, &Tm_vec[i], pts, 2)) goto cleanup;
181 }
182
183 /* 4. Compute Challenge */
184 compute_challenge_equality_shared_r(ctx, e, n, C1, C2_vec, Pk_vec, &Tr, Tm_vec, context_id);
185
186 /* 5. Compute Responses */
187
188 /* s_m = k_m + e * m */
189 memcpy(s_m, k_m, 32);
190 memcpy(term, m_scalar, 32);
191 if (!secp256k1_ec_seckey_tweak_mul(ctx, term, e)) goto cleanup;
192 if (!secp256k1_ec_seckey_tweak_add(ctx, s_m, term)) goto cleanup;
193
194 /* s_r = k_r + e * r */
195 memcpy(s_r, k_r, 32);
196 memcpy(term, r_shared, 32);
197 if (!secp256k1_ec_seckey_tweak_mul(ctx, term, e)) goto cleanup;
198 if (!secp256k1_ec_seckey_tweak_add(ctx, s_r, term)) goto cleanup;
199
200 /* 6. Serialize Proof */
201
202 /* Serialize Tr */
203 len = 33;
204 if (!secp256k1_ec_pubkey_serialize(ctx, ptr, &len, &Tr, SECP256K1_EC_COMPRESSED)) goto cleanup;
205 ptr += 33;
206
207 /* Serialize Tm_i array */
208 for (i = 0; i < n; i++) {
209 len = 33;
210 if (!secp256k1_ec_pubkey_serialize(ctx, ptr, &len, &Tm_vec[i], SECP256K1_EC_COMPRESSED)) goto cleanup;
211 ptr += 33;
212 }
213
214 /* Serialize Scalars */
215 memcpy(ptr, s_m, 32); ptr += 32;
216 memcpy(ptr, s_r, 32); ptr += 32;
217
218 ok = 1;
219
220 cleanup:
221 // Wipe Secrets
222 OPENSSL_cleanse(k_m, 32);
223 OPENSSL_cleanse(k_r, 32);
224 OPENSSL_cleanse(m_scalar, 32);
225
226 // Wipe Intermediates
227 OPENSSL_cleanse(term, 32);
228 OPENSSL_cleanse(s_m, 32);
229 OPENSSL_cleanse(s_r, 32);
230
231 if (Tm_vec) free(Tm_vec);
232 return ok;
233}
235 const secp256k1_context* ctx,
236 const unsigned char* proof, // Caller MUST provide buffer of size: secp256k1_mpt_proof_equality_shared_r_size(n)
237 size_t n,
238 const secp256k1_pubkey* C1,
239 const secp256k1_pubkey* C2_vec,
240 const secp256k1_pubkey* Pk_vec,
241 const unsigned char* context_id
242) {
243 /* Calculate expected size internally for strict checking later */
244 size_t expected_len = secp256k1_mpt_proof_equality_shared_r_size(n);
245
246 /* Local Variables */
247 secp256k1_pubkey Tr;
248 secp256k1_pubkey* Tm_vec = NULL;
249 unsigned char s_m[32], s_r[32];
250 unsigned char e[32];
251 int ok = 0;
252 size_t i;
253 const unsigned char* ptr = proof;
254
255 /* Allocate memory for commitments */
256 Tm_vec = (secp256k1_pubkey*)malloc(sizeof(secp256k1_pubkey) * n);
257 if (!Tm_vec) return 0;
258
259 /* 1. Deserialize Proof */
260
261 // Parse Tr (33 bytes)
262 if (!secp256k1_ec_pubkey_parse(ctx, &Tr, ptr, 33)) goto cleanup;
263 ptr += 33;
264
265 // Parse N commitments (N * 33 bytes)
266 for (i = 0; i < n; i++) {
267 if (!secp256k1_ec_pubkey_parse(ctx, &Tm_vec[i], ptr, 33)) goto cleanup;
268 ptr += 33;
269 }
270
271 // Parse Scalars (32 + 32 bytes)
272 memcpy(s_m, ptr, 32); ptr += 32;
273 memcpy(s_r, ptr, 32); ptr += 32;
274
275 // Sanity check scalars
276 if (!secp256k1_ec_seckey_verify(ctx, s_m)) goto cleanup;
277 if (!secp256k1_ec_seckey_verify(ctx, s_r)) goto cleanup;
278
279 /* 2. Challenge */
280 compute_challenge_equality_shared_r(ctx, e, n, C1, C2_vec, Pk_vec, &Tr, Tm_vec, context_id);
281
282 /* 3. Verification Equations */
283
284 /* Eq 1: sr * G == Tr + e * C1 */
285 {
286 secp256k1_pubkey LHS, RHS;
287 secp256k1_pubkey eC1 = *C1;
288
289 if (!secp256k1_ec_pubkey_create(ctx, &LHS, s_r)) goto cleanup; // sr*G
290
291 if (!secp256k1_ec_pubkey_tweak_mul(ctx, &eC1, e)) goto cleanup; // e*C1
292 const secp256k1_pubkey* pts[2] = {&Tr, &eC1};
293 if (!secp256k1_ec_pubkey_combine(ctx, &RHS, pts, 2)) goto cleanup; // Tr + e*C1
294
295 if (!pubkey_equal(ctx, &LHS, &RHS)) goto cleanup;
296 }
297
298 /* Eq 2: For each i, sm * G + sr * Pk_i == Tm_i + e * C2_i */
299 {
300 secp256k1_pubkey smG;
301 if (!secp256k1_ec_pubkey_create(ctx, &smG, s_m)) goto cleanup; // Precompute sm*G
302
303 for (i = 0; i < n; i++) {
304 secp256k1_pubkey LHS, RHS;
305
306 /* LHS = sm*G + sr*Pk_i */
307 secp256k1_pubkey srPk = Pk_vec[i];
308 if (!secp256k1_ec_pubkey_tweak_mul(ctx, &srPk, s_r)) goto cleanup;
309
310 const secp256k1_pubkey* lhs_pts[2] = {&smG, &srPk};
311 if (!secp256k1_ec_pubkey_combine(ctx, &LHS, lhs_pts, 2)) goto cleanup;
312
313 /* RHS = Tm_i + e*C2_i */
314 secp256k1_pubkey eC2 = C2_vec[i];
315 if (!secp256k1_ec_pubkey_tweak_mul(ctx, &eC2, e)) goto cleanup;
316
317 const secp256k1_pubkey* rhs_pts[2] = {&Tm_vec[i], &eC2};
318 if (!secp256k1_ec_pubkey_combine(ctx, &RHS, rhs_pts, 2)) goto cleanup;
319
320 if (!pubkey_equal(ctx, &LHS, &RHS)) goto cleanup;
321 }
322 }
323
324 // Strict Length Check: Ensure we read exactly what was expected
325 if ((size_t)(ptr - proof) != expected_len) goto cleanup;
326
327 ok = 1;
328
329 cleanup:
330 if (Tm_vec) free(Tm_vec);
331 return ok;
332}
size_t secp256k1_mpt_proof_equality_shared_r_size(size_t n_recipients)
static void compute_challenge_equality_shared_r(const secp256k1_context *ctx, unsigned char *e_out, size_t n, const secp256k1_pubkey *C1, const secp256k1_pubkey *C2_vec, const secp256k1_pubkey *Pk_vec, const secp256k1_pubkey *Tr, const secp256k1_pubkey *Tm_vec, const unsigned char *context_id)
int secp256k1_mpt_prove_equality_shared_r(const secp256k1_context *ctx, unsigned char *proof_out, uint64_t amount, const unsigned char *r_shared, size_t n, const secp256k1_pubkey *C1, const secp256k1_pubkey *C2_vec, const secp256k1_pubkey *Pk_vec, const unsigned char *context_id)
static int pubkey_equal(const secp256k1_context *ctx, const secp256k1_pubkey *pk1, const secp256k1_pubkey *pk2)
int secp256k1_mpt_verify_equality_shared_r(const secp256k1_context *ctx, const unsigned char *proof, size_t n, const secp256k1_pubkey *C1, const secp256k1_pubkey *C2_vec, const secp256k1_pubkey *Pk_vec, const unsigned char *context_id)
static int generate_random_scalar(const secp256k1_context *ctx, unsigned char *scalar)
void secp256k1_mpt_scalar_reduce32(unsigned char out32[32], const unsigned char in32[32])
Definition mpt_scalar.c:106