pq_crypto 0.2.0 → 0.3.1

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;
@@ -128,6 +99,32 @@ cleanup:
128
99
  return ret;
129
100
  }
130
101
 
102
+ static int x25519_public_from_private(uint8_t *pk, const uint8_t *sk) {
103
+ EVP_PKEY *pkey = NULL;
104
+ size_t pklen = X25519_PUBLICKEYBYTES;
105
+ int ret = PQ_ERROR_KEYPAIR;
106
+
107
+ if (!pk || !sk) {
108
+ return PQ_ERROR_BUFFER;
109
+ }
110
+
111
+ pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_X25519, NULL, sk, X25519_SECRETKEYBYTES);
112
+ if (!pkey)
113
+ goto cleanup;
114
+
115
+ if (EVP_PKEY_get_raw_public_key(pkey, pk, &pklen) <= 0)
116
+ goto cleanup;
117
+ if (pklen != X25519_PUBLICKEYBYTES)
118
+ goto cleanup;
119
+
120
+ ret = PQ_SUCCESS;
121
+
122
+ cleanup:
123
+ if (pkey)
124
+ EVP_PKEY_free(pkey);
125
+ return ret;
126
+ }
127
+
131
128
  static int x25519_shared_secret(uint8_t *shared, const uint8_t *their_pk, const uint8_t *my_sk) {
132
129
  EVP_PKEY_CTX *ctx = NULL;
133
130
  EVP_PKEY *pkey = NULL;
@@ -171,201 +168,116 @@ cleanup:
171
168
  return ret;
172
169
  }
173
170
 
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;
171
+ static const uint8_t XWING_LABEL[6] = {0x5c, 0x2e, 0x2f, 0x2f, 0x5e, 0x5c};
178
172
 
