mpt-crypto
Confidential Multi-Purpose Tokens Cryptographic Library
Loading...
Searching...
No Matches
proof_same_plaintext_multi.c
Go to the documentation of this file.
1
46#include "secp256k1_mpt.h"
47#include <openssl/sha.h>
48#include <openssl/rand.h>
49#include <string.h>
50#include <stdlib.h>
51
52/* Helper for comparing public keys (from internal utils) */
53static int pubkey_equal(const secp256k1_context* ctx, const secp256k1_pubkey* pk1, const secp256k1_pubkey* pk2) {
54 return secp256k1_ec_pubkey_cmp(ctx, pk1, pk2) == 0;
55}
56
57static int generate_random_scalar(const secp256k1_context* ctx, unsigned char* scalar) {
58 do {
59 if (RAND_bytes(scalar, 32) != 1) return 0;
60 } while (!secp256k1_ec_seckey_verify(ctx, scalar));
61 return 1;
62}
63
64/*
65 * Hash( Domain || {R_i, S_i, Pk_i} || Tm || {TrG_i, TrP_i} || TxID )
66 */
68 const secp256k1_context* ctx,
69 unsigned char* e_out,
70 size_t n,
71 const secp256k1_pubkey* R,
72 const secp256k1_pubkey* S,
73 const secp256k1_pubkey* Pk,
74 const secp256k1_pubkey* Tm,
75 const secp256k1_pubkey* TrG,
76 const secp256k1_pubkey* TrP,
77 const unsigned char* tx_id
78) {
79 SHA256_CTX sha;
80 unsigned char buf[33];
81 unsigned char h[32];
82 size_t len;
83 size_t i;
84 const char* domain = "MPT_POK_SAME_PLAINTEXT_PROOF";
85
86 SHA256_Init(&sha);
87 SHA256_Update(&sha, domain, strlen(domain));
88
89 /* 1. Public Inputs */
90 for (i = 0; i < n; ++i) {
91 len = 33;
92 secp256k1_ec_pubkey_serialize(ctx, buf, &len, &R[i], SECP256K1_EC_COMPRESSED);
93 SHA256_Update(&sha, buf, 33);
94
95 len = 33;
96 secp256k1_ec_pubkey_serialize(ctx, buf, &len, &S[i], SECP256K1_EC_COMPRESSED);
97 SHA256_Update(&sha, buf, 33);
98
99 len = 33;
100 secp256k1_ec_pubkey_serialize(ctx, buf, &len, &Pk[i], SECP256K1_EC_COMPRESSED);
101 SHA256_Update(&sha, buf, 33);
102 }
103
104 /* 2. Commitments */
105 len = 33;
106 secp256k1_ec_pubkey_serialize(ctx, buf, &len, Tm, SECP256K1_EC_COMPRESSED);
107 SHA256_Update(&sha, buf, 33);
108
109 for (i = 0; i < n; ++i) {
110 len = 33;
111 secp256k1_ec_pubkey_serialize(ctx, buf, &len, &TrG[i], SECP256K1_EC_COMPRESSED);
112 SHA256_Update(&sha, buf, 33);
113
114 len = 33;
115 secp256k1_ec_pubkey_serialize(ctx, buf, &len, &TrP[i], SECP256K1_EC_COMPRESSED);
116 SHA256_Update(&sha, buf, 33);
117 }
118
119 /* 3. Context */
120 if (tx_id) {
121 SHA256_Update(&sha, tx_id, 32);
122 }
123
124 SHA256_Final(h, &sha);
126}
127
128/* --- Public API --- */
129
131 // (1 Tm + 2N Tr) * 33 + (1 sm + N sr) * 32
132 return ((1 + 2 * n) * 33) + ((1 + n) * 32);
133}
134
136 const secp256k1_context* ctx,
137 unsigned char* proof_out,
138 size_t* proof_len,
139 uint64_t amount_m,
140 size_t n,
141 const secp256k1_pubkey* R,
142 const secp256k1_pubkey* S,
143 const secp256k1_pubkey* Pk,
144 const unsigned char* r_array,
145 const unsigned char* tx_id
146) {
147 size_t required_len = secp256k1_mpt_prove_same_plaintext_multi_size(n);
148 if (!proof_len || *proof_len < required_len) {
149 if (proof_len) *proof_len = required_len;
150 return 0;
151 }
152 *proof_len = required_len;
153
154 /* Heap Allocation to avoid Stack Overflow on large N */
155 unsigned char* k_r_flat = NULL; // Stores n * 32 bytes
156 secp256k1_pubkey* TrG = NULL;
157 secp256k1_pubkey* TrP = NULL;
158
159 unsigned char k_m[32];
160 secp256k1_pubkey Tm;
161 unsigned char e[32];
162 unsigned char s_m[32];
163
164 int ok = 0;
165 size_t i;
166 unsigned char* ptr = proof_out;
167
168 /* Allocations */
169 k_r_flat = (unsigned char*)malloc(n * 32);
170 TrG = (secp256k1_pubkey*)malloc(n * sizeof(secp256k1_pubkey));
171 TrP = (secp256k1_pubkey*)malloc(n * sizeof(secp256k1_pubkey));
172
173 if (!k_r_flat || !TrG || !TrP) goto cleanup;
174
175 /* 1. Generate Randomness & Commitments */
176
177 // km -> Tm = km * G
178 if (!generate_random_scalar(ctx, k_m)) goto cleanup;
179 if (!secp256k1_ec_pubkey_create(ctx, &Tm, k_m)) goto cleanup;
180
181 for (i = 0; i < n; i++) {
182 unsigned char* kri = &k_r_flat[i * 32];
183
184 // kri -> TrG = kri * G
185 if (!generate_random_scalar(ctx, kri)) goto cleanup;
186 if (!secp256k1_ec_pubkey_create(ctx, &TrG[i], kri)) goto cleanup;
187
188 // TrP = kri * Pk_i
189 TrP[i] = Pk[i];
190 if (!secp256k1_ec_pubkey_tweak_mul(ctx, &TrP[i], kri)) goto cleanup;
191 }
192
193 /* 2. Compute Challenge */
194 compute_challenge_multi(ctx, e, n, R, S, Pk, &Tm, TrG, TrP, tx_id);
195
196 /* 3. Compute Responses */
197
198 // s_m = k_m + e * m
199 {
200 unsigned char m_scalar[32] = {0};
201 // Convert uint64 to big-endian 32-byte
202 for (i = 0; i < 8; ++i) m_scalar[31 - i] = (amount_m >> (i * 8)) & 0xFF;
203
204 memcpy(s_m, k_m, 32);
205 if (!secp256k1_ec_seckey_tweak_mul(ctx, m_scalar, e)) goto cleanup; // m*e
206 if (!secp256k1_ec_seckey_tweak_add(ctx, s_m, m_scalar)) goto cleanup; // km + m*e
207 }
208
209 // Serialize Points first (Protocol Format)
210 size_t len = 33;
211 secp256k1_ec_pubkey_serialize(ctx, ptr, &len, &Tm, SECP256K1_EC_COMPRESSED); ptr += 33;
212 for(i=0; i<n; ++i) {
213 secp256k1_ec_pubkey_serialize(ctx, ptr, &len, &TrG[i], SECP256K1_EC_COMPRESSED); ptr += 33;
214 }
215 for(i=0; i<n; ++i) {
216 secp256k1_ec_pubkey_serialize(ctx, ptr, &len, &TrP[i], SECP256K1_EC_COMPRESSED); ptr += 33;
217 }
218
219 // Serialize sm
220 memcpy(ptr, s_m, 32); ptr += 32;
221
222 // Calculate and Serialize sri = kri + e * ri
223 for (i = 0; i < n; i++) {
224 unsigned char s_ri[32];
225 unsigned char term[32];
226
227 memcpy(s_ri, &k_r_flat[i*32], 32); // k_ri
228 memcpy(term, &r_array[i*32], 32); // r_i
229
230 if (!secp256k1_ec_seckey_tweak_mul(ctx, term, e)) goto cleanup; // r*e
231 if (!secp256k1_ec_seckey_tweak_add(ctx, s_ri, term)) goto cleanup; // k + r*e
232
233 memcpy(ptr, s_ri, 32); ptr += 32;
234 }
235
236 ok = 1;
237
238 cleanup:
239 if (k_r_flat) {
240 // Secure wipe of randomness
241 OPENSSL_cleanse(k_r_flat, n * 32);
242 free(k_r_flat);
243 }
244 OPENSSL_cleanse(k_m, 32);
245 if (TrG) free(TrG);
246 if (TrP) free(TrP);
247 return ok;
248}
249
251 const secp256k1_context* ctx,
252 const unsigned char* proof,
253 size_t proof_len,
254 size_t n,
255 const secp256k1_pubkey* R,
256 const secp256k1_pubkey* S,
257 const secp256k1_pubkey* Pk,
258 const unsigned char* tx_id
259) {
260 if (proof_len != secp256k1_mpt_prove_same_plaintext_multi_size(n)) return 0;
261
262 secp256k1_pubkey Tm;
263 secp256k1_pubkey* TrG = NULL;
264 secp256k1_pubkey* TrP = NULL;
265 unsigned char s_m[32];
266 unsigned char e[32];
267 int ok = 0;
268 size_t i;
269 const unsigned char* ptr = proof;
270
271 TrG = (secp256k1_pubkey*)malloc(n * sizeof(secp256k1_pubkey));
272 TrP = (secp256k1_pubkey*)malloc(n * sizeof(secp256k1_pubkey));
273 if (!TrG || !TrP) goto cleanup;
274
275 /* 1. Deserialize */
276 if (!secp256k1_ec_pubkey_parse(ctx, &Tm, ptr, 33)) goto cleanup; ptr += 33;
277
278 for(i=0; i<n; ++i) {
279 if (!secp256k1_ec_pubkey_parse(ctx, &TrG[i], ptr, 33)) goto cleanup; ptr += 33;
280 }
281 for(i=0; i<n; ++i) {
282 if (!secp256k1_ec_pubkey_parse(ctx, &TrP[i], ptr, 33)) goto cleanup; ptr += 33;
283 }
284
285 memcpy(s_m, ptr, 32); ptr += 32;
286 if (!secp256k1_ec_seckey_verify(ctx, s_m)) goto cleanup;
287
288 /* 2. Recompute Challenge */
289 compute_challenge_multi(ctx, e, n, R, S, Pk, &Tm, TrG, TrP, tx_id);
290
291 /* 3. Verify Equations */
292
293 /* Precompute s_m * G (Shared across all i) */
294 secp256k1_pubkey SmG;
295 if (!secp256k1_ec_pubkey_create(ctx, &SmG, s_m)) goto cleanup;
296
297 for(i=0; i<n; ++i) {
298 unsigned char s_ri[32];
299 memcpy(s_ri, ptr, 32); ptr += 32;
300 if (!secp256k1_ec_seckey_verify(ctx, s_ri)) goto cleanup;
301
302 secp256k1_pubkey LHS, RHS, term;
303 const secp256k1_pubkey* pts[3];
304
305 /* --- Eq 1: s_ri * G == TrG_i + e * R_i --- */
306 if (!secp256k1_ec_pubkey_create(ctx, &LHS, s_ri)) goto cleanup;
307
308 term = R[i];
309 if (!secp256k1_ec_pubkey_tweak_mul(ctx, &term, e)) goto cleanup;
310 pts[0] = &TrG[i]; pts[1] = &term;
311 if (!secp256k1_ec_pubkey_combine(ctx, &RHS, pts, 2)) goto cleanup;
312
313 if (!pubkey_equal(ctx, &LHS, &RHS)) goto cleanup;
314
315
316 /* --- Eq 2: s_m * G + s_ri * Pk_i == Tm + TrP_i + e * S_i --- */
317
318 /* LHS = SmG + s_ri * Pk_i */
319 term = Pk[i];
320 if (!secp256k1_ec_pubkey_tweak_mul(ctx, &term, s_ri)) goto cleanup;
321 pts[0] = &SmG; pts[1] = &term;
322 if (!secp256k1_ec_pubkey_combine(ctx, &LHS, pts, 2)) goto cleanup;
323
324 /* RHS = Tm + TrP_i + e * S_i */
325 term = S[i];
326 if (!secp256k1_ec_pubkey_tweak_mul(ctx, &term, e)) goto cleanup;
327 pts[0] = &Tm; pts[1] = &TrP[i]; pts[2] = &term;
328 if (!secp256k1_ec_pubkey_combine(ctx, &RHS, pts, 3)) goto cleanup;
329
330 if (!pubkey_equal(ctx, &LHS, &RHS)) goto cleanup;
331 }
332
333 if ((size_t)(ptr - proof) != proof_len) goto cleanup;
334 ok = 1;
335
336 cleanup:
337 if (TrG) free(TrG);
338 if (TrP) free(TrP);
339 return ok;
340}
size_t secp256k1_mpt_prove_same_plaintext_multi_size(size_t n)
Calculates the expected proof size for a given number of ciphertexts.
int secp256k1_mpt_verify_same_plaintext_multi(const secp256k1_context *ctx, const unsigned char *proof, size_t proof_len, size_t n, const secp256k1_pubkey *R, const secp256k1_pubkey *S, const secp256k1_pubkey *Pk, const unsigned char *tx_id)
Verifies a proof that N ciphertexts encrypt the same secret amount.
static void compute_challenge_multi(const secp256k1_context *ctx, unsigned char *e_out, size_t n, const secp256k1_pubkey *R, const secp256k1_pubkey *S, const secp256k1_pubkey *Pk, const secp256k1_pubkey *Tm, const secp256k1_pubkey *TrG, const secp256k1_pubkey *TrP, const unsigned char *tx_id)
static int pubkey_equal(const secp256k1_context *ctx, const secp256k1_pubkey *pk1, const secp256k1_pubkey *pk2)
static int generate_random_scalar(const secp256k1_context *ctx, unsigned char *scalar)
int secp256k1_mpt_prove_same_plaintext_multi(const secp256k1_context *ctx, unsigned char *proof_out, size_t *proof_len, uint64_t amount_m, size_t n, const secp256k1_pubkey *R, const secp256k1_pubkey *S, const secp256k1_pubkey *Pk, const unsigned char *r_array, const unsigned char *tx_id)
Generates a proof that N ciphertexts encrypt the same secret amount 'm'.
void secp256k1_mpt_scalar_reduce32(unsigned char out32[32], const unsigned char in32[32])
Definition mpt_scalar.c:106