mpt-crypto
Confidential Multi-Purpose Tokens Cryptographic Library
Loading...
Searching...
No Matches
proof_same_plaintext.c
Go to the documentation of this file.
1
48#include "secp256k1_mpt.h"
49#include <openssl/sha.h>
50#include <openssl/rand.h>
51#include <string.h>
52#include <stdlib.h>
53
54/* --- Internal Helpers --- */
55
56static int pubkey_equal(const secp256k1_context* ctx, const secp256k1_pubkey* pk1, const secp256k1_pubkey* pk2) {
57 return secp256k1_ec_pubkey_cmp(ctx, pk1, pk2) == 0;
58}
59
60static int generate_random_scalar(const secp256k1_context* ctx, unsigned char* scalar) {
61 do {
62 if (RAND_bytes(scalar, 32) != 1) return 0;
63 } while (!secp256k1_ec_seckey_verify(ctx, scalar));
64 return 1;
65}
66
71 const secp256k1_context* ctx,
72 unsigned char* e_out,
73 const secp256k1_pubkey* R1, const secp256k1_pubkey* S1, const secp256k1_pubkey* P1,
74 const secp256k1_pubkey* R2, const secp256k1_pubkey* S2, const secp256k1_pubkey* P2,
75 const secp256k1_pubkey* T_m,
76 const secp256k1_pubkey* T_r1_G, const secp256k1_pubkey* T_r1_P1,
77 const secp256k1_pubkey* T_r2_G, const secp256k1_pubkey* T_r2_P2,
78 const unsigned char* tx_context_id)
79{
80 SHA256_CTX sha;
81 unsigned char buf[33];
82 unsigned char h[32];
83 size_t len;
84 const char* domain = "MPT_POK_SAME_PLAINTEXT_PROOF";
85
86 SHA256_Init(&sha);
87 SHA256_Update(&sha, domain, strlen(domain));
88
89 /* Helper macro to serialize and update */
90#define SER_AND_HASH(pk) do { \
91 len = 33; \
92 secp256k1_ec_pubkey_serialize(ctx, buf, &len, pk, SECP256K1_EC_COMPRESSED); \
93 SHA256_Update(&sha, buf, 33); \
94 } while(0)
95
96 // 6 Public Inputs
99
100 // 5 Commitments
101 SER_AND_HASH(T_m);
102 SER_AND_HASH(T_r1_G); SER_AND_HASH(T_r1_P1);
103 SER_AND_HASH(T_r2_G); SER_AND_HASH(T_r2_P2);
104
105#undef SER_AND_HASH
106
107 if (tx_context_id) {
108 SHA256_Update(&sha, tx_context_id, 32);
109 }
110
111 SHA256_Final(h, &sha);
113}
114
115/* --- Public API --- */
116
118 const secp256k1_context* ctx,
119 unsigned char* proof_out,
120 const secp256k1_pubkey* R1, const secp256k1_pubkey* S1, const secp256k1_pubkey* P1,
121 const secp256k1_pubkey* R2, const secp256k1_pubkey* S2, const secp256k1_pubkey* P2,
122 uint64_t amount_m,
123 const unsigned char* randomness_r1,
124 const unsigned char* randomness_r2,
125 const unsigned char* tx_context_id
126) {
127 unsigned char k_m[32], k_r1[32], k_r2[32];
128 unsigned char m_scalar[32] = {0};
129 unsigned char e[32];
130 unsigned char s_m[32], s_r1[32], s_r2[32];
131 unsigned char term[32];
132 secp256k1_pubkey T_m, T_r1_G, T_r1_P1, T_r2_G, T_r2_P2;
133 size_t len;
134 int ok = 0;
135
136 /* 1. Generate Randomness */
137 if (!generate_random_scalar(ctx, k_m)) goto cleanup;
138 if (!generate_random_scalar(ctx, k_r1)) goto cleanup;
139 if (!generate_random_scalar(ctx, k_r2)) goto cleanup;
140
141 /* 2. Commitments */
142 if (!secp256k1_ec_pubkey_create(ctx, &T_m, k_m)) goto cleanup;
143
144 if (!secp256k1_ec_pubkey_create(ctx, &T_r1_G, k_r1)) goto cleanup;
145 T_r1_P1 = *P1;
146 if (!secp256k1_ec_pubkey_tweak_mul(ctx, &T_r1_P1, k_r1)) goto cleanup;
147
148 if (!secp256k1_ec_pubkey_create(ctx, &T_r2_G, k_r2)) goto cleanup;
149 T_r2_P2 = *P2;
150 if (!secp256k1_ec_pubkey_tweak_mul(ctx, &T_r2_P2, k_r2)) goto cleanup;
151
152 /* 3. Challenge */
153 build_same_plaintext_hash_input(ctx, e, R1, S1, P1, R2, S2, P2,
154 &T_m, &T_r1_G, &T_r1_P1, &T_r2_G, &T_r2_P2,
155 tx_context_id);
156
157 /* 4. Responses */
158 for (int i = 0; i < 8; ++i) m_scalar[31 - i] = (amount_m >> (i * 8)) & 0xFF;
159
160 // s_m = k_m + e * m
161 memcpy(s_m, k_m, 32);
162 memcpy(term, m_scalar, 32);
163 if (!secp256k1_ec_seckey_tweak_mul(ctx, term, e)) goto cleanup;
164 if (!secp256k1_ec_seckey_tweak_add(ctx, s_m, term)) goto cleanup;
165
166 // s_r1 = k_r1 + e * r1
167 memcpy(s_r1, k_r1, 32);
168 memcpy(term, randomness_r1, 32);
169 if (!secp256k1_ec_seckey_tweak_mul(ctx, term, e)) goto cleanup;
170 if (!secp256k1_ec_seckey_tweak_add(ctx, s_r1, term)) goto cleanup;
171
172 // s_r2 = k_r2 + e * r2
173 memcpy(s_r2, k_r2, 32);
174 memcpy(term, randomness_r2, 32);
175 if (!secp256k1_ec_seckey_tweak_mul(ctx, term, e)) goto cleanup;
176 if (!secp256k1_ec_seckey_tweak_add(ctx, s_r2, term)) goto cleanup;
177
178 /* 5. Serialize */
179 unsigned char* ptr = proof_out;
180
181 len = 33; if(!secp256k1_ec_pubkey_serialize(ctx, ptr, &len, &T_m, SECP256K1_EC_COMPRESSED)) goto cleanup; ptr += 33;
182 len = 33; if(!secp256k1_ec_pubkey_serialize(ctx, ptr, &len, &T_r1_G, SECP256K1_EC_COMPRESSED)) goto cleanup; ptr += 33;
183 len = 33; if(!secp256k1_ec_pubkey_serialize(ctx, ptr, &len, &T_r1_P1, SECP256K1_EC_COMPRESSED)) goto cleanup; ptr += 33;
184 len = 33; if(!secp256k1_ec_pubkey_serialize(ctx, ptr, &len, &T_r2_G, SECP256K1_EC_COMPRESSED)) goto cleanup; ptr += 33;
185 len = 33; if(!secp256k1_ec_pubkey_serialize(ctx, ptr, &len, &T_r2_P2, SECP256K1_EC_COMPRESSED)) goto cleanup; ptr += 33;
186
187 memcpy(ptr, s_m, 32); ptr += 32;
188 memcpy(ptr, s_r1, 32); ptr += 32;
189 memcpy(ptr, s_r2, 32); ptr += 32;
190
191 ok = 1;
192
193 cleanup:
194 OPENSSL_cleanse(k_m, 32);
195 OPENSSL_cleanse(k_r1, 32);
196 OPENSSL_cleanse(k_r2, 32);
197 OPENSSL_cleanse(m_scalar, 32);
198 return ok;
199}
200
202 const secp256k1_context* ctx,
203 const unsigned char* proof, // Caller MUST provide at least 261 bytes
204 const secp256k1_pubkey* R1, const secp256k1_pubkey* S1, const secp256k1_pubkey* P1,
205 const secp256k1_pubkey* R2, const secp256k1_pubkey* S2, const secp256k1_pubkey* P2,
206 const unsigned char* tx_context_id
207) {
208 /* Fixed Size: 5 points (33) + 3 scalars (32) = 165 + 96 = 261 bytes */
209
210 secp256k1_pubkey T_m, T_r1_G, T_r1_P1, T_r2_G, T_r2_P2;
211 unsigned char s_m[32], s_r1[32], s_r2[32];
212 unsigned char e[32];
213 const unsigned char* ptr = proof;
214 int ok = 0;
215
216 secp256k1_pubkey LHS, RHS, term, SmG;
217 const secp256k1_pubkey* pts[3];
218
219 /* 1. Deserialize (Strict 261 bytes) */
220
221 // Parse 5 Points (5 * 33 = 165 bytes)
222 if (!secp256k1_ec_pubkey_parse(ctx, &T_m, ptr, 33)) goto cleanup; ptr += 33;
223 if (!secp256k1_ec_pubkey_parse(ctx, &T_r1_G, ptr, 33)) goto cleanup; ptr += 33;
224 if (!secp256k1_ec_pubkey_parse(ctx, &T_r1_P1,ptr, 33)) goto cleanup; ptr += 33;
225 if (!secp256k1_ec_pubkey_parse(ctx, &T_r2_G, ptr, 33)) goto cleanup; ptr += 33;
226 if (!secp256k1_ec_pubkey_parse(ctx, &T_r2_P2,ptr, 33)) goto cleanup; ptr += 33;
227
228 // Parse 3 Scalars (3 * 32 = 96 bytes)
229 memcpy(s_m, ptr, 32); ptr += 32;
230 memcpy(s_r1, ptr, 32); ptr += 32;
231 memcpy(s_r2, ptr, 32); ptr += 32;
232
233 // Sanity Check Scalars
234 if (!secp256k1_ec_seckey_verify(ctx, s_m)) goto cleanup;
235 if (!secp256k1_ec_seckey_verify(ctx, s_r1)) goto cleanup;
236 if (!secp256k1_ec_seckey_verify(ctx, s_r2)) goto cleanup;
237
238 /* 2. Challenge */
239 build_same_plaintext_hash_input(ctx, e, R1, S1, P1, R2, S2, P2,
240 &T_m, &T_r1_G, &T_r1_P1, &T_r2_G, &T_r2_P2,
241 tx_context_id);
242
243 /* 3. Verification Equations */
244
245 // Precompute SmG (used in Eq 2 & 4)
246 if (!secp256k1_ec_pubkey_create(ctx, &SmG, s_m)) goto cleanup;
247
248 /* Eq 1: s_r1 * G == T_r1_G + e * R1 */
249 if (!secp256k1_ec_pubkey_create(ctx, &LHS, s_r1)) goto cleanup;
250 term = *R1;
251 if (!secp256k1_ec_pubkey_tweak_mul(ctx, &term, e)) goto cleanup;
252 pts[0] = &T_r1_G; pts[1] = &term;
253 if (!secp256k1_ec_pubkey_combine(ctx, &RHS, pts, 2)) goto cleanup;
254 if (!pubkey_equal(ctx, &LHS, &RHS)) goto cleanup;
255
256 /* Eq 2: s_m * G + s_r1 * P1 == T_m + T_r1_P1 + e * S1 */
257 term = *P1;
258 if (!secp256k1_ec_pubkey_tweak_mul(ctx, &term, s_r1)) goto cleanup;
259 pts[0] = &SmG; pts[1] = &term;
260 if (!secp256k1_ec_pubkey_combine(ctx, &LHS, pts, 2)) goto cleanup;
261
262 term = *S1;
263 if (!secp256k1_ec_pubkey_tweak_mul(ctx, &term, e)) goto cleanup;
264 pts[0] = &T_m; pts[1] = &T_r1_P1; pts[2] = &term;
265 if (!secp256k1_ec_pubkey_combine(ctx, &RHS, pts, 3)) goto cleanup;
266 if (!pubkey_equal(ctx, &LHS, &RHS)) goto cleanup;
267
268 /* Eq 3: s_r2 * G == T_r2_G + e * R2 */
269 if (!secp256k1_ec_pubkey_create(ctx, &LHS, s_r2)) goto cleanup;
270 term = *R2;
271 if (!secp256k1_ec_pubkey_tweak_mul(ctx, &term, e)) goto cleanup;
272 pts[0] = &T_r2_G; pts[1] = &term;
273 if (!secp256k1_ec_pubkey_combine(ctx, &RHS, pts, 2)) goto cleanup;
274 if (!pubkey_equal(ctx, &LHS, &RHS)) goto cleanup;
275
276 /* Eq 4: s_m * G + s_r2 * P2 == T_m + T_r2_P2 + e * S2 */
277 term = *P2;
278 if (!secp256k1_ec_pubkey_tweak_mul(ctx, &term, s_r2)) goto cleanup;
279 pts[0] = &SmG; pts[1] = &term;
280 if (!secp256k1_ec_pubkey_combine(ctx, &LHS, pts, 2)) goto cleanup;
281
282 term = *S2;
283 if (!secp256k1_ec_pubkey_tweak_mul(ctx, &term, e)) goto cleanup;
284 pts[0] = &T_m; pts[1] = &T_r2_P2; pts[2] = &term;
285 if (!secp256k1_ec_pubkey_combine(ctx, &RHS, pts, 3)) goto cleanup;
286 if (!pubkey_equal(ctx, &LHS, &RHS)) goto cleanup;
287
288 ok = 1;
289
290 cleanup:
291 return ok;
292}
static void build_same_plaintext_hash_input(const secp256k1_context *ctx, unsigned char *e_out, const secp256k1_pubkey *R1, const secp256k1_pubkey *S1, const secp256k1_pubkey *P1, const secp256k1_pubkey *R2, const secp256k1_pubkey *S2, const secp256k1_pubkey *P2, const secp256k1_pubkey *T_m, const secp256k1_pubkey *T_r1_G, const secp256k1_pubkey *T_r1_P1, const secp256k1_pubkey *T_r2_G, const secp256k1_pubkey *T_r2_P2, const unsigned char *tx_context_id)
int secp256k1_mpt_prove_same_plaintext(const secp256k1_context *ctx, unsigned char *proof_out, const secp256k1_pubkey *R1, const secp256k1_pubkey *S1, const secp256k1_pubkey *P1, const secp256k1_pubkey *R2, const secp256k1_pubkey *S2, const secp256k1_pubkey *P2, uint64_t amount_m, const unsigned char *randomness_r1, const unsigned char *randomness_r2, const unsigned char *tx_context_id)
Generates a proof that two ciphertexts (under different keys) encrypt the same secret amount 'm'.
int secp256k1_mpt_verify_same_plaintext(const secp256k1_context *ctx, const unsigned char *proof, const secp256k1_pubkey *R1, const secp256k1_pubkey *S1, const secp256k1_pubkey *P1, const secp256k1_pubkey *R2, const secp256k1_pubkey *S2, const secp256k1_pubkey *P2, const unsigned char *tx_context_id)
Verifies a proof that two ciphertexts encrypt the same secret amount.
static int pubkey_equal(const secp256k1_context *ctx, const secp256k1_pubkey *pk1, const secp256k1_pubkey *pk2)
#define SER_AND_HASH(pk)
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