179
- pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_X25519, NULL, sk, X25519_SECRETKEYBYTES);
180
- if (!pkey) {
181
- return ret;
182
- }
173
+ static int xwing_combiner(uint8_t shared_secret[HYBRID_SHAREDSECRETBYTES],
174
+ const uint8_t ss_M[MLKEM_SHAREDSECRETBYTES],
175
+ const uint8_t ss_X[X25519_SHAREDSECRETBYTES],
176
+ const uint8_t ct_X[X25519_PUBLICKEYBYTES],
177
+ const uint8_t pk_X[X25519_PUBLICKEYBYTES]) {
178
+ EVP_MD_CTX *ctx = EVP_MD_CTX_new();
179
+ unsigned int out_len = 0;
180
+ int ret = PQ_ERROR_OPENSSL;
183
181
 
184
- if (EVP_PKEY_get_raw_public_key(pkey, pk, &pklen) <= 0 || pklen != X25519_PUBLICKEYBYTES) {
185
- EVP_PKEY_free(pkey);
186
- return ret;
182
+ if (!ctx) {
183
+ return PQ_ERROR_OPENSSL;
187
184
  }
188
185
 
189
- EVP_PKEY_free(pkey);
190
- return PQ_SUCCESS;
191
- }
186
+ if (EVP_DigestInit_ex(ctx, EVP_sha3_256(), NULL) != 1)
187
+ goto cleanup;
188
+ if (EVP_DigestUpdate(ctx, ss_M, MLKEM_SHAREDSECRETBYTES) != 1)
189
+ goto cleanup;
190
+ if (EVP_DigestUpdate(ctx, ss_X, X25519_SHAREDSECRETBYTES) != 1)
191
+ goto cleanup;
192
+ if (EVP_DigestUpdate(ctx, ct_X, X25519_PUBLICKEYBYTES) != 1)
193
+ goto cleanup;
194
+ if (EVP_DigestUpdate(ctx, pk_X, X25519_PUBLICKEYBYTES) != 1)
195
+ goto cleanup;
196
+ if (EVP_DigestUpdate(ctx, XWING_LABEL, sizeof(XWING_LABEL)) != 1)
197
+ goto cleanup;
198
+ if (EVP_DigestFinal_ex(ctx, shared_secret, &out_len) != 1)
199
+ goto cleanup;
200
+ if (out_len != HYBRID_SHAREDSECRETBYTES)
201
+ goto cleanup;
192
202
 
193
- static int size_t_to_int_checked(size_t value, int *out) {
194
- if (value > INT_MAX) {
195
- return PQ_ERROR_BUFFER;
196
- }
203
+ ret = PQ_SUCCESS;
197
204
 
198
- *out = (int)value;
199
- return PQ_SUCCESS;
205
+ cleanup:
206
+ EVP_MD_CTX_free(ctx);
207
+ return ret;
200
208
  }
201
209
 
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;
210
+ static int xwing_expand_secret_key(hybrid_expanded_secret_key_t *expanded_key,
211
+ const uint8_t seed[HYBRID_SECRETKEYBYTES]) {
212
+ EVP_MD_CTX *ctx = NULL;
213
+ uint8_t expanded[XWING_EXPANDEDBYTES];
214
+ int ret = PQ_ERROR_OPENSSL;
207
215
 
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)
216
+ if (!expanded_key || !seed) {
211
217
  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
218
  }
223
219
 
224
- if (EVP_PKEY_CTX_set_hkdf_md(pctx, EVP_sha256()) <= 0) {
225
- EVP_PKEY_CTX_free(pctx);
226
- return PQ_ERROR_OPENSSL;
227
- }
220
+ memset(expanded_key, 0, sizeof(*expanded_key));
221
+ memset(expanded, 0, sizeof(expanded));
228
222
 
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
- }
223
+ ctx = EVP_MD_CTX_new();
224
+ if (!ctx)
225
+ goto cleanup;
233
226
 
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
- }
227
+ if (EVP_DigestInit_ex(ctx, EVP_shake256(), NULL) != 1)
228
+ goto cleanup;
229
+ if (EVP_DigestUpdate(ctx, seed, HYBRID_SECRETKEYBYTES) != 1)
230
+ goto cleanup;
231
+ if (EVP_DigestFinalXOF(ctx, expanded, sizeof(expanded)) != 1)
232
+ goto cleanup;
240
233
 
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
- }
234
+ ret = PQCLEAN_MLKEM768_CLEAN_crypto_kem_keypair_derand(expanded_key->mlkem_pk,
235
+ expanded_key->mlkem_sk, expanded);
236
+ if (ret != 0) {
237
+ ret = PQ_ERROR_KEYPAIR;
238
+ goto cleanup;
246
239
  }
247
240
 
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;
241
+ memcpy(expanded_key->x25519_sk, expanded + 64, X25519_SECRETKEYBYTES);
242
+ ret = x25519_public_from_private(expanded_key->x25519_pk, expanded_key->x25519_sk);
243
+ if (ret != PQ_SUCCESS) {
244
+ goto cleanup;
252
245
  }
253
246
 
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) {
259
- EVP_MD_CTX *ctx = EVP_MD_CTX_new();
260
- unsigned int out_len = 0;
261
-
262
- if (!ctx) {
263
- return PQ_ERROR_OPENSSL;
264
- }
247
+ ret = PQ_SUCCESS;
265
248
 
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) {
249
+ cleanup:
250
+ if (ctx)
269
251
  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);
297
-
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);
252
+ pq_secure_wipe(expanded, sizeof(expanded));
253
+ if (ret != PQ_SUCCESS && expanded_key) {
254
+ pq_secure_wipe(expanded_key, sizeof(*expanded_key));
302
255
  }
303
-
304
- pq_secure_wipe(ikm, sizeof(ikm));
305
- pq_secure_wipe(salt, sizeof(salt));
306
- pq_secure_wipe(transcript, transcript_len);
307
- free(transcript);
308
256
  return ret;
309
257
  }
