pq_crypto 0.2.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
- }
145
+ static const uint8_t XWING_LABEL[6] = {0x5c, 0x2e, 0x2f, 0x2f, 0x5e, 0x5c};
201
146
 
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
- }
233
-
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);
313
- }
314
-
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);
317
- }
318
-
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);
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;
321
186
  }
322
187
 
323
- static int mldsa_keypair(uint8_t *pk, uint8_t *sk) {
324
- return PQCLEAN_MLDSA65_CLEAN_crypto_sign_keypair(pk, sk);
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;
325
191
  }
326
192
 
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) {
246
+ return PQ_ERROR_BUFFER;
247
+ }
248
+
249
+ if (seed_len != 32) {
540
250
  return PQ_ERROR_BUFFER;
541
251
  }
542
252
 
543
- return pq_testing_mldsa_keypair_from_seed_impl(public_key, secret_key, seed);
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;
283
+
284
+ if (!public_key || !secret_key) {
285
+ return PQ_ERROR_BUFFER;
286
+ }
576
287
 
577
- memset(sk, 0, sizeof(*sk));
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,8 +428,9 @@ 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";
708
435
  static const char PQC_PUBLIC_KEY_PEM_LABEL[] = "PQC PUBLIC KEY CONTAINER";
709
436
  static const char PQC_PRIVATE_KEY_PEM_LABEL[] = "PQC PRIVATE KEY CONTAINER";
@@ -717,8 +444,8 @@ typedef struct {
717
444
 
718
445
  static const pq_serialization_algorithm_t PQC_SERIALIZATION_ALGORITHMS[] = {
719
446
  {"ml_kem_768", PQC_OID_ML_KEM_768, PQ_MLKEM_PUBLICKEYBYTES, PQ_MLKEM_SECRETKEYBYTES},
720
- {"ml_kem_768_x25519_hkdf_sha256", PQC_OID_ML_KEM_768_X25519_HKDF_SHA256,
721
- PQ_HYBRID_PUBLICKEYBYTES, PQ_HYBRID_SECRETKEYBYTES},
447
+ {"ml_kem_768_x25519_xwing", PQC_OID_ML_KEM_768_X25519_XWING, PQ_HYBRID_PUBLICKEYBYTES,
448
+ PQ_HYBRID_SECRETKEYBYTES},
722
449
  {"ml_dsa_65", PQC_OID_ML_DSA_65, MLDSA_PUBLICKEYBYTES, MLDSA_SECRETKEYBYTES},
723
450
  };
724
451
 
@@ -895,17 +622,17 @@ static int pq_decode_serialized_key(const uint8_t *input, size_t input_len, uint
895
622
 
896
623
  static int pq_der_to_pem(const char *label, const uint8_t *der, size_t der_len, char **output,
897
624
  size_t *output_len) {
898
- size_t encoded_len = 0;
899
- size_t line_count = 0;
900
- size_t body_len = 0;
901
- size_t total_len = 0;
902
- unsigned char *encoded = NULL;
903
- char *pem = NULL;
625
+ BIO *bio_mem = NULL;
626
+ BIO *bio_b64 = NULL;
627
+ BIO *bio_chain = NULL;
628
+ BUF_MEM *bptr = NULL;
904
629
  char header[64];
905
630
  char footer[64];
906
- char *cursor;
907
631
  int header_len, footer_len;
908
- int ret;
632
+ int ret = PQ_ERROR_OPENSSL;
633
+ char *pem = NULL;
634
+ size_t total_len = 0;
635
+ size_t needed = 0;
909
636
 
910
637
  if (!label || !der || !output || !output_len)
911
638
  return PQ_ERROR_BUFFER;
@@ -918,55 +645,68 @@ static int pq_der_to_pem(const char *label, const uint8_t *der, size_t der_len,
918
645
  if (der_len > (size_t)INT_MAX)
919
646
  return PQ_ERROR_BUFFER;
920
647
 
921
- ret = pq_size_mul((der_len + 2) / 3, 4, &encoded_len);
922
- if (ret != PQ_SUCCESS)
923
- return ret;
924
- ret = pq_size_add(encoded_len, 63, &line_count);
925
- if (ret != PQ_SUCCESS)
926
- return ret;
927
- line_count /= 64;
928
- ret = pq_size_add(encoded_len, line_count, &body_len);
929
- if (ret != PQ_SUCCESS)
930
- return ret;
931
- ret = pq_size_add((size_t)header_len, body_len, &total_len);
932
- if (ret != PQ_SUCCESS)
933
- return ret;
934
- ret = pq_size_add(total_len, (size_t)footer_len, &total_len);
935
- if (ret != PQ_SUCCESS)
936
- 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
+ }
937
654
 
938
- encoded = malloc(encoded_len + 1);
939
- pem = malloc(total_len + 1);
940
- if (!encoded || !pem) {
941
- free(encoded);
942
- free(pem);
943
- 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);
944
689
  }
945
- if (EVP_EncodeBlock(encoded, der, (int)der_len) != (int)encoded_len) {
946
- free(encoded);
947
- 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);
948
707
  free(pem);
949
- return PQ_ERROR_OPENSSL;
950
- }
951
- cursor = pem;
952
- memcpy(cursor, header, (size_t)header_len);
953
- cursor += header_len;
954
- for (size_t off = 0; off < encoded_len; off += 64) {
955
- size_t chunk = encoded_len - off;
956
- if (chunk > 64)
957
- chunk = 64;
958
- memcpy(cursor, encoded + off, chunk);
959
- cursor += chunk;
960
- *cursor++ = '\n';
961
708
  }
962
- memcpy(cursor, footer, (size_t)footer_len);
963
- cursor += footer_len;
964
- *cursor = '\0';
965
- *output = pem;
966
- *output_len = (size_t)(cursor - pem);
967
- pq_secure_wipe(encoded, encoded_len + 1);
968
- free(encoded);
969
- return PQ_SUCCESS;
709
+ return ret;
970
710
  }
