pq_crypto 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,18 +6,16 @@
6
6
  #include <unistd.h>
7
7
  #include <limits.h>
8
8
 
9
- #if defined(__linux__)
10
- #include <sys/random.h>
11
- #endif
12
-
13
9
  #ifndef HAVE_OPENSSL_EVP_H
14
10
  #error \
15
11
  "OpenSSL with EVP support is required for secure cryptographic operations. Install OpenSSL development packages."
16
12
  #endif
17
13
 
14
+ #include <openssl/crypto.h>
18
15
  #include <openssl/evp.h>
19
16
  #include <openssl/rand.h>
20
- #include <openssl/kdf.h>
17
+ #include <openssl/bio.h>
18
+ #include <openssl/buffer.h>
21
19
 
22
20
  #if OPENSSL_VERSION_NUMBER < 0x30000000L
23
21
  #error "OpenSSL 3.0 or later is required for pq_crypto"
@@ -29,11 +27,11 @@
29
27
 
30
28
  #include "mlkem_api.h"
31
29
  #include "mldsa_api.h"
32
- #include "fips202.h"
33
- #include "packing.h"
34
- #include "params.h"
35
30
 
36
31
  void pq_secure_wipe(void *ptr, size_t len) {
32
+ if (ptr == NULL) {
33
+ return;
34
+ }
37
35
  volatile uint8_t *p = ptr;
38
36
  while (len--) {
39
37
  *p++ = 0;
@@ -64,33 +62,6 @@ static int pq_is_pem_whitespace(char c) {
64
62
  return c == '\n' || c == '\r' || c == ' ' || c == '\t';
65
63
  }
66
64
 
67
- static int pq_randombytes(uint8_t *buf, size_t len) {
68
- #ifdef HAVE_OPENSSL_RAND_H
69
- if (len > INT_MAX) {
70
- return PQ_ERROR_BUFFER;
71
- }
72
- if (RAND_bytes(buf, (int)len) == 1) {
73
- return 0;
74
- }
75
-
76
- #endif
77
-
78
- #if defined(__linux__)
79
- ssize_t ret = getrandom(buf, len, 0);
80
- return (ret == (ssize_t)len) ? 0 : PQ_ERROR_RANDOM;
81
- #elif defined(__APPLE__)
82
- arc4random_buf(buf, len);
83
- return 0;
84
- #else
85
- FILE *f = fopen("/dev/urandom", "rb");
86
- if (!f)
87
- return PQ_ERROR_RANDOM;
88
- size_t read = fread(buf, 1, len, f);
89
- fclose(f);
90
- return (read == len) ? 0 : PQ_ERROR_RANDOM;
91
- #endif
92
- }
93
-
94
65
  static int x25519_keypair(uint8_t *pk, uint8_t *sk) {
95
66
  EVP_PKEY_CTX *ctx = NULL;
96
67
  EVP_PKEY *pkey = NULL;
@@ -171,201 +142,67 @@ cleanup:
171
142
  return ret;
172
143
  }
173
144
 
174
- static int x25519_public_from_secret(uint8_t *pk, const uint8_t *sk) {
175
- EVP_PKEY *pkey = NULL;
176
- size_t pklen = X25519_PUBLICKEYBYTES;
177
- int ret = PQ_ERROR_KEYPAIR;
178
-
179
- pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_X25519, NULL, sk, X25519_SECRETKEYBYTES);
180
- if (!pkey) {
181
- return ret;
182
- }
183
-
184
- if (EVP_PKEY_get_raw_public_key(pkey, pk, &pklen) <= 0 || pklen != X25519_PUBLICKEYBYTES) {
185
- EVP_PKEY_free(pkey);
186
- return ret;
187
- }
188
-
189
- EVP_PKEY_free(pkey);
190
- return PQ_SUCCESS;
191
- }
192
-
193
- static int size_t_to_int_checked(size_t value, int *out) {
194
- if (value > INT_MAX) {
195
- return PQ_ERROR_BUFFER;
196
- }
197
-
198
- *out = (int)value;
199
- return PQ_SUCCESS;
200
- }
201
-
202
- static int secure_hkdf(uint8_t *output, size_t output_len, const uint8_t *ikm, size_t ikm_len,
203
- const uint8_t *salt, size_t salt_len, const uint8_t *info, size_t info_len) {
204
- int ikm_len_i = 0;
205
- int salt_len_i = 0;
206
- int info_len_i = 0;
207
-
208
- if (size_t_to_int_checked(ikm_len, &ikm_len_i) != PQ_SUCCESS)
209
- return PQ_ERROR_BUFFER;
210
- if (size_t_to_int_checked(salt_len, &salt_len_i) != PQ_SUCCESS)
211
- return PQ_ERROR_BUFFER;
212
- if (size_t_to_int_checked(info_len, &info_len_i) != PQ_SUCCESS)
213
- return PQ_ERROR_BUFFER;
214
-
215
- EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);
216
- if (!pctx)
217
- return PQ_ERROR_OPENSSL;
218
-
219
- if (EVP_PKEY_derive_init(pctx) <= 0) {
220
- EVP_PKEY_CTX_free(pctx);
221
- return PQ_ERROR_OPENSSL;
222
- }
223
-
224
- if (EVP_PKEY_CTX_set_hkdf_md(pctx, EVP_sha256()) <= 0) {
225
- EVP_PKEY_CTX_free(pctx);
226
- return PQ_ERROR_OPENSSL;
227
- }
228
-
229
- if (EVP_PKEY_CTX_set1_hkdf_key(pctx, ikm, ikm_len_i) <= 0) {
230
- EVP_PKEY_CTX_free(pctx);
231
- return PQ_ERROR_OPENSSL;
232
- }
145
+ static const uint8_t XWING_LABEL[6] = {0x5c, 0x2e, 0x2f, 0x2f, 0x5e, 0x5c};
233
146
 
234
- if (salt && salt_len > 0) {
235
- if (EVP_PKEY_CTX_set1_hkdf_salt(pctx, salt, salt_len_i) <= 0) {
236
- EVP_PKEY_CTX_free(pctx);
237
- return PQ_ERROR_OPENSSL;
238
- }
239
- }
240
-
241
- if (info && info_len > 0) {
242
- if (EVP_PKEY_CTX_add1_hkdf_info(pctx, info, info_len_i) <= 0) {
243
- EVP_PKEY_CTX_free(pctx);
244
- return PQ_ERROR_OPENSSL;
245
- }
246
- }
247
-
248
- size_t outlen = output_len;
249
- if (EVP_PKEY_derive(pctx, output, &outlen) <= 0) {
250
- EVP_PKEY_CTX_free(pctx);
251
- return PQ_ERROR_OPENSSL;
252
- }
253
-
254
- EVP_PKEY_CTX_free(pctx);
255
- return (outlen == output_len) ? 0 : PQ_ERROR_KDF;
256
- }
257
-
258
- static int secure_sha256(uint8_t *output, const uint8_t *input, size_t input_len) {
147
+ static int xwing_combiner(uint8_t shared_secret[HYBRID_SHAREDSECRETBYTES],
148
+ const uint8_t ss_M[MLKEM_SHAREDSECRETBYTES],
149
+ const uint8_t ss_X[X25519_SHAREDSECRETBYTES],
150
+ const uint8_t ct_X[X25519_PUBLICKEYBYTES],
151
+ const uint8_t pk_X[X25519_PUBLICKEYBYTES]) {
259
152
  EVP_MD_CTX *ctx = EVP_MD_CTX_new();
260
153
  unsigned int out_len = 0;
154
+ int ret = PQ_ERROR_OPENSSL;
261
155
 
262
156
  if (!ctx) {
263
157
  return PQ_ERROR_OPENSSL;
264
158
  }
265
159
 
266
- if (EVP_DigestInit_ex(ctx, EVP_sha256(), NULL) != 1 ||
267
- EVP_DigestUpdate(ctx, input, input_len) != 1 ||
268
- EVP_DigestFinal_ex(ctx, output, &out_len) != 1) {
269
- EVP_MD_CTX_free(ctx);
270
- return PQ_ERROR_OPENSSL;
271
- }
272
-
273
- EVP_MD_CTX_free(ctx);
274
- return out_len == 32 ? PQ_SUCCESS : PQ_ERROR_OPENSSL;
275
- }
276
-
277
- static int hybrid_combiner(uint8_t *shared_secret, const uint8_t *mlkem_ss,
278
- const uint8_t *x25519_ss, const uint8_t *recipient_x25519_pk,
279
- const hybrid_ciphertext_t *ct) {
280
- static const uint8_t label[] = "pqcrypto/v1/hybrid-kem";
281
- uint8_t ikm[MLKEM_SHAREDSECRETBYTES + X25519_SHAREDSECRETBYTES];
282
- uint8_t salt[32];
283
- int ret = PQ_SUCCESS;
284
- size_t transcript_len = sizeof(label) - 1 + X25519_PUBLICKEYBYTES + HYBRID_CIPHERTEXTBYTES;
285
- uint8_t *transcript = malloc(transcript_len);
286
-
287
- if (!transcript) {
288
- return PQ_ERROR_NOMEM;
289
- }
290
-
291
- memcpy(ikm, mlkem_ss, MLKEM_SHAREDSECRETBYTES);
292
- memcpy(ikm + MLKEM_SHAREDSECRETBYTES, x25519_ss, X25519_SHAREDSECRETBYTES);
293
-
294
- memcpy(transcript, label, sizeof(label) - 1);
295
- memcpy(transcript + sizeof(label) - 1, recipient_x25519_pk, X25519_PUBLICKEYBYTES);
296
- memcpy(transcript + sizeof(label) - 1 + X25519_PUBLICKEYBYTES, ct, HYBRID_CIPHERTEXTBYTES);
160
+ if (EVP_DigestInit_ex(ctx, EVP_sha3_256(), NULL) != 1)
161
+ goto cleanup;
162
+ if (EVP_DigestUpdate(ctx, XWING_LABEL, sizeof(XWING_LABEL)) != 1)
163
+ goto cleanup;
164
+ if (EVP_DigestUpdate(ctx, ss_M, MLKEM_SHAREDSECRETBYTES) != 1)
165
+ goto cleanup;
166
+ if (EVP_DigestUpdate(ctx, ss_X, X25519_SHAREDSECRETBYTES) != 1)
167
+ goto cleanup;
168
+ if (EVP_DigestUpdate(ctx, ct_X, X25519_PUBLICKEYBYTES) != 1)
169
+ goto cleanup;
170
+ if (EVP_DigestUpdate(ctx, pk_X, X25519_PUBLICKEYBYTES) != 1)
171
+ goto cleanup;
172
+ if (EVP_DigestFinal_ex(ctx, shared_secret, &out_len) != 1)
173
+ goto cleanup;
174
+ if (out_len != HYBRID_SHAREDSECRETBYTES)
175
+ goto cleanup;
297
176
 
298
- ret = secure_sha256(salt, transcript, transcript_len);
299
- if (ret == PQ_SUCCESS) {
300
- ret = secure_hkdf(shared_secret, HYBRID_SHAREDSECRETBYTES, ikm, sizeof(ikm), salt,
301
- sizeof(salt), transcript, transcript_len);
302
- }
177
+ ret = PQ_SUCCESS;
303
178
 
304
- pq_secure_wipe(ikm, sizeof(ikm));
305
- pq_secure_wipe(salt, sizeof(salt));
306
- pq_secure_wipe(transcript, transcript_len);
307
- free(transcript);
179
+ cleanup:
180
+ EVP_MD_CTX_free(ctx);
308
181
  return ret;
309
182
  }
310
183
 
311
- static int mlkem_keypair(uint8_t *pk, uint8_t *sk) {
312
- return PQCLEAN_MLKEM768_CLEAN_crypto_kem_keypair(pk, sk);
184
+ int pq_mlkem_keypair(uint8_t *pk, uint8_t *sk) {
185
+ return PQCLEAN_MLKEM768_CLEAN_crypto_kem_keypair(pk, sk) == 0 ? PQ_SUCCESS : PQ_ERROR_KEYPAIR;
313
186
  }
314
187
 
315
- static int mlkem_encapsulate(uint8_t *ct, uint8_t *ss, const uint8_t *pk) {
316
- return PQCLEAN_MLKEM768_CLEAN_crypto_kem_enc(ct, ss, pk);
188
+ int pq_mlkem_encapsulate(uint8_t *ct, uint8_t *ss, const uint8_t *pk) {
189
+ return PQCLEAN_MLKEM768_CLEAN_crypto_kem_enc(ct, ss, pk) == 0 ? PQ_SUCCESS
190
+ : PQ_ERROR_ENCAPSULATE;
317
191
  }
318
192
 
319
- static int mlkem_decapsulate(uint8_t *ss, const uint8_t *ct, const uint8_t *sk) {
320
- return PQCLEAN_MLKEM768_CLEAN_crypto_kem_dec(ss, ct, sk);
321
- }
322
-
323
- static int mldsa_keypair(uint8_t *pk, uint8_t *sk) {
324
- return PQCLEAN_MLDSA65_CLEAN_crypto_sign_keypair(pk, sk);
325
- }
326
-
327
- static int mldsa_sign(uint8_t *sig, size_t *siglen, const uint8_t *msg, size_t msglen,
328
- const uint8_t *sk) {
329
- return PQCLEAN_MLDSA65_CLEAN_crypto_sign_signature(sig, siglen, msg, msglen, sk);
330
- }
331
-
332
- static int mldsa_verify(const uint8_t *sig, size_t siglen, const uint8_t *msg, size_t msglen,
333
- const uint8_t *pk) {
334
- return PQCLEAN_MLDSA65_CLEAN_crypto_sign_verify(sig, siglen, msg, msglen, pk);
193
+ int pq_mlkem_decapsulate(uint8_t *ss, const uint8_t *ct, const uint8_t *sk) {
194
+ return PQCLEAN_MLKEM768_CLEAN_crypto_kem_dec(ss, ct, sk) == 0 ? PQ_SUCCESS
195
+ : PQ_ERROR_DECAPSULATE;
335
196
  }
336
197
 
337
198
  int pq_testing_mlkem_keypair_from_seed(uint8_t *public_key, uint8_t *secret_key,
338
199
  const uint8_t *seed, size_t seed_len) {
339
- uint8_t expanded_seed[64];
340
- int ret;
341
-
342
- if (!public_key || !secret_key || !seed) {
200
+ if (!public_key || !secret_key || !seed || seed_len != 64) {
343
201
  return PQ_ERROR_BUFFER;
344
202
  }
345
-
346
- if (seed_len == 64) {
347
- return PQCLEAN_MLKEM768_CLEAN_crypto_kem_keypair_derand(public_key, secret_key, seed) == 0
348
- ? PQ_SUCCESS
349
- : PQ_ERROR_KEYPAIR;
350
- }
351
-
352
- if (seed_len != 32) {
353
- return PQ_ERROR_BUFFER;
354
- }
355
-
356
- ret = secure_hkdf(expanded_seed, sizeof(expanded_seed), seed, seed_len, NULL, 0,
357
- (const uint8_t *)"pqcrypto-test-mlkem-keypair", 26);
358
- if (ret != PQ_SUCCESS) {
359
- pq_secure_wipe(expanded_seed, sizeof(expanded_seed));
360
- return ret;
361
- }
362
-
363
- ret =
364
- PQCLEAN_MLKEM768_CLEAN_crypto_kem_keypair_derand(public_key, secret_key, expanded_seed) == 0
365
- ? PQ_SUCCESS
366
- : PQ_ERROR_KEYPAIR;
367
- pq_secure_wipe(expanded_seed, sizeof(expanded_seed));
368
- return ret;
203
+ return PQCLEAN_MLKEM768_CLEAN_crypto_kem_keypair_derand(public_key, secret_key, seed) == 0
204
+ ? PQ_SUCCESS
205
+ : PQ_ERROR_KEYPAIR;
369
206
  }
370
207
 
371
208
  int pq_testing_mlkem_encapsulate_from_seed(uint8_t *ciphertext, uint8_t *shared_secret,
@@ -374,325 +211,214 @@ int pq_testing_mlkem_encapsulate_from_seed(uint8_t *ciphertext, uint8_t *shared_
374
211
  if (!ciphertext || !shared_secret || !public_key || !seed || seed_len != 32) {
375
212
  return PQ_ERROR_BUFFER;
376
213
  }
377
-
378
214
  return PQCLEAN_MLKEM768_CLEAN_crypto_kem_enc_derand(ciphertext, shared_secret, public_key,
379
215
  seed) == 0
380
216
  ? PQ_SUCCESS
381
217
  : PQ_ERROR_ENCAPSULATE;
382
218
  }
383
219
 
384
- static int pq_testing_mldsa_keypair_from_seed_impl(uint8_t *public_key, uint8_t *secret_key,
385
- const uint8_t seed[SEEDBYTES]) {
386
- uint8_t seedbuf[2 * SEEDBYTES + CRHBYTES];
387
- uint8_t tr[TRBYTES];
388
- const uint8_t *rho, *rhoprime, *key;
389
- polyvecl mat[K];
390
- polyvecl s1, s1hat;
391
- polyveck s2, t1, t0;
392
-
393
- memcpy(seedbuf, seed, SEEDBYTES);
394
- seedbuf[SEEDBYTES + 0] = K;
395
- seedbuf[SEEDBYTES + 1] = L;
396
- shake256(seedbuf, 2 * SEEDBYTES + CRHBYTES, seedbuf, SEEDBYTES + 2);
397
- rho = seedbuf;
398
- rhoprime = rho + SEEDBYTES;
399
- key = rhoprime + CRHBYTES;
400
-
401
- PQCLEAN_MLDSA65_CLEAN_polyvec_matrix_expand(mat, rho);
402
- PQCLEAN_MLDSA65_CLEAN_polyvecl_uniform_eta(&s1, rhoprime, 0);
403
- PQCLEAN_MLDSA65_CLEAN_polyveck_uniform_eta(&s2, rhoprime, L);
404
-
405
- s1hat = s1;
406
- PQCLEAN_MLDSA65_CLEAN_polyvecl_ntt(&s1hat);
407
- PQCLEAN_MLDSA65_CLEAN_polyvec_matrix_pointwise_montgomery(&t1, mat, &s1hat);
408
- PQCLEAN_MLDSA65_CLEAN_polyveck_reduce(&t1);
409
- PQCLEAN_MLDSA65_CLEAN_polyveck_invntt_tomont(&t1);
410
-
411
- PQCLEAN_MLDSA65_CLEAN_polyveck_add(&t1, &t1, &s2);
412
- PQCLEAN_MLDSA65_CLEAN_polyveck_caddq(&t1);
413
- PQCLEAN_MLDSA65_CLEAN_polyveck_power2round(&t1, &t0, &t1);
414
- PQCLEAN_MLDSA65_CLEAN_pack_pk(public_key, rho, &t1);
415
-
416
- shake256(tr, TRBYTES, public_key, PQCLEAN_MLDSA65_CLEAN_CRYPTO_PUBLICKEYBYTES);
417
- PQCLEAN_MLDSA65_CLEAN_pack_sk(secret_key, rho, tr, key, &t0, &s1, &s2);
418
-
419
- pq_secure_wipe(seedbuf, sizeof(seedbuf));
420
- pq_secure_wipe(tr, sizeof(tr));
421
- pq_secure_wipe(&s1, sizeof(s1));
422
- pq_secure_wipe(&s1hat, sizeof(s1hat));
423
- pq_secure_wipe(&s2, sizeof(s2));
424
- pq_secure_wipe(&t1, sizeof(t1));
425
- pq_secure_wipe(&t0, sizeof(t0));
426
- pq_secure_wipe(mat, sizeof(mat));
427
- return PQ_SUCCESS;
220
+ int pq_sign_keypair(uint8_t *public_key, uint8_t *secret_key) {
221
+ return PQCLEAN_MLDSA65_CLEAN_crypto_sign_keypair(public_key, secret_key) == 0
222
+ ? PQ_SUCCESS
223
+ : PQ_ERROR_KEYPAIR;
428
224
  }
429
225
 
430
- static int pq_testing_mldsa_sign_from_seed_impl(uint8_t *signature, size_t *signature_len,
431
- const uint8_t *message, size_t message_len,
432
- const uint8_t *secret_key,
433
- const uint8_t seed[RNDBYTES]) {
434
- unsigned int n;
435
- uint8_t seedbuf[2 * SEEDBYTES + TRBYTES + RNDBYTES + 2 * CRHBYTES];
436
- uint8_t *rho, *tr, *key, *mu, *rhoprime, *rnd;
437
- uint16_t nonce = 0;
438
- polyvecl mat[K], s1, y, z;
439
- polyveck t0, s2, w1, w0, h;
440
- poly cp;
441
- shake256incctx state;
442
-
443
- rho = seedbuf;
444
- tr = rho + SEEDBYTES;
445
- key = tr + TRBYTES;
446
- rnd = key + SEEDBYTES;
447
- mu = rnd + RNDBYTES;
448
- rhoprime = mu + CRHBYTES;
449
- PQCLEAN_MLDSA65_CLEAN_unpack_sk(rho, tr, key, &t0, &s1, &s2, secret_key);
450
-
451
- mu[0] = 0;
452
- mu[1] = 0;
453
- shake256_inc_init(&state);
454
- shake256_inc_absorb(&state, tr, TRBYTES);
455
- shake256_inc_absorb(&state, mu, 2);
456
- shake256_inc_absorb(&state, message, message_len);
457
- shake256_inc_finalize(&state);
458
- shake256_inc_squeeze(mu, CRHBYTES, &state);
459
- shake256_inc_ctx_release(&state);
460
-
461
- memcpy(rnd, seed, RNDBYTES);
462
- shake256(rhoprime, CRHBYTES, key, SEEDBYTES + RNDBYTES + CRHBYTES);
463
-
464
- PQCLEAN_MLDSA65_CLEAN_polyvec_matrix_expand(mat, rho);
465
- PQCLEAN_MLDSA65_CLEAN_polyvecl_ntt(&s1);
466
- PQCLEAN_MLDSA65_CLEAN_polyveck_ntt(&s2);
467
- PQCLEAN_MLDSA65_CLEAN_polyveck_ntt(&t0);
468
-
469
- rej:
470
- PQCLEAN_MLDSA65_CLEAN_polyvecl_uniform_gamma1(&y, rhoprime, nonce++);
471
-
472
- z = y;
473
- PQCLEAN_MLDSA65_CLEAN_polyvecl_ntt(&z);
474
- PQCLEAN_MLDSA65_CLEAN_polyvec_matrix_pointwise_montgomery(&w1, mat, &z);
475
- PQCLEAN_MLDSA65_CLEAN_polyveck_reduce(&w1);
476
- PQCLEAN_MLDSA65_CLEAN_polyveck_invntt_tomont(&w1);
477
-
478
- PQCLEAN_MLDSA65_CLEAN_polyveck_caddq(&w1);
479
- PQCLEAN_MLDSA65_CLEAN_polyveck_decompose(&w1, &w0, &w1);
480
- PQCLEAN_MLDSA65_CLEAN_polyveck_pack_w1(signature, &w1);
481
-
482
- shake256_inc_init(&state);
483
- shake256_inc_absorb(&state, mu, CRHBYTES);
484
- shake256_inc_absorb(&state, signature, K * POLYW1_PACKEDBYTES);
485
- shake256_inc_finalize(&state);
486
- shake256_inc_squeeze(signature, CTILDEBYTES, &state);
487
- shake256_inc_ctx_release(&state);
488
- PQCLEAN_MLDSA65_CLEAN_poly_challenge(&cp, signature);
489
- PQCLEAN_MLDSA65_CLEAN_poly_ntt(&cp);
490
-
491
- PQCLEAN_MLDSA65_CLEAN_polyvecl_pointwise_poly_montgomery(&z, &cp, &s1);
492
- PQCLEAN_MLDSA65_CLEAN_polyvecl_invntt_tomont(&z);
493
- PQCLEAN_MLDSA65_CLEAN_polyvecl_add(&z, &z, &y);
494
- PQCLEAN_MLDSA65_CLEAN_polyvecl_reduce(&z);
495
- if (PQCLEAN_MLDSA65_CLEAN_polyvecl_chknorm(&z, GAMMA1 - BETA)) {
496
- goto rej;
497
- }
498
-
499
- PQCLEAN_MLDSA65_CLEAN_polyveck_pointwise_poly_montgomery(&h, &cp, &s2);
500
- PQCLEAN_MLDSA65_CLEAN_polyveck_invntt_tomont(&h);
501
- PQCLEAN_MLDSA65_CLEAN_polyveck_sub(&w0, &w0, &h);
502
- PQCLEAN_MLDSA65_CLEAN_polyveck_reduce(&w0);
503
- if (PQCLEAN_MLDSA65_CLEAN_polyveck_chknorm(&w0, GAMMA2 - BETA)) {
504
- goto rej;
505
- }
506
-
507
- PQCLEAN_MLDSA65_CLEAN_polyveck_pointwise_poly_montgomery(&h, &cp, &t0);
508
- PQCLEAN_MLDSA65_CLEAN_polyveck_invntt_tomont(&h);
509
- PQCLEAN_MLDSA65_CLEAN_polyveck_reduce(&h);
510
- if (PQCLEAN_MLDSA65_CLEAN_polyveck_chknorm(&h, GAMMA2)) {
511
- goto rej;
512
- }
513
-
514
- PQCLEAN_MLDSA65_CLEAN_polyveck_add(&w0, &w0, &h);
515
- n = PQCLEAN_MLDSA65_CLEAN_polyveck_make_hint(&h, &w0, &w1);
516
- if (n > OMEGA) {
517
- goto rej;
518
- }
226
+ int pq_sign(uint8_t *signature, size_t *signature_len, const uint8_t *message, size_t message_len,
227
+ const uint8_t *secret_key) {
228
+ return PQCLEAN_MLDSA65_CLEAN_crypto_sign_signature(signature, signature_len, message,
229
+ message_len, secret_key) == 0
230
+ ? PQ_SUCCESS
231
+ : PQ_ERROR_SIGN;
232
+ }
519
233
 
520
- PQCLEAN_MLDSA65_CLEAN_pack_sig(signature, signature, &z, &h);
521
- *signature_len = PQCLEAN_MLDSA65_CLEAN_CRYPTO_BYTES;
522
-
523
- pq_secure_wipe(seedbuf, sizeof(seedbuf));
524
- pq_secure_wipe(&s1, sizeof(s1));
525
- pq_secure_wipe(&y, sizeof(y));
526
- pq_secure_wipe(&z, sizeof(z));
527
- pq_secure_wipe(&t0, sizeof(t0));
528
- pq_secure_wipe(&s2, sizeof(s2));
529
- pq_secure_wipe(&w1, sizeof(w1));
530
- pq_secure_wipe(&w0, sizeof(w0));
531
- pq_secure_wipe(&h, sizeof(h));
532
- pq_secure_wipe(&cp, sizeof(cp));
533
- pq_secure_wipe(mat, sizeof(mat));
534
- return PQ_SUCCESS;
234
+ int pq_verify(const uint8_t *signature, size_t signature_len, const uint8_t *message,
235
+ size_t message_len, const uint8_t *public_key) {
236
+ return PQCLEAN_MLDSA65_CLEAN_crypto_sign_verify(signature, signature_len, message, message_len,
237
+ public_key) == 0
238
+ ? PQ_SUCCESS
239
+ : PQ_ERROR_VERIFY;
535
240
  }
536
241
 
537
242
  int pq_testing_mldsa_keypair_from_seed(uint8_t *public_key, uint8_t *secret_key,
538
243
  const uint8_t *seed, size_t seed_len) {
539
- if (!public_key || !secret_key || !seed || seed_len != SEEDBYTES) {
244
+ int rc;
245
+ if (!public_key || !secret_key || !seed) {
540
246
  return PQ_ERROR_BUFFER;
541
247
  }
542
248
 
543
- return pq_testing_mldsa_keypair_from_seed_impl(public_key, secret_key, seed);
249
+ if (seed_len != 32) {
250
+ return PQ_ERROR_BUFFER;
251
+ }
252
+
253
+ pq_testing_set_seed(seed, seed_len);
254
+ rc = PQCLEAN_MLDSA65_CLEAN_crypto_sign_keypair(public_key, secret_key);
255
+ pq_testing_clear_seed();
256
+ return rc == 0 ? PQ_SUCCESS : PQ_ERROR_KEYPAIR;
544
257
  }
545
258
 
546
259
  int pq_testing_mldsa_sign_from_seed(uint8_t *signature, size_t *signature_len,
547
260
  const uint8_t *message, size_t message_len,
548
261
  const uint8_t *secret_key, const uint8_t *seed,
549
262
  size_t seed_len) {
550
- if (!signature || !signature_len || !message || !secret_key || !seed || seed_len != RNDBYTES) {
263
+ int rc;
264
+ if (!signature || !signature_len || !secret_key || !seed) {
551
265
  return PQ_ERROR_BUFFER;
552
266
  }
553
267
 
554
- return pq_testing_mldsa_sign_from_seed_impl(signature, signature_len, message, message_len,
555
- secret_key, seed);
556
- }
557
-
558
- int pq_mlkem_keypair(uint8_t *public_key, uint8_t *secret_key) {
559
- return mlkem_keypair(public_key, secret_key) == 0 ? PQ_SUCCESS : PQ_ERROR_KEYPAIR;
560
- }
561
-
562
- int pq_mlkem_encapsulate(uint8_t *ciphertext, uint8_t *shared_secret, const uint8_t *public_key) {
563
- return mlkem_encapsulate(ciphertext, shared_secret, public_key) == 0 ? PQ_SUCCESS
564
- : PQ_ERROR_ENCAPSULATE;
565
- }
268
+ if (seed_len != 32) {
269
+ return PQ_ERROR_BUFFER;
270
+ }
566
271
 
567
- int pq_mlkem_decapsulate(uint8_t *shared_secret, const uint8_t *ciphertext,
568
- const uint8_t *secret_key) {
569
- return mlkem_decapsulate(shared_secret, ciphertext, secret_key) == 0 ? PQ_SUCCESS
570
- : PQ_ERROR_DECAPSULATE;
272
+ pq_testing_set_seed(seed, seed_len);
273
+ rc = PQCLEAN_MLDSA65_CLEAN_crypto_sign_signature(signature, signature_len, message, message_len,
274
+ secret_key);
275
+ pq_testing_clear_seed();
276
+ return rc == 0 ? PQ_SUCCESS : PQ_ERROR_SIGN;
571
277
  }
572
278
 
573
279
  int pq_hybrid_kem_keypair(uint8_t *public_key, uint8_t *secret_key) {
574
- hybrid_public_key_t *pk = (hybrid_public_key_t *)public_key;
575
- hybrid_secret_key_t *sk = (hybrid_secret_key_t *)secret_key;
280
+ hybrid_public_key_t pk;
281
+ hybrid_secret_key_t sk;
282
+ int ret;
576
283
 
577
- memset(sk, 0, sizeof(*sk));
284
+ if (!public_key || !secret_key) {
285
+ return PQ_ERROR_BUFFER;
286
+ }
287
+
288
+ memset(&pk, 0, sizeof(pk));
289
+ memset(&sk, 0, sizeof(sk));
578
290
 
579
- if (mlkem_keypair(pk->mlkem_pk, sk->mlkem_sk) != 0) {
580
- pq_secure_wipe(sk, sizeof(*sk));
581
- return PQ_ERROR_KEYPAIR;
291
+ ret = PQCLEAN_MLKEM768_CLEAN_crypto_kem_keypair(pk.mlkem_pk, sk.mlkem_sk) == 0
292
+ ? PQ_SUCCESS
293
+ : PQ_ERROR_KEYPAIR;
294
+ if (ret != PQ_SUCCESS) {
295
+ goto cleanup;
582
296
  }
583
297
 
584
- if (x25519_keypair(pk->x25519_pk, sk->x25519_sk) != 0) {
585
- pq_secure_wipe(sk, sizeof(*sk));
586
- return PQ_ERROR_KEYPAIR;
298
+ ret = x25519_keypair(pk.x25519_pk, sk.x25519_sk);
299
+ if (ret != PQ_SUCCESS) {
300
+ goto cleanup;
587
301
  }
588
302
 
589
- return PQ_SUCCESS;
303
+ memcpy(public_key, &pk, HYBRID_PUBLICKEYBYTES);
304
+ memcpy(secret_key, &sk, HYBRID_SECRETKEYBYTES);
305
+
306
+ cleanup:
307
+ pq_secure_wipe(&sk, sizeof(sk));
308
+
309
+ pq_secure_wipe(&pk, sizeof(pk));
310
+ return ret;
590
311
  }
591
312
 
592
313
  int pq_hybrid_kem_encapsulate(uint8_t *ciphertext, uint8_t *shared_secret,
593
314
  const uint8_t *public_key) {
594
- const hybrid_public_key_t *pk = (const hybrid_public_key_t *)public_key;
595
- hybrid_ciphertext_t *ct = (hybrid_ciphertext_t *)ciphertext;
596
-
315
+ hybrid_public_key_t pk;
316
+ hybrid_ciphertext_t ct;
597
317
  uint8_t mlkem_ss[MLKEM_SHAREDSECRETBYTES];
598
318
  uint8_t x25519_ss[X25519_SHAREDSECRETBYTES];
599
319
  uint8_t x25519_ephemeral_sk[X25519_SECRETKEYBYTES];
600
320
  int ret = PQ_SUCCESS;
601
321
 
322
+ if (!ciphertext || !shared_secret || !public_key) {
323
+ return PQ_ERROR_BUFFER;
324
+ }
325
+
326
+ memcpy(&pk, public_key, HYBRID_PUBLICKEYBYTES);
327
+ memset(&ct, 0, sizeof(ct));
602
328
  memset(mlkem_ss, 0, sizeof(mlkem_ss));
603
329
  memset(x25519_ss, 0, sizeof(x25519_ss));
604
330
  memset(x25519_ephemeral_sk, 0, sizeof(x25519_ephemeral_sk));
605
331
 
606
- if (mlkem_encapsulate(ct->mlkem_ct, mlkem_ss, pk->mlkem_pk) != 0) {
332
+ if (PQCLEAN_MLKEM768_CLEAN_crypto_kem_enc(ct.mlkem_ct, mlkem_ss, pk.mlkem_pk) != 0) {
607
333
  ret = PQ_ERROR_ENCAPSULATE;
608
334
  goto cleanup;
609
335
  }
610
336
 
611
- ret = x25519_keypair(ct->x25519_ephemeral, x25519_ephemeral_sk);
337
+ ret = x25519_keypair(ct.x25519_ephemeral, x25519_ephemeral_sk);
612
338
  if (ret != PQ_SUCCESS) {
613
339
  ret = PQ_ERROR_ENCAPSULATE;
614
340
  goto cleanup;
615
341
  }
616
342
 
617
- ret = x25519_shared_secret(x25519_ss, pk->x25519_pk, x25519_ephemeral_sk);
343
+ ret = x25519_shared_secret(x25519_ss, pk.x25519_pk, x25519_ephemeral_sk);
618
344
  if (ret != PQ_SUCCESS) {
619
345
  ret = PQ_ERROR_ENCAPSULATE;
620
346
  goto cleanup;
621
347
  }
622
348
 
623
- ret = hybrid_combiner(shared_secret, mlkem_ss, x25519_ss, pk->x25519_pk, ct);
349
+ ret = xwing_combiner(shared_secret, mlkem_ss, x25519_ss, ct.x25519_ephemeral, pk.x25519_pk);
624
350
  if (ret != PQ_SUCCESS) {
625
351
  goto cleanup;
626
352
  }
627
353
 
628
- ret = PQ_SUCCESS;
354
+ memcpy(ciphertext, &ct, HYBRID_CIPHERTEXTBYTES);
629
355
 
630
356
  cleanup:
631
357
  pq_secure_wipe(mlkem_ss, sizeof(mlkem_ss));
632
358
  pq_secure_wipe(x25519_ss, sizeof(x25519_ss));
633
359
  pq_secure_wipe(x25519_ephemeral_sk, sizeof(x25519_ephemeral_sk));
360
+ pq_secure_wipe(&pk, sizeof(pk));
361
+ pq_secure_wipe(&ct, sizeof(ct));
634
362
  return ret;
635
363
  }
636
364
 
637
365
  int pq_hybrid_kem_decapsulate(uint8_t *shared_secret, const uint8_t *ciphertext,
638
366
  const uint8_t *secret_key) {
639
- const hybrid_ciphertext_t *ct = (const hybrid_ciphertext_t *)ciphertext;
367
+ hybrid_ciphertext_t ct;
368
+ hybrid_secret_key_t sk;
640
369
  uint8_t recipient_x25519_pk[X25519_PUBLICKEYBYTES];
641
- const hybrid_secret_key_t *sk = (const hybrid_secret_key_t *)secret_key;
642
-
643
370
  uint8_t mlkem_ss[MLKEM_SHAREDSECRETBYTES];
644
371
  uint8_t x25519_ss[X25519_SHAREDSECRETBYTES];
372
+ EVP_PKEY *pkey = NULL;
373
+ size_t pklen = X25519_PUBLICKEYBYTES;
645
374
  int ret = PQ_SUCCESS;
646
375
 
376
+ if (!shared_secret || !ciphertext || !secret_key) {
377
+ return PQ_ERROR_BUFFER;
378
+ }
379
+
380
+ memcpy(&ct, ciphertext, HYBRID_CIPHERTEXTBYTES);
381
+ memcpy(&sk, secret_key, HYBRID_SECRETKEYBYTES);
647
382
  memset(recipient_x25519_pk, 0, sizeof(recipient_x25519_pk));
648
383
  memset(mlkem_ss, 0, sizeof(mlkem_ss));
649
384
  memset(x25519_ss, 0, sizeof(x25519_ss));
650
385
 
651
- ret = mlkem_decapsulate(mlkem_ss, ct->mlkem_ct, sk->mlkem_sk) == 0 ? PQ_SUCCESS
652
- : PQ_ERROR_DECAPSULATE;
653
- if (ret != PQ_SUCCESS) {
386
+ if (PQCLEAN_MLKEM768_CLEAN_crypto_kem_dec(mlkem_ss, ct.mlkem_ct, sk.mlkem_sk) != 0) {
387
+ ret = PQ_ERROR_DECAPSULATE;
654
388
  goto cleanup;
655
389
  }
656
390
 
657
- ret = x25519_shared_secret(x25519_ss, ct->x25519_ephemeral, sk->x25519_sk);
391
+ ret = x25519_shared_secret(x25519_ss, ct.x25519_ephemeral, sk.x25519_sk);
658
392
  if (ret != PQ_SUCCESS) {
659
393
  ret = PQ_ERROR_DECAPSULATE;
660
394
  goto cleanup;
661
395
  }
662
396
 
663
- ret = x25519_public_from_secret(recipient_x25519_pk, sk->x25519_sk);
664
- if (ret != PQ_SUCCESS) {
397
+ pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_X25519, NULL, sk.x25519_sk, X25519_SECRETKEYBYTES);
398
+ if (!pkey) {
399
+ ret = PQ_ERROR_DECAPSULATE;
400
+ goto cleanup;
401
+ }
402
+ if (EVP_PKEY_get_raw_public_key(pkey, recipient_x25519_pk, &pklen) <= 0 ||
403
+ pklen != X25519_PUBLICKEYBYTES) {
665
404
  ret = PQ_ERROR_DECAPSULATE;
666
405
  goto cleanup;
667
406
  }
668
407
 
669
- ret = hybrid_combiner(shared_secret, mlkem_ss, x25519_ss, recipient_x25519_pk, ct);
408
+ ret = xwing_combiner(shared_secret, mlkem_ss, x25519_ss, ct.x25519_ephemeral,
409
+ recipient_x25519_pk);
670
410
 
671
411
  cleanup:
412
+ if (pkey)
413
+ EVP_PKEY_free(pkey);
672
414
  pq_secure_wipe(recipient_x25519_pk, sizeof(recipient_x25519_pk));
673
415
  pq_secure_wipe(mlkem_ss, sizeof(mlkem_ss));
674
416
  pq_secure_wipe(x25519_ss, sizeof(x25519_ss));
417
+ pq_secure_wipe(&ct, sizeof(ct));
418
+ pq_secure_wipe(&sk, sizeof(sk));
675
419
  return ret;
676
420
  }
677
421
 
678
- int pq_sign_keypair(uint8_t *public_key, uint8_t *secret_key) {
679
- return mldsa_keypair(public_key, secret_key) == 0 ? PQ_SUCCESS : PQ_ERROR_KEYPAIR;
680
- }
681
-
682
- int pq_sign(uint8_t *signature, size_t *signature_len, const uint8_t *message, size_t message_len,
683
- const uint8_t *secret_key) {
684
- return mldsa_sign(signature, signature_len, message, message_len, secret_key) == 0
685
- ? PQ_SUCCESS
686
- : PQ_ERROR_SIGN;
687
- }
688
-
689
- int pq_verify(const uint8_t *signature, size_t signature_len, const uint8_t *message,
690
- size_t message_len, const uint8_t *public_key) {
691
- return mldsa_verify(signature, signature_len, message, message_len, public_key) == 0
692
- ? PQ_SUCCESS
693
- : PQ_ERROR_VERIFY;
694
- }
695
-
696
422
  #define PQC_SERIALIZATION_MAGIC_0 'P'
697
423
  #define PQC_SERIALIZATION_MAGIC_1 'Q'
698
424
  #define PQC_SERIALIZATION_MAGIC_2 'C'
@@ -702,72 +428,64 @@ int pq_verify(const uint8_t *signature, size_t signature_len, const uint8_t *mes
702
428
  #define PQC_SERIALIZATION_TYPE_SECRET 0x02
703
429
 
704
430
  static const char PQC_OID_ML_KEM_768[] = "2.25.186599352125448088867056807454444238446";
705
- static const char PQC_OID_ML_KEM_768_X25519_HKDF_SHA256[] =
706
- "2.25.260242945110721168101139140490528778800";
431
+
432
+ static const char PQC_OID_ML_KEM_768_X25519_XWING[] =
433
+ "2.25.318532651283923671095712569430174917109";
707
434
  static const char PQC_OID_ML_DSA_65[] = "2.25.305232938483772195555080795650659207792";
435
+ static const char PQC_PUBLIC_KEY_PEM_LABEL[] = "PQC PUBLIC KEY CONTAINER";
436
+ static const char PQC_PRIVATE_KEY_PEM_LABEL[] = "PQC PRIVATE KEY CONTAINER";
708
437
 
709
- static int pq_serialization_key_bytes_for_algorithm(const char *algorithm, int is_public,
710
- size_t *expected_len) {
711
- if (!algorithm || !expected_len)
712
- return PQ_ERROR_BUFFER;
713
- if (strcmp(algorithm, "ml_kem_768") == 0) {
714
- *expected_len = is_public ? PQ_MLKEM_PUBLICKEYBYTES : PQ_MLKEM_SECRETKEYBYTES;
715
- return PQ_SUCCESS;
716
- }
717
- if (strcmp(algorithm, "ml_kem_768_x25519_hkdf_sha256") == 0) {
718
- *expected_len = is_public ? PQ_HYBRID_PUBLICKEYBYTES : PQ_HYBRID_SECRETKEYBYTES;
719
- return PQ_SUCCESS;
720
- }
721
- if (strcmp(algorithm, "ml_dsa_65") == 0) {
722
- *expected_len = is_public ? MLDSA_PUBLICKEYBYTES : MLDSA_SECRETKEYBYTES;
723
- return PQ_SUCCESS;
724
- }
725
- return PQ_ERROR_BUFFER;
726
- }
438
+ typedef struct {
439
+ const char *algorithm;
440
+ const char *oid;
441
+ size_t public_key_len;
442
+ size_t secret_key_len;
443
+ } pq_serialization_algorithm_t;
727
444
 
728
- static int pq_serialization_oid_for_algorithm(const char *algorithm, const char **oid_out) {
729
- if (!algorithm || !oid_out)
730
- return PQ_ERROR_BUFFER;
731
- if (strcmp(algorithm, "ml_kem_768") == 0) {
732
- *oid_out = PQC_OID_ML_KEM_768;
733
- return PQ_SUCCESS;
734
- }
735
- if (strcmp(algorithm, "ml_kem_768_x25519_hkdf_sha256") == 0) {
736
- *oid_out = PQC_OID_ML_KEM_768_X25519_HKDF_SHA256;
737
- return PQ_SUCCESS;
738
- }
739
- if (strcmp(algorithm, "ml_dsa_65") == 0) {
740
- *oid_out = PQC_OID_ML_DSA_65;
741
- return PQ_SUCCESS;
445
+ static const pq_serialization_algorithm_t PQC_SERIALIZATION_ALGORITHMS[] = {
446
+ {"ml_kem_768", PQC_OID_ML_KEM_768, PQ_MLKEM_PUBLICKEYBYTES, PQ_MLKEM_SECRETKEYBYTES},
447
+ {"ml_kem_768_x25519_xwing", PQC_OID_ML_KEM_768_X25519_XWING, PQ_HYBRID_PUBLICKEYBYTES,
448
+ PQ_HYBRID_SECRETKEYBYTES},
449
+ {"ml_dsa_65", PQC_OID_ML_DSA_65, MLDSA_PUBLICKEYBYTES, MLDSA_SECRETKEYBYTES},
450
+ };
451
+
452
+ static const pq_serialization_algorithm_t *pq_find_serialization_algorithm(const char *algorithm) {
453
+ size_t i;
454
+
455
+ if (!algorithm)
456
+ return NULL;
457
+
458
+ for (i = 0; i < sizeof(PQC_SERIALIZATION_ALGORITHMS) / sizeof(PQC_SERIALIZATION_ALGORITHMS[0]);
459
+ ++i) {
460
+ if (strcmp(algorithm, PQC_SERIALIZATION_ALGORITHMS[i].algorithm) == 0)
461
+ return &PQC_SERIALIZATION_ALGORITHMS[i];
742
462
  }
743
- return PQ_ERROR_BUFFER;
463
+
464
+ return NULL;
744
465
  }
745
466
 
746
- static int pq_serialization_algorithm_for_oid(const char *oid, size_t oid_len,
747
- const char **algorithm_out) {
748
- if (!oid || !algorithm_out)
749
- return PQ_ERROR_BUFFER;
750
- if (oid_len == strlen(PQC_OID_ML_KEM_768) && memcmp(oid, PQC_OID_ML_KEM_768, oid_len) == 0) {
751
- *algorithm_out = "ml_kem_768";
752
- return PQ_SUCCESS;
753
- }
754
- if (oid_len == strlen(PQC_OID_ML_KEM_768_X25519_HKDF_SHA256) &&
755
- memcmp(oid, PQC_OID_ML_KEM_768_X25519_HKDF_SHA256, oid_len) == 0) {
756
- *algorithm_out = "ml_kem_768_x25519_hkdf_sha256";
757
- return PQ_SUCCESS;
758
- }
759
- if (oid_len == strlen(PQC_OID_ML_DSA_65) && memcmp(oid, PQC_OID_ML_DSA_65, oid_len) == 0) {
760
- *algorithm_out = "ml_dsa_65";
761
- return PQ_SUCCESS;
467
+ static const pq_serialization_algorithm_t *pq_find_serialization_algorithm_by_oid(const char *oid,
468
+ size_t oid_len) {
469
+ size_t i;
470
+
471
+ if (!oid)
472
+ return NULL;
473
+
474
+ for (i = 0; i < sizeof(PQC_SERIALIZATION_ALGORITHMS) / sizeof(PQC_SERIALIZATION_ALGORITHMS[0]);
475
+ ++i) {
476
+ const pq_serialization_algorithm_t *entry = &PQC_SERIALIZATION_ALGORITHMS[i];
477
+ if (oid_len == strlen(entry->oid) && memcmp(oid, entry->oid, oid_len) == 0)
478
+ return entry;
762
479
  }
763
- return PQ_ERROR_BUFFER;
480
+
481
+ return NULL;
764
482
  }
765
483
 
766
484
  static int pq_encode_serialized_key(uint8_t **output, size_t *output_len, uint8_t type,
767
485
  const uint8_t *key_bytes, size_t key_len,
768
486
  const char *algorithm) {
769
- const char *oid = NULL;
770
- size_t expected_len = 0;
487
+ const pq_serialization_algorithm_t *entry;
488
+ size_t expected_len;
771
489
  size_t oid_len;
772
490
  size_t total_len = 0;
773
491
  uint8_t *buf;
@@ -779,15 +497,16 @@ static int pq_encode_serialized_key(uint8_t **output, size_t *output_len, uint8_
779
497
  *output = NULL;
780
498
  *output_len = 0;
781
499
 
782
- ret = pq_serialization_key_bytes_for_algorithm(algorithm, type == PQC_SERIALIZATION_TYPE_PUBLIC,
783
- &expected_len);
784
- if (ret != PQ_SUCCESS || key_len != expected_len)
500
+ entry = pq_find_serialization_algorithm(algorithm);
501
+ if (!entry)
785
502
  return PQ_ERROR_BUFFER;
786
- ret = pq_serialization_oid_for_algorithm(algorithm, &oid);
787
- if (ret != PQ_SUCCESS)
788
- return ret;
789
503
 
790
- oid_len = strlen(oid);
504
+ expected_len =
505
+ (type == PQC_SERIALIZATION_TYPE_PUBLIC) ? entry->public_key_len : entry->secret_key_len;
506
+ if (key_len != expected_len)
507
+ return PQ_ERROR_BUFFER;
508
+
509
+ oid_len = strlen(entry->oid);
791
510
  if (oid_len == 0 || oid_len > UINT16_MAX)
792
511
  return PQ_ERROR_BUFFER;
793
512
  if (key_len > UINT32_MAX)
@@ -821,7 +540,7 @@ static int pq_encode_serialized_key(uint8_t **output, size_t *output_len, uint8_
821
540
  buf[5] = type;
822
541
  buf[6] = (uint8_t)((oid_len >> 8) & 0xFF);
823
542
  buf[7] = (uint8_t)(oid_len & 0xFF);
824
- memcpy(buf + 8, oid, oid_len);
543
+ memcpy(buf + 8, entry->oid, oid_len);
825
544
  buf[8 + oid_len + 0] = (uint8_t)((key_len >> 24) & 0xFF);
826
545
  buf[8 + oid_len + 1] = (uint8_t)((key_len >> 16) & 0xFF);
827
546
  buf[8 + oid_len + 2] = (uint8_t)((key_len >> 8) & 0xFF);
@@ -837,12 +556,11 @@ static int pq_decode_serialized_key(const uint8_t *input, size_t input_len, uint
837
556
  char **algorithm_out, uint8_t **key_out, size_t *key_len_out) {
838
557
  uint16_t oid_len;
839
558
  uint32_t key_len;
840
- const char *algorithm = NULL;
559
+ const pq_serialization_algorithm_t *entry;
841
560
  size_t offset;
842
561
  size_t expected_len = 0;
843
562
  uint8_t *key_copy = NULL;
844
563
  char *algorithm_copy = NULL;
845
- int ret;
846
564
 
847
565
  if (!input || !algorithm_out || !key_out || !key_len_out)
848
566
  return PQ_ERROR_BUFFER;
@@ -866,18 +584,18 @@ static int pq_decode_serialized_key(const uint8_t *input, size_t input_len, uint
866
584
  offset = 8;
867
585
  if (input_len < offset || input_len - offset < (size_t)oid_len + 4)
868
586
  return PQ_ERROR_BUFFER;
869
- ret = pq_serialization_algorithm_for_oid((const char *)(input + offset), oid_len, &algorithm);
870
- if (ret != PQ_SUCCESS)
871
- return ret;
587
+ entry = pq_find_serialization_algorithm_by_oid((const char *)(input + offset), oid_len);
588
+ if (!entry)
589
+ return PQ_ERROR_BUFFER;
872
590
  offset += oid_len;
873
591
  key_len = ((uint32_t)input[offset + 0] << 24) | ((uint32_t)input[offset + 1] << 16) |
874
592
  ((uint32_t)input[offset + 2] << 8) | (uint32_t)input[offset + 3];
875
593
  offset += 4;
876
594
  if (input_len < offset || input_len - offset != (size_t)key_len)
877
595
  return PQ_ERROR_BUFFER;
878
- ret = pq_serialization_key_bytes_for_algorithm(
879
- algorithm, expected_type == PQC_SERIALIZATION_TYPE_PUBLIC, &expected_len);
880
- if (ret != PQ_SUCCESS || (size_t)key_len != expected_len)
596
+ expected_len = (expected_type == PQC_SERIALIZATION_TYPE_PUBLIC) ? entry->public_key_len
597
+ : entry->secret_key_len;
598
+ if ((size_t)key_len != expected_len)
881
599
  return PQ_ERROR_BUFFER;
882
600
 
883
601
  key_copy = malloc((size_t)key_len);
@@ -886,14 +604,14 @@ static int pq_decode_serialized_key(const uint8_t *input, size_t input_len, uint
886
604
  memcpy(key_copy, input + offset, (size_t)key_len);
887
605
 
888
606
  {
889
- size_t algorithm_len = strlen(algorithm);
607
+ size_t algorithm_len = strlen(entry->algorithm);
890
608
  algorithm_copy = malloc(algorithm_len + 1);
891
609
  if (!algorithm_copy) {
892
610
  pq_secure_wipe(key_copy, (size_t)key_len);
893
611
  free(key_copy);
894
612
  return PQ_ERROR_NOMEM;
895
613
  }
896
- memcpy(algorithm_copy, algorithm, algorithm_len + 1);
614
+ memcpy(algorithm_copy, entry->algorithm, algorithm_len + 1);
897
615
  }
898
616
 
899
617
  *algorithm_out = algorithm_copy;
@@ -904,17 +622,17 @@ static int pq_decode_serialized_key(const uint8_t *input, size_t input_len, uint
904
622
 
905
623
  static int pq_der_to_pem(const char *label, const uint8_t *der, size_t der_len, char **output,
906
624
  size_t *output_len) {
907
- size_t encoded_len = 0;
908
- size_t line_count = 0;
909
- size_t body_len = 0;
910
- size_t total_len = 0;
911
- unsigned char *encoded = NULL;
912
- char *pem = NULL;
625
+ BIO *bio_mem = NULL;
626
+ BIO *bio_b64 = NULL;
627
+ BIO *bio_chain = NULL;
628
+ BUF_MEM *bptr = NULL;
913
629
  char header[64];
914
630
  char footer[64];
915
- char *cursor;
916
631
  int header_len, footer_len;
917
- int ret;
632
+ int ret = PQ_ERROR_OPENSSL;
633
+ char *pem = NULL;
634
+ size_t total_len = 0;
635
+ size_t needed = 0;
918
636
 
919
637
  if (!label || !der || !output || !output_len)
920
638
  return PQ_ERROR_BUFFER;
@@ -927,55 +645,68 @@ static int pq_der_to_pem(const char *label, const uint8_t *der, size_t der_len,
927
645
  if (der_len > (size_t)INT_MAX)
928
646
  return PQ_ERROR_BUFFER;
929
647
 
930
- ret = pq_size_mul((der_len + 2) / 3, 4, &encoded_len);
931
- if (ret != PQ_SUCCESS)
932
- return ret;
933
- ret = pq_size_add(encoded_len, 63, &line_count);
934
- if (ret != PQ_SUCCESS)
935
- return ret;
936
- line_count /= 64;
937
- ret = pq_size_add(encoded_len, line_count, &body_len);
938
- if (ret != PQ_SUCCESS)
939
- return ret;
940
- ret = pq_size_add((size_t)header_len, body_len, &total_len);
941
- if (ret != PQ_SUCCESS)
942
- return ret;
943
- ret = pq_size_add(total_len, (size_t)footer_len, &total_len);
944
- if (ret != PQ_SUCCESS)
945
- return ret;
648
+ bio_b64 = BIO_new(BIO_f_base64());
649
+ bio_mem = BIO_new(BIO_s_mem());
650
+ if (!bio_b64 || !bio_mem) {
651
+ ret = PQ_ERROR_NOMEM;
652
+ goto cleanup;
653
+ }
946
654
 
947
- encoded = malloc(encoded_len + 1);
948
- pem = malloc(total_len + 1);
949
- if (!encoded || !pem) {
950
- free(encoded);
951
- free(pem);
952
- return PQ_ERROR_NOMEM;
655
+ bio_chain = BIO_push(bio_b64, bio_mem);
656
+
657
+ if (BIO_write(bio_chain, der, (int)der_len) != (int)der_len)
658
+ goto cleanup;
659
+ if (BIO_flush(bio_chain) != 1)
660
+ goto cleanup;
661
+ BIO_get_mem_ptr(bio_chain, &bptr);
662
+ if (!bptr || !bptr->data)
663
+ goto cleanup;
664
+
665
+ {
666
+ size_t body_len = bptr->length;
667
+ needed = (size_t)header_len + 1 + body_len;
668
+ if (body_len == 0 || bptr->data[body_len - 1] != '\n')
669
+ needed += 1;
670
+ needed += (size_t)footer_len + 1;
671
+
672
+ pem = malloc(needed);
673
+ if (!pem) {
674
+ ret = PQ_ERROR_NOMEM;
675
+ goto cleanup;
676
+ }
677
+ char *cur = pem;
678
+ memcpy(cur, header, (size_t)header_len);
679
+ cur += header_len;
680
+ *cur++ = '\n';
681
+ memcpy(cur, bptr->data, body_len);
682
+ cur += body_len;
683
+ if (body_len == 0 || bptr->data[body_len - 1] != '\n')
684
+ *cur++ = '\n';
685
+ memcpy(cur, footer, (size_t)footer_len);
686
+ cur += footer_len;
687
+ *cur = '\0';
688
+ total_len = (size_t)(cur - pem);
953
689
  }
954
- if (EVP_EncodeBlock(encoded, der, (int)der_len) != (int)encoded_len) {
955
- free(encoded);
956
- pq_secure_wipe(pem, total_len + 1);
690
+
691
+ *output = pem;
692
+ *output_len = total_len;
693
+ pem = NULL;
694
+ ret = PQ_SUCCESS;
695
+
696
+ cleanup:
697
+ if (bio_chain) {
698
+ BIO_free_all(bio_chain);
699
+ } else {
700
+ if (bio_b64)
701
+ BIO_free(bio_b64);
702
+ if (bio_mem)
703
+ BIO_free(bio_mem);
704
+ }
705
+ if (pem) {
706
+ pq_secure_wipe(pem, needed);
957
707
  free(pem);
958
- return PQ_ERROR_OPENSSL;
959
- }
960
- cursor = pem;
961
- memcpy(cursor, header, (size_t)header_len);
962
- cursor += header_len;
963
- for (size_t off = 0; off < encoded_len; off += 64) {
964
- size_t chunk = encoded_len - off;
965
- if (chunk > 64)
966
- chunk = 64;
967
- memcpy(cursor, encoded + off, chunk);
968
- cursor += chunk;
969
- *cursor++ = '\n';
970
708
  }
971
- memcpy(cursor, footer, (size_t)footer_len);
972
- cursor += footer_len;
973
- *cursor = '\0';
974
- *output = pem;
975
- *output_len = (size_t)(cursor - pem);
976
- pq_secure_wipe(encoded, encoded_len + 1);
977
- free(encoded);
978
- return PQ_SUCCESS;
709
+ return ret;
979
710
  }
980
711
 
981
712
  static int pq_pem_to_der(const char *label, const char *input, size_t input_len, uint8_t **der_out,
@@ -984,13 +715,13 @@ static int pq_pem_to_der(const char *label, const char *input, size_t input_len,
984
715
  int header_len, footer_len;
985
716
  const char *body_start, *footer_pos;
986
717
  const char *tail;
987
- char *encoded = NULL;
988
718
  uint8_t *der = NULL;
989
- size_t encoded_len = 0;
990
- size_t der_capacity = 0;
991
- int decoded_len;
719
+ size_t body_len = 0;
992
720
  int ret;
993
- size_t pad = 0;
721
+ BIO *bio_b64 = NULL;
722
+ BIO *bio_mem = NULL;
723
+ BIO *bio_chain = NULL;
724
+ int decoded_len = 0;
994
725
 
995
726
  if (!label || !input || !der_out || !der_len_out)
996
727
  return PQ_ERROR_BUFFER;
@@ -1030,63 +761,57 @@ static int pq_pem_to_der(const char *label, const char *input, size_t input_len,
1030
761
  tail++;
1031
762
  }
1032
763
 
1033
- encoded = malloc((size_t)(footer_pos - body_start) + 1);
1034
- if (!encoded)
1035
- return PQ_ERROR_NOMEM;
1036
- for (const char *p = body_start; p < footer_pos; ++p) {
1037
- if (pq_is_pem_whitespace(*p))
1038
- continue;
1039
- encoded[encoded_len++] = *p;
1040
- }
1041
- if (encoded_len == 0 || (encoded_len % 4) != 0) {
1042
- free(encoded);
764
+ body_len = (size_t)(footer_pos - body_start);
765
+ if (body_len > (size_t)INT_MAX)
1043
766
  return PQ_ERROR_BUFFER;
1044
- }
1045
- encoded[encoded_len] = '\0';
1046
767
 
1047
- ret = pq_size_mul(encoded_len / 4, 3, &der_capacity);
1048
- if (ret != PQ_SUCCESS) {
1049
- pq_secure_wipe(encoded, encoded_len + 1);
1050
- free(encoded);
1051
- return ret;
1052
- }
1053
- ret = pq_size_add(der_capacity, 1, &der_capacity);
1054
- if (ret != PQ_SUCCESS || der_capacity == 0) {
1055
- pq_secure_wipe(encoded, encoded_len + 1);
1056
- free(encoded);
1057
- return PQ_ERROR_BUFFER;
1058
- }
768
+ {
769
+ size_t der_cap = (body_len * 3) / 4 + 3;
770
+ der = malloc(der_cap ? der_cap : 1);
771
+ if (!der)
772
+ return PQ_ERROR_NOMEM;
1059
773
 
1060
- der = malloc(der_capacity);
1061
- if (!der) {
1062
- pq_secure_wipe(encoded, encoded_len + 1);
1063
- free(encoded);
1064
- return PQ_ERROR_NOMEM;
1065
- }
1066
- decoded_len = EVP_DecodeBlock(der, (unsigned char *)encoded, (int)encoded_len);
1067
- if (decoded_len < 0) {
1068
- pq_secure_wipe(encoded, encoded_len + 1);
1069
- free(encoded);
1070
- pq_secure_wipe(der, der_capacity);
1071
- free(der);
1072
- return PQ_ERROR_BUFFER;
774
+ bio_mem = BIO_new_mem_buf(body_start, (int)body_len);
775
+ bio_b64 = BIO_new(BIO_f_base64());
776
+ if (!bio_mem || !bio_b64) {
777
+ ret = PQ_ERROR_NOMEM;
778
+ goto cleanup;
779
+ }
780
+ bio_chain = BIO_push(bio_b64, bio_mem);
781
+
782
+ decoded_len = BIO_read(bio_chain, der, (int)der_cap);
783
+ if (decoded_len <= 0) {
784
+ ret = PQ_ERROR_BUFFER;
785
+ goto cleanup;
786
+ }
787
+ {
788
+ unsigned char tail_byte;
789
+ int extra = BIO_read(bio_chain, &tail_byte, 1);
790
+ if (extra > 0) {
791
+ ret = PQ_ERROR_BUFFER;
792
+ goto cleanup;
793
+ }
794
+ }
795
+
796
+ *der_len_out = (size_t)decoded_len;
797
+ *der_out = der;
798
+ der = NULL;
799
+ ret = PQ_SUCCESS;
1073
800
  }
1074
- if (encoded_len >= 1 && encoded[encoded_len - 1] == '=')
1075
- pad++;
1076
- if (encoded_len >= 2 && encoded[encoded_len - 2] == '=')
1077
- pad++;
1078
- if ((size_t)decoded_len < pad) {
1079
- pq_secure_wipe(encoded, encoded_len + 1);
1080
- free(encoded);
1081
- pq_secure_wipe(der, der_capacity);
801
+
802
+ cleanup:
803
+ if (bio_chain) {
804
+ BIO_free_all(bio_chain);
805
+ } else {
806
+ if (bio_b64)
807
+ BIO_free(bio_b64);
808
+ if (bio_mem)
809
+ BIO_free(bio_mem);
810
+ }
811
+ if (der) {
1082
812
  free(der);
1083
- return PQ_ERROR_BUFFER;
1084
813
  }
1085
- *der_len_out = (size_t)decoded_len - pad;
1086
- *der_out = der;
1087
- pq_secure_wipe(encoded, encoded_len + 1);
1088
- free(encoded);
1089
- return PQ_SUCCESS;
814
+ return ret;
1090
815
  }
1091
816
 
1092
817
  int pq_public_key_to_pqc_container_der(uint8_t **output, size_t *output_len,
@@ -1104,7 +829,7 @@ int pq_public_key_to_pqc_container_pem(char **output, size_t *output_len, const
1104
829
  ret = pq_public_key_to_pqc_container_der(&der, &der_len, public_key, public_key_len, algorithm);
1105
830
  if (ret != PQ_SUCCESS)
1106
831
  return ret;
1107
- ret = pq_der_to_pem("PQC PUBLIC KEY CONTAINER", der, der_len, output, output_len);
832
+ ret = pq_der_to_pem(PQC_PUBLIC_KEY_PEM_LABEL, der, der_len, output, output_len);
1108
833
  pq_secure_wipe(der, der_len);
1109
834
  free(der);
1110
835
  return ret;
@@ -1125,7 +850,7 @@ int pq_secret_key_to_pqc_container_pem(char **output, size_t *output_len, const
1125
850
  ret = pq_secret_key_to_pqc_container_der(&der, &der_len, secret_key, secret_key_len, algorithm);
1126
851
  if (ret != PQ_SUCCESS)
1127
852
  return ret;
1128
- ret = pq_der_to_pem("PQC PRIVATE KEY CONTAINER", der, der_len, output, output_len);
853
+ ret = pq_der_to_pem(PQC_PRIVATE_KEY_PEM_LABEL, der, der_len, output, output_len);
1129
854
  pq_secure_wipe(der, der_len);
1130
855
  free(der);
1131
856
  return ret;
@@ -1143,7 +868,7 @@ int pq_public_key_from_pqc_container_pem(char **algorithm_out, uint8_t **key_out
1143
868
  uint8_t *der = NULL;
1144
869
  size_t der_len = 0;
1145
870
  int ret;
1146
- ret = pq_pem_to_der("PQC PUBLIC KEY CONTAINER", input, input_len, &der, &der_len);
871
+ ret = pq_pem_to_der(PQC_PUBLIC_KEY_PEM_LABEL, input, input_len, &der, &der_len);
1147
872
  if (ret != PQ_SUCCESS)
1148
873
  return ret;
1149
874
  ret = pq_public_key_from_pqc_container_der(algorithm_out, key_out, key_len_out, der, der_len);
@@ -1164,7 +889,7 @@ int pq_secret_key_from_pqc_container_pem(char **algorithm_out, uint8_t **key_out
1164
889
  uint8_t *der = NULL;
1165
890
  size_t der_len = 0;
1166
891
  int ret;
1167
- ret = pq_pem_to_der("PQC PRIVATE KEY CONTAINER", input, input_len, &der, &der_len);
892
+ ret = pq_pem_to_der(PQC_PRIVATE_KEY_PEM_LABEL, input, input_len, &der, &der_len);
1168
893
  if (ret != PQ_SUCCESS)
1169
894
  return ret;
1170
895
  ret = pq_secret_key_from_pqc_container_der(algorithm_out, key_out, key_len_out, der, der_len);
@@ -1174,5 +899,5 @@ int pq_secret_key_from_pqc_container_pem(char **algorithm_out, uint8_t **key_out
1174
899
  }
1175
900
 
1176
901
  const char *pq_version(void) {
1177
- return "0.1.0";
902
+ return "0.3.0";
1178
903
  }