310
258
 
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);
321
- }
322
-
323
- static int mldsa_keypair(uint8_t *pk, uint8_t *sk) {
324
- return PQCLEAN_MLDSA65_CLEAN_crypto_sign_keypair(pk, sk);
259
+ int pq_mlkem_keypair(uint8_t *pk, uint8_t *sk) {
260
+ return PQCLEAN_MLKEM768_CLEAN_crypto_kem_keypair(pk, sk) == 0 ? PQ_SUCCESS : PQ_ERROR_KEYPAIR;
325
261
  }
326
262
 
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);
263
+ int pq_mlkem_encapsulate(uint8_t *ct, uint8_t *ss, const uint8_t *pk) {
264
+ return PQCLEAN_MLKEM768_CLEAN_crypto_kem_enc(ct, ss, pk) == 0 ? PQ_SUCCESS
265
+ : PQ_ERROR_ENCAPSULATE;
330
266
  }
331
267
 
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);
268
+ int pq_mlkem_decapsulate(uint8_t *ss, const uint8_t *ct, const uint8_t *sk) {
269
+ return PQCLEAN_MLKEM768_CLEAN_crypto_kem_dec(ss, ct, sk) == 0 ? PQ_SUCCESS
270
+ : PQ_ERROR_DECAPSULATE;
335
271
  }
336
272
 
337
273
  int pq_testing_mlkem_keypair_from_seed(uint8_t *public_key, uint8_t *secret_key,
338
274
  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) {
275
+ if (!public_key || !secret_key || !seed || seed_len != 64) {
343
276
  return PQ_ERROR_BUFFER;
344
277
  }
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;
278
+ return PQCLEAN_MLKEM768_CLEAN_crypto_kem_keypair_derand(public_key, secret_key, seed) == 0
279
+ ? PQ_SUCCESS
280
+ : PQ_ERROR_KEYPAIR;
369
281
  }
370
282
 
371
283
  int pq_testing_mlkem_encapsulate_from_seed(uint8_t *ciphertext, uint8_t *shared_secret,
@@ -374,258 +286,148 @@ int pq_testing_mlkem_encapsulate_from_seed(uint8_t *ciphertext, uint8_t *shared_
374
286
  if (!ciphertext || !shared_secret || !public_key || !seed || seed_len != 32) {
375
287
  return PQ_ERROR_BUFFER;
376
288
  }
377
-
378
289
  return PQCLEAN_MLKEM768_CLEAN_crypto_kem_enc_derand(ciphertext, shared_secret, public_key,
379
290
  seed) == 0
380
291
  ? PQ_SUCCESS
381
292
  : PQ_ERROR_ENCAPSULATE;
382
293
  }
383
294
 
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;
295
+ int pq_sign_keypair(uint8_t *public_key, uint8_t *secret_key) {
296
+ return PQCLEAN_MLDSA65_CLEAN_crypto_sign_keypair(public_key, secret_key) == 0
297
+ ? PQ_SUCCESS
298
+ : PQ_ERROR_KEYPAIR;
428
299
  }
429
300
 
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
- }
301
+ int pq_sign(uint8_t *signature, size_t *signature_len, const uint8_t *message, size_t message_len,
302
+ const uint8_t *secret_key) {
303
+ return PQCLEAN_MLDSA65_CLEAN_crypto_sign_signature(signature, signature_len, message,
304
+ message_len, secret_key) == 0
305
+ ? PQ_SUCCESS
306
+ : PQ_ERROR_SIGN;
307
+ }
519
308
 
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;
309
+ int pq_verify(const uint8_t *signature, size_t signature_len, const uint8_t *message,
310
+ size_t message_len, const uint8_t *public_key) {
311
+ return PQCLEAN_MLDSA65_CLEAN_crypto_sign_verify(signature, signature_len, message, message_len,
312
+ public_key) == 0
313
+ ? PQ_SUCCESS
314
+ : PQ_ERROR_VERIFY;
535
315
  }
536
316
 
