mpt-crypto
Confidential Multi-Purpose Tokens Cryptographic Library
Loading...
Searching...
No Matches
elgamal.c
Go to the documentation of this file.
1
36#include "secp256k1_mpt.h"
37#include <openssl/rand.h>
38#include <openssl/sha.h>
39#include <string.h>
40#include <stdlib.h>
41
42/* --- Internal Helpers --- */
43
44static int pubkey_equal(const secp256k1_context* ctx, const secp256k1_pubkey* pk1, const secp256k1_pubkey* pk2) {
45 return secp256k1_ec_pubkey_cmp(ctx, pk1, pk2) == 0;
46}
47
48static int compute_amount_point(const secp256k1_context* ctx, secp256k1_pubkey* mG, uint64_t amount) {
49 unsigned char amount_scalar[32] = {0};
50 int ret;
51 for (int i = 0; i < 8; ++i) {
52 amount_scalar[31 - i] = (amount >> (i * 8)) & 0xFF;
53 }
54 ret = secp256k1_ec_pubkey_create(ctx, mG, amount_scalar);
55 OPENSSL_cleanse(amount_scalar, 32); // Wipe scalar after use
56 return ret;
57}
58
59/* --- Key Generation --- */
60
62 const secp256k1_context* ctx,
63 unsigned char* privkey,
64 secp256k1_pubkey* pubkey)
65{
66 do {
67 if (RAND_bytes(privkey, 32) != 1) return 0;
68 } while (!secp256k1_ec_seckey_verify(ctx, privkey));
69
70 if (!secp256k1_ec_pubkey_create(ctx, pubkey, privkey)) {
71 OPENSSL_cleanse(privkey, 32); // Cleanup on failure
72 return 0;
73 }
74 return 1;
75}
76
77/* --- Encryption --- */
78
80 const secp256k1_context* ctx,
81 secp256k1_pubkey* c1,
82 secp256k1_pubkey* c2,
83 const secp256k1_pubkey* pubkey_Q,
84 uint64_t amount,
85 const unsigned char* blinding_factor
86) {
87 secp256k1_pubkey S, mG;
88 const secp256k1_pubkey* pts[2];
89
90 /* 1. C1 = r * G */
91 if (!secp256k1_ec_pubkey_create(ctx, c1, blinding_factor)) return 0;
92
93 /* 2. S = r * Q (Shared Secret) */
94 S = *pubkey_Q;
95 if (!secp256k1_ec_pubkey_tweak_mul(ctx, &S, blinding_factor)) return 0;
96
97 /* 3. C2 = S + m*G */
98 if (amount == 0) {
99 *c2 = S; // m*G is infinity, so C2 = S
100 } else {
101 if (!compute_amount_point(ctx, &mG, amount)) return 0;
102 pts[0] = &mG; pts[1] = &S;
103 if (!secp256k1_ec_pubkey_combine(ctx, c2, pts, 2)) return 0;
104 }
105
106 return 1;
107}
108
109/* --- Decryption --- */
110
112 const secp256k1_context* ctx,
113 uint64_t* amount,
114 const secp256k1_pubkey* c1,
115 const secp256k1_pubkey* c2,
116 const unsigned char* privkey
117) {
118 secp256k1_pubkey S, M_target, current_M, G_point, next_M;
119 const secp256k1_pubkey* pts[2];
120 uint64_t i;
121 unsigned char one[32] = {0}; one[31] = 1;
122
123 /* 1. Recover Shared Secret: S = privkey * C1 */
124 S = *c1;
125 if (!secp256k1_ec_pubkey_tweak_mul(ctx, &S, privkey)) return 0;
126
127 /* 2. Check for Amount = 0 (C2 == S) */
128 /* This is much faster than doing point subtraction first */
129 if (pubkey_equal(ctx, c2, &S)) {
130 *amount = 0;
131 return 1;
132 }
133
134 /* 3. Prepare Target: M_target = C2 - S */
135 /* M_target = C2 + (-S) */
136 if (!secp256k1_ec_pubkey_negate(ctx, &S)) return 0;
137 pts[0] = c2; pts[1] = &S;
138 if (!secp256k1_ec_pubkey_combine(ctx, &M_target, pts, 2)) return 0;
139
140 /* 4. Brute Force Search (1 to 1,000,000) */
141 /* Optimization: Use point comparison, no serialization inside loop */
142
143 if (!secp256k1_ec_pubkey_create(ctx, &G_point, one)) return 0; // G
144 current_M = G_point; // Start at 1*G
145
146 for (i = 1; i <= 1000000; ++i) {
147 // Fast comparison
148 if (pubkey_equal(ctx, &current_M, &M_target)) {
149 *amount = i;
150 return 1;
151 }
152
153 // Increment: current_M = current_M + G
154 pts[0] = &current_M; pts[1] = &G_point;
155 if (!secp256k1_ec_pubkey_combine(ctx, &next_M, pts, 2)) return 0;
156 current_M = next_M;
157 }
158
159 return 0; // Amount not found in range
160}
161
162/* --- Homomorphic Operations --- */
163
165 const secp256k1_context* ctx,
166 secp256k1_pubkey* sum_c1,
167 secp256k1_pubkey* sum_c2,
168 const secp256k1_pubkey* a_c1,
169 const secp256k1_pubkey* a_c2,
170 const secp256k1_pubkey* b_c1,
171 const secp256k1_pubkey* b_c2
172) {
173 const secp256k1_pubkey* pts[2];
174
175 pts[0] = a_c1; pts[1] = b_c1;
176 if (!secp256k1_ec_pubkey_combine(ctx, sum_c1, pts, 2)) return 0;
177
178 pts[0] = a_c2; pts[1] = b_c2;
179 if (!secp256k1_ec_pubkey_combine(ctx, sum_c2, pts, 2)) return 0;
180
181 return 1;
182}
183
185 const secp256k1_context* ctx,
186 secp256k1_pubkey* diff_c1,
187 secp256k1_pubkey* diff_c2,
188 const secp256k1_pubkey* a_c1,
189 const secp256k1_pubkey* a_c2,
190 const secp256k1_pubkey* b_c1,
191 const secp256k1_pubkey* b_c2
192) {
193 secp256k1_pubkey neg_b_c1 = *b_c1;
194 secp256k1_pubkey neg_b_c2 = *b_c2;
195 const secp256k1_pubkey* pts[2];
196
197 if (!secp256k1_ec_pubkey_negate(ctx, &neg_b_c1)) return 0;
198 if (!secp256k1_ec_pubkey_negate(ctx, &neg_b_c2)) return 0;
199
200 pts[0] = a_c1; pts[1] = &neg_b_c1;
201 if (!secp256k1_ec_pubkey_combine(ctx, diff_c1, pts, 2)) return 0;
202
203 pts[0] = a_c2; pts[1] = &neg_b_c2;
204 if (!secp256k1_ec_pubkey_combine(ctx, diff_c2, pts, 2)) return 0;
205
206 return 1;
207}
208
209/* --- Canonical Encrypted Zero --- */
210
212 const secp256k1_context* ctx,
213 secp256k1_pubkey* enc_zero_c1,
214 secp256k1_pubkey* enc_zero_c2,
215 const secp256k1_pubkey* pubkey,
216 const unsigned char* account_id, // 20 bytes
217 const unsigned char* mpt_issuance_id // 24 bytes
218) {
219 unsigned char deterministic_scalar[32];
220 unsigned char hash_input[51]; // 7 ("EncZero") + 20 + 24
221 const char* domain = "EncZero";
222 int ret;
223 SHA256_CTX sha;
224
225 // Build static buffer part
226 memcpy(hash_input, domain, 7);
227 memcpy(hash_input + 7, account_id, 20);
228 memcpy(hash_input + 27, mpt_issuance_id, 24);
229
230 /* Rejection sampling loop to ensure scalar is valid */
231 do {
232 SHA256(hash_input, 51, deterministic_scalar);
233
234 // If invalid, re-hash the hash (standard chain method for determinism)
235 // Or simply fail if strict canonical behavior is required.
236 // Assuming rejection sampling is the intended design for safety:
237 if (secp256k1_ec_seckey_verify(ctx, deterministic_scalar)) break;
238
239 // Update input for next iteration to get new hash
240 // (Note: The original code just looped SHA256 on same input which is static,
241 // so it would loop forever if the first hash was invalid.
242 // Fixed here by re-hashing the output if needed, though highly unlikely to fail).
243 memcpy(hash_input, deterministic_scalar, 32);
244
245 } while (1);
246
248 ctx,
249 enc_zero_c1,
250 enc_zero_c2,
251 pubkey,
252 0,
253 deterministic_scalar
254 );
255
256 OPENSSL_cleanse(deterministic_scalar, 32); // Secure cleanup
257 return ret;
258}
259
260/* --- Direct Verification (Convert) --- */
261
263 const secp256k1_context* ctx,
264 const secp256k1_pubkey* c1,
265 const secp256k1_pubkey* c2,
266 const secp256k1_pubkey* pubkey_Q,
267 uint64_t amount,
268 const unsigned char* blinding_factor)
269{
270 secp256k1_pubkey expected_c1, expected_c2, mG, S;
271 const secp256k1_pubkey* pts[2];
272
273 /* 1. Verify C1 == r * G */
274 if (!secp256k1_ec_pubkey_create(ctx, &expected_c1, blinding_factor)) return 0;
275 if (!pubkey_equal(ctx, c1, &expected_c1)) return 0;
276
277 /* 2. Verify C2 == r*Q + m*G */
278
279 // S = r * Q
280 S = *pubkey_Q;
281 if (!secp256k1_ec_pubkey_tweak_mul(ctx, &S, blinding_factor)) return 0;
282
283 if (amount == 0) {
284 expected_c2 = S;
285 } else {
286 if (!compute_amount_point(ctx, &mG, amount)) return 0;
287 pts[0] = &mG; pts[1] = &S;
288 if (!secp256k1_ec_pubkey_combine(ctx, &expected_c2, pts, 2)) return 0;
289 }
290
291 if (!pubkey_equal(ctx, c2, &expected_c2)) return 0;
292
293 return 1;
294}
int secp256k1_elgamal_verify_encryption(const secp256k1_context *ctx, const secp256k1_pubkey *c1, const secp256k1_pubkey *c2, const secp256k1_pubkey *pubkey_Q, uint64_t amount, const unsigned char *blinding_factor)
Definition elgamal.c:262
int generate_canonical_encrypted_zero(const secp256k1_context *ctx, secp256k1_pubkey *enc_zero_c1, secp256k1_pubkey *enc_zero_c2, const secp256k1_pubkey *pubkey, const unsigned char *account_id, const unsigned char *mpt_issuance_id)
Generates the canonical encrypted zero for a given MPT token instance.
Definition elgamal.c:211
int secp256k1_elgamal_subtract(const secp256k1_context *ctx, secp256k1_pubkey *diff_c1, secp256k1_pubkey *diff_c2, const secp256k1_pubkey *a_c1, const secp256k1_pubkey *a_c2, const secp256k1_pubkey *b_c1, const secp256k1_pubkey *b_c2)
Homomorphically subtracts two ElGamal ciphertexts.
Definition elgamal.c:184
int secp256k1_elgamal_add(const secp256k1_context *ctx, secp256k1_pubkey *sum_c1, secp256k1_pubkey *sum_c2, const secp256k1_pubkey *a_c1, const secp256k1_pubkey *a_c2, const secp256k1_pubkey *b_c1, const secp256k1_pubkey *b_c2)
Homomorphically adds two ElGamal ciphertexts.
Definition elgamal.c:164
int secp256k1_elgamal_decrypt(const secp256k1_context *ctx, uint64_t *amount, const secp256k1_pubkey *c1, const secp256k1_pubkey *c2, const unsigned char *privkey)
Decrypts an ElGamal ciphertext to recover the amount.
Definition elgamal.c:111
int secp256k1_elgamal_encrypt(const secp256k1_context *ctx, secp256k1_pubkey *c1, secp256k1_pubkey *c2, const secp256k1_pubkey *pubkey_Q, uint64_t amount, const unsigned char *blinding_factor)
Encrypts a 64-bit amount using ElGamal.
Definition elgamal.c:79
int secp256k1_elgamal_generate_keypair(const secp256k1_context *ctx, unsigned char *privkey, secp256k1_pubkey *pubkey)
Generates a new secp256k1 key pair.
Definition elgamal.c:61
static int compute_amount_point(const secp256k1_context *ctx, secp256k1_pubkey *mG, uint64_t amount)
Definition elgamal.c:48
static int pubkey_equal(const secp256k1_context *ctx, const secp256k1_pubkey *pk1, const secp256k1_pubkey *pk2)
Definition elgamal.c:44