971
711
 
972
712
  static int pq_pem_to_der(const char *label, const char *input, size_t input_len, uint8_t **der_out,
@@ -975,13 +715,13 @@ static int pq_pem_to_der(const char *label, const char *input, size_t input_len,
975
715
  int header_len, footer_len;
976
716
  const char *body_start, *footer_pos;
977
717
  const char *tail;
978
- char *encoded = NULL;
979
718
  uint8_t *der = NULL;
980
- size_t encoded_len = 0;
981
- size_t der_capacity = 0;
982
- int decoded_len;
719
+ size_t body_len = 0;
983
720
  int ret;
984
- size_t pad = 0;
721
+ BIO *bio_b64 = NULL;
722
+ BIO *bio_mem = NULL;
723
+ BIO *bio_chain = NULL;
724
+ int decoded_len = 0;
985
725
 
986
726
  if (!label || !input || !der_out || !der_len_out)
987
727
  return PQ_ERROR_BUFFER;
@@ -1021,63 +761,57 @@ static int pq_pem_to_der(const char *label, const char *input, size_t input_len,
1021
761
  tail++;
1022
762
  }
1023
763
 
1024
- encoded = malloc((size_t)(footer_pos - body_start) + 1);
1025
- if (!encoded)
1026
- return PQ_ERROR_NOMEM;
1027
- for (const char *p = body_start; p < footer_pos; ++p) {
1028
- if (pq_is_pem_whitespace(*p))
1029
- continue;
1030
- encoded[encoded_len++] = *p;
1031
- }
1032
- if (encoded_len == 0 || (encoded_len % 4) != 0) {
1033
- free(encoded);
764
+ body_len = (size_t)(footer_pos - body_start);
765
+ if (body_len > (size_t)INT_MAX)
1034
766
  return PQ_ERROR_BUFFER;
1035
- }
1036
- encoded[encoded_len] = '\0';
1037
767
 
1038
- ret = pq_size_mul(encoded_len / 4, 3, &der_capacity);
1039
- if (ret != PQ_SUCCESS) {
1040
- pq_secure_wipe(encoded, encoded_len + 1);
1041
- free(encoded);
1042
- return ret;
1043
- }
1044
- ret = pq_size_add(der_capacity, 1, &der_capacity);
1045
- if (ret != PQ_SUCCESS || der_capacity == 0) {
1046
- pq_secure_wipe(encoded, encoded_len + 1);
1047
- free(encoded);
1048
- return PQ_ERROR_BUFFER;
1049
- }
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;
1050
773
 
1051
- der = malloc(der_capacity);
1052
- if (!der) {
1053
- pq_secure_wipe(encoded, encoded_len + 1);
1054
- free(encoded);
1055
- return PQ_ERROR_NOMEM;
1056
- }
1057
- decoded_len = EVP_DecodeBlock(der, (unsigned char *)encoded, (int)encoded_len);
1058
- if (decoded_len < 0) {
1059
- pq_secure_wipe(encoded, encoded_len + 1);
1060
- free(encoded);
1061
- pq_secure_wipe(der, der_capacity);
1062
- free(der);
1063
- 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;
1064
800
  }
1065
- if (encoded_len >= 1 && encoded[encoded_len - 1] == '=')
1066
- pad++;
1067
- if (encoded_len >= 2 && encoded[encoded_len - 2] == '=')
1068
- pad++;
1069
- if ((size_t)decoded_len < pad) {
1070
- pq_secure_wipe(encoded, encoded_len + 1);
1071
- free(encoded);
1072
- 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) {
1073
812
  free(der);
1074
- return PQ_ERROR_BUFFER;
1075
813
  }
1076
- *der_len_out = (size_t)decoded_len - pad;
1077
- *der_out = der;
1078
- pq_secure_wipe(encoded, encoded_len + 1);
1079
- free(encoded);
1080
- return PQ_SUCCESS;
814
+ return ret;
1081
815
  }
1082
816
 
1083
817
  int pq_public_key_to_pqc_container_der(uint8_t **output, size_t *output_len,
@@ -1165,5 +899,5 @@ int pq_secret_key_from_pqc_container_pem(char **algorithm_out, uint8_t **key_out
1165
899
  }
1166
900
 
1167
901
  const char *pq_version(void) {
1168
- return "0.2.0";
902
+ return "0.3.0";
1169
903
  }