537
317
  int pq_testing_mldsa_keypair_from_seed(uint8_t *public_key, uint8_t *secret_key,
538
318
  const uint8_t *seed, size_t seed_len) {
539
- if (!public_key || !secret_key || !seed || seed_len != SEEDBYTES) {
319
+ int rc;
320
+ if (!public_key || !secret_key || !seed) {
540
321
  return PQ_ERROR_BUFFER;
541
322
  }
542
323
 
543
- return pq_testing_mldsa_keypair_from_seed_impl(public_key, secret_key, seed);
324
+ if (seed_len != 32) {
325
+ return PQ_ERROR_BUFFER;
326
+ }
327
+
328
+ pq_testing_set_seed(seed, seed_len);
329
+ rc = PQCLEAN_MLDSA65_CLEAN_crypto_sign_keypair(public_key, secret_key);
330
+ pq_testing_clear_seed();
331
+ return rc == 0 ? PQ_SUCCESS : PQ_ERROR_KEYPAIR;
544
332
  }
545
333
 
546
334
  int pq_testing_mldsa_sign_from_seed(uint8_t *signature, size_t *signature_len,
547
335
  const uint8_t *message, size_t message_len,
548
336
  const uint8_t *secret_key, const uint8_t *seed,
549
337
  size_t seed_len) {
550
- if (!signature || !signature_len || !message || !secret_key || !seed || seed_len != RNDBYTES) {
338
+ int rc;
339
+ if (!signature || !signature_len || !secret_key || !seed) {
551
340
  return PQ_ERROR_BUFFER;
552
341
  }
553
342
 
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
- }
343
+ if (seed_len != 32) {
344
+ return PQ_ERROR_BUFFER;
345
+ }
566
346
 
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;
347
+ pq_testing_set_seed(seed, seed_len);
348
+ rc = PQCLEAN_MLDSA65_CLEAN_crypto_sign_signature(signature, signature_len, message, message_len,
349
+ secret_key);
350
+ pq_testing_clear_seed();
351
+ return rc == 0 ? PQ_SUCCESS : PQ_ERROR_SIGN;
571
352
  }
572
353
 
573
354
  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;
355
+ hybrid_public_key_t pk;
356
+ hybrid_expanded_secret_key_t expanded;
357
+ uint8_t seed[HYBRID_SECRETKEYBYTES];
358
+ int ret = PQ_SUCCESS;
576
359
 
577
- memset(sk, 0, sizeof(*sk));
360
+ if (!public_key || !secret_key) {
361
+ return PQ_ERROR_BUFFER;
362
+ }
363
+
364
+ memset(&pk, 0, sizeof(pk));
365
+ memset(&expanded, 0, sizeof(expanded));
366
+ memset(seed, 0, sizeof(seed));
578
367
 
579
- if (mlkem_keypair(pk->mlkem_pk, sk->mlkem_sk) != 0) {
580
- pq_secure_wipe(sk, sizeof(*sk));
581
- return PQ_ERROR_KEYPAIR;
368
+ if (RAND_bytes(seed, sizeof(seed)) != 1) {
369
+ ret = PQ_ERROR_RANDOM;
370
+ goto cleanup;
582
371
  }
583
372
 
584
- if (x25519_keypair(pk->x25519_pk, sk->x25519_sk) != 0) {
585
- pq_secure_wipe(sk, sizeof(*sk));
586
- return PQ_ERROR_KEYPAIR;
373
+ ret = xwing_expand_secret_key(&expanded, seed);
374
+ if (ret != PQ_SUCCESS) {
375
+ goto cleanup;
587
376
  }
588
377
 
589
- return PQ_SUCCESS;
378
+ memcpy(pk.mlkem_pk, expanded.mlkem_pk, MLKEM_PUBLICKEYBYTES);
379
+ memcpy(pk.x25519_pk, expanded.x25519_pk, X25519_PUBLICKEYBYTES);
380
+ memcpy(public_key, &pk, HYBRID_PUBLICKEYBYTES);
381
+ memcpy(secret_key, seed, HYBRID_SECRETKEYBYTES);
382
+
383
+ cleanup:
384
+ pq_secure_wipe(seed, sizeof(seed));
385
+ pq_secure_wipe(&expanded, sizeof(expanded));
386
+ return ret;
590
387
  }
591
388
 
592
389
  int pq_hybrid_kem_encapsulate(uint8_t *ciphertext, uint8_t *shared_secret,
593
390
  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
-
391
+ hybrid_public_key_t pk;
392
+ hybrid_ciphertext_t ct;
597
393
  uint8_t mlkem_ss[MLKEM_SHAREDSECRETBYTES];
598
394
  uint8_t x25519_ss[X25519_SHAREDSECRETBYTES];
599
395
  uint8_t x25519_ephemeral_sk[X25519_SECRETKEYBYTES];
600
396
  int ret = PQ_SUCCESS;
601
397
 
398
+ if (!ciphertext || !shared_secret || !public_key) {
399
+ return PQ_ERROR_BUFFER;
400
+ }
401
+
402
+ memcpy(&pk, public_key, HYBRID_PUBLICKEYBYTES);
403
+ memset(&ct, 0, sizeof(ct));
602
404
  memset(mlkem_ss, 0, sizeof(mlkem_ss));
603
405
  memset(x25519_ss, 0, sizeof(x25519_ss));
604
406
  memset(x25519_ephemeral_sk, 0, sizeof(x25519_ephemeral_sk));
605
407
 
606
- if (mlkem_encapsulate(ct->mlkem_ct, mlkem_ss, pk->mlkem_pk) != 0) {
408
+ if (PQCLEAN_MLKEM768_CLEAN_crypto_kem_enc(ct.mlkem_ct, mlkem_ss, pk.mlkem_pk) != 0) {
607
409
  ret = PQ_ERROR_ENCAPSULATE;
608
410
  goto cleanup;
609
411
  }
610
412
 
611
- ret = x25519_keypair(ct->x25519_ephemeral, x25519_ephemeral_sk);
413
+ ret = x25519_keypair(ct.x25519_ephemeral, x25519_ephemeral_sk);
612
414
  if (ret != PQ_SUCCESS) {
613
415
  ret = PQ_ERROR_ENCAPSULATE;
614
416
  goto cleanup;
615
417
  }
616
418
 
617
- ret = x25519_shared_secret(x25519_ss, pk->x25519_pk, x25519_ephemeral_sk);
419
+ ret = x25519_shared_secret(x25519_ss, pk.x25519_pk, x25519_ephemeral_sk);
618
420
  if (ret != PQ_SUCCESS) {
619
421
  ret = PQ_ERROR_ENCAPSULATE;
620
422
  goto cleanup;
621
423
  }
622
424
 
623
- ret = hybrid_combiner(shared_secret, mlkem_ss, x25519_ss, pk->x25519_pk, ct);
425
+ ret = xwing_combiner(shared_secret, mlkem_ss, x25519_ss, ct.x25519_ephemeral, pk.x25519_pk);
624
426
  if (ret != PQ_SUCCESS) {
625
427
  goto cleanup;
626
428
  }
627
429
 
628
- ret = PQ_SUCCESS;
430
+ memcpy(ciphertext, &ct, HYBRID_CIPHERTEXTBYTES);
629
431
 
630
432
  cleanup:
631
433
  pq_secure_wipe(mlkem_ss, sizeof(mlkem_ss));
@@ -636,63 +438,48 @@ cleanup:
636
438
 
637
439
  int pq_hybrid_kem_decapsulate(uint8_t *shared_secret, const uint8_t *ciphertext,
638
440
  const uint8_t *secret_key) {
639
- const hybrid_ciphertext_t *ct = (const hybrid_ciphertext_t *)ciphertext;
640
- uint8_t recipient_x25519_pk[X25519_PUBLICKEYBYTES];
641
- const hybrid_secret_key_t *sk = (const hybrid_secret_key_t *)secret_key;
642
-
441
+ hybrid_ciphertext_t ct;
442
+ hybrid_expanded_secret_key_t expanded;
643
443
  uint8_t mlkem_ss[MLKEM_SHAREDSECRETBYTES];
644
444
  uint8_t x25519_ss[X25519_SHAREDSECRETBYTES];
645
445
  int ret = PQ_SUCCESS;
646
446
 
647
- memset(recipient_x25519_pk, 0, sizeof(recipient_x25519_pk));
447
+ if (!shared_secret || !ciphertext || !secret_key) {
448
+ return PQ_ERROR_BUFFER;
449
+ }
450
+
451
+ memcpy(&ct, ciphertext, HYBRID_CIPHERTEXTBYTES);
452
+ memset(&expanded, 0, sizeof(expanded));
648
453
  memset(mlkem_ss, 0, sizeof(mlkem_ss));
649
454
  memset(x25519_ss, 0, sizeof(x25519_ss));
650
455
 
651
- ret = mlkem_decapsulate(mlkem_ss, ct->mlkem_ct, sk->mlkem_sk) == 0 ? PQ_SUCCESS
652
- : PQ_ERROR_DECAPSULATE;
456
+ ret = xwing_expand_secret_key(&expanded, secret_key);
653
457
  if (ret != PQ_SUCCESS) {
458
+ ret = PQ_ERROR_DECAPSULATE;
654
459
  goto cleanup;
655
460
  }
656
461
 
657
- ret = x25519_shared_secret(x25519_ss, ct->x25519_ephemeral, sk->x25519_sk);
658
- if (ret != PQ_SUCCESS) {
462
+ if (PQCLEAN_MLKEM768_CLEAN_crypto_kem_dec(mlkem_ss, ct.mlkem_ct, expanded.mlkem_sk) != 0) {
659
463
  ret = PQ_ERROR_DECAPSULATE;
660
464
  goto cleanup;
661
465
  }
662
466
 
663
- ret = x25519_public_from_secret(recipient_x25519_pk, sk->x25519_sk);
467
+ ret = x25519_shared_secret(x25519_ss, ct.x25519_ephemeral, expanded.x25519_sk);
664
468
  if (ret != PQ_SUCCESS) {
665
469
  ret = PQ_ERROR_DECAPSULATE;
666
470
  goto cleanup;
667
471
  }
668
472
 
669
- ret = hybrid_combiner(shared_secret, mlkem_ss, x25519_ss, recipient_x25519_pk, ct);
473
+ ret =
474
+ xwing_combiner(shared_secret, mlkem_ss, x25519_ss, ct.x25519_ephemeral, expanded.x25519_pk);
670
475
 
671
476
  cleanup:
672
- pq_secure_wipe(recipient_x25519_pk, sizeof(recipient_x25519_pk));
673
477
  pq_secure_wipe(mlkem_ss, sizeof(mlkem_ss));
674
478
  pq_secure_wipe(x25519_ss, sizeof(x25519_ss));
479
+ pq_secure_wipe(&expanded, sizeof(expanded));
675
480
  return ret;
676
481
  }
677
482
 
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
483
  #define PQC_SERIALIZATION_MAGIC_0 'P'
697
484
  #define PQC_SERIALIZATION_MAGIC_1 'Q'
698
485
  #define PQC_SERIALIZATION_MAGIC_2 'C'
@@ -702,8 +489,8 @@ int pq_verify(const uint8_t *signature, size_t signature_len, const uint8_t *mes
702
489
  #define PQC_SERIALIZATION_TYPE_SECRET 0x02
703
490
 
704
491
  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";
492
+
493
+ static const char PQC_OID_ML_KEM_768_X25519_XWING[] = "1.3.6.1.4.1.62253.25722";
707
494
  static const char PQC_OID_ML_DSA_65[] = "2.25.305232938483772195555080795650659207792";
708
495
  static const char PQC_PUBLIC_KEY_PEM_LABEL[] = "PQC PUBLIC KEY CONTAINER";
709
496
  static const char PQC_PRIVATE_KEY_PEM_LABEL[] = "PQC PRIVATE KEY CONTAINER";
@@ -717,8 +504,8 @@ typedef struct {
717
504
 
718
505
  static const pq_serialization_algorithm_t PQC_SERIALIZATION_ALGORITHMS[] = {
719
506
  {"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},
507
+ {"ml_kem_768_x25519_xwing", PQC_OID_ML_KEM_768_X25519_XWING, PQ_HYBRID_PUBLICKEYBYTES,
508
+ PQ_HYBRID_SECRETKEYBYTES},
722
509
  {"ml_dsa_65", PQC_OID_ML_DSA_65, MLDSA_PUBLICKEYBYTES, MLDSA_SECRETKEYBYTES},
723
510
  };
724
511
 
@@ -895,17 +682,17 @@ static int pq_decode_serialized_key(const uint8_t *input, size_t input_len, uint
895
682
 
896
683
  static int pq_der_to_pem(const char *label, const uint8_t *der, size_t der_len, char **output,
897
684
  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;
685
+ BIO *bio_mem = NULL;
686
+ BIO *bio_b64 = NULL;
687
+ BIO *bio_chain = NULL;
688
+ BUF_MEM *bptr = NULL;
904
689
  char header[64];
905
690
  char footer[64];
906
- char *cursor;
907
691
  int header_len, footer_len;
908
- int ret;
692
+ int ret = PQ_ERROR_OPENSSL;
693
+ char *pem = NULL;
694
+ size_t total_len = 0;
695
+ size_t needed = 0;
909
696
 
910
697
  if (!label || !der || !output || !output_len)
911
698
  return PQ_ERROR_BUFFER;
@@ -918,55 +705,68 @@ static int pq_der_to_pem(const char *label, const uint8_t *der, size_t der_len,
918
705
  if (der_len > (size_t)INT_MAX)
919
706
  return PQ_ERROR_BUFFER;
920
707
 
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;
708
+ bio_b64 = BIO_new(BIO_f_base64());
709
+ bio_mem = BIO_new(BIO_s_mem());
710
+ if (!bio_b64 || !bio_mem) {
711
+ ret = PQ_ERROR_NOMEM;
712
+ goto cleanup;
713
+ }
937
714
 
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;
715
+ bio_chain = BIO_push(bio_b64, bio_mem);
716
+
717
+ if (BIO_write(bio_chain, der, (int)der_len) != (int)der_len)
718
+ goto cleanup;
719
+ if (BIO_flush(bio_chain) != 1)
720
+ goto cleanup;
721
+ BIO_get_mem_ptr(bio_chain, &bptr);
722
+ if (!bptr || !bptr->data)
723
+ goto cleanup;
724
+
725
+ {
726
+ size_t body_len = bptr->length;
727
+ needed = (size_t)header_len + 1 + body_len;
728
+ if (body_len == 0 || bptr->data[body_len - 1] != '\n')
729
+ needed += 1;
730
+ needed += (size_t)footer_len + 1;
731
+
732
+ pem = malloc(needed);
733
+ if (!pem) {
734
+ ret = PQ_ERROR_NOMEM;
735
+ goto cleanup;
736
+ }
737
+ char *cur = pem;
738
+ memcpy(cur, header, (size_t)header_len);
739
+ cur += header_len;
740
+ *cur++ = '\n';
741
+ memcpy(cur, bptr->data, body_len);
742
+ cur += body_len;
743
+ if (body_len == 0 || bptr->data[body_len - 1] != '\n')
744
+ *cur++ = '\n';
745
+ memcpy(cur, footer, (size_t)footer_len);
746
+ cur += footer_len;
747
+ *cur = '\0';
748
+ total_len = (size_t)(cur - pem);
944
749
  }
945
- if (EVP_EncodeBlock(encoded, der, (int)der_len) != (int)encoded_len) {
946
- free(encoded);
947
- pq_secure_wipe(pem, total_len + 1);
750
+
751
+ *output = pem;
752
+ *output_len = total_len;
753
+ pem = NULL;
754
+ ret = PQ_SUCCESS;
755
+
756
+ cleanup:
757
+ if (bio_chain) {
758
+ BIO_free_all(bio_chain);
759
+ } else {
760
+ if (bio_b64)
761
+ BIO_free(bio_b64);
762
+ if (bio_mem)
763
+ BIO_free(bio_mem);
764
+ }
765
+ if (pem) {
766
+ pq_secure_wipe(pem, needed);
948
767
  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
768
  }
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;
769
+ return ret;
970
770
  }
971
771
 
972
772
  static int pq_pem_to_der(const char *label, const char *input, size_t input_len, uint8_t **der_out,
@@ -975,13 +775,13 @@ static int pq_pem_to_der(const char *label, const char *input, size_t input_len,
975
775
  int header_len, footer_len;
976
776
  const char *body_start, *footer_pos;
977
777
  const char *tail;
978
- char *encoded = NULL;
979
778
  uint8_t *der = NULL;
980
- size_t encoded_len = 0;
981
- size_t der_capacity = 0;
982
- int decoded_len;
779
+ size_t body_len = 0;
983
780
  int ret;
984
- size_t pad = 0;
781
+ BIO *bio_b64 = NULL;
782
+ BIO *bio_mem = NULL;
783
+ BIO *bio_chain = NULL;
784
+ int decoded_len = 0;
985
785
 
986
786
  if (!label || !input || !der_out || !der_len_out)
987
787
  return PQ_ERROR_BUFFER;
@@ -1021,63 +821,57 @@ static int pq_pem_to_der(const char *label, const char *input, size_t input_len,
1021
821
  tail++;
1022
822
  }
1023
823
 
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);
824
+ body_len = (size_t)(footer_pos - body_start);
825
+ if (body_len > (size_t)INT_MAX)
1034
826
  return PQ_ERROR_BUFFER;
1035
- }
1036
- encoded[encoded_len] = '\0';
1037
827
 
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
- }
828
+ {
829
+ size_t der_cap = (body_len * 3) / 4 + 3;
830
+ der = malloc(der_cap ? der_cap : 1);
831
+ if (!der)
832
+ return PQ_ERROR_NOMEM;
1050
833
 
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;
834
+ bio_mem = BIO_new_mem_buf(body_start, (int)body_len);
835
+ bio_b64 = BIO_new(BIO_f_base64());
836
+ if (!bio_mem || !bio_b64) {
837
+ ret = PQ_ERROR_NOMEM;
838
+ goto cleanup;
839
+ }
840
+ bio_chain = BIO_push(bio_b64, bio_mem);
841
+
842
+ decoded_len = BIO_read(bio_chain, der, (int)der_cap);
843
+ if (decoded_len <= 0) {
844
+ ret = PQ_ERROR_BUFFER;
845
+ goto cleanup;
846
+ }
847
+ {
848
+ unsigned char tail_byte;
849
+ int extra = BIO_read(bio_chain, &tail_byte, 1);
850
+ if (extra > 0) {
851
+ ret = PQ_ERROR_BUFFER;
852
+ goto cleanup;
853
+ }
854
+ }
855
+
856
+ *der_len_out = (size_t)decoded_len;
857
+ *der_out = der;
858
+ der = NULL;
859
+ ret = PQ_SUCCESS;
1064
860
  }
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);
861
+
862
+ cleanup:
863
+ if (bio_chain) {
864
+ BIO_free_all(bio_chain);
865
+ } else {
866
+ if (bio_b64)
867
+ BIO_free(bio_b64);
868
+ if (bio_mem)
869
+ BIO_free(bio_mem);
870
+ }
871
+ if (der) {
1073
872
  free(der);
1074
- return PQ_ERROR_BUFFER;
1075
873
  }
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;
874
+ return ret;
1081
875
  }
1082
876
 
1083
877
  int pq_public_key_to_pqc_container_der(uint8_t **output, size_t *output_len,
@@ -1165,5 +959,5 @@ int pq_secret_key_from_pqc_container_pem(char **algorithm_out, uint8_t **key_out
1165
959
  }
1166
960
 
1167
961
  const char *pq_version(void) {
1168
- return "0.2.0";
962
+ return "0.3.0";
1169
963
  }