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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +88 -0
- data/GET_STARTED.md +16 -9
- data/README.md +82 -26
- data/SECURITY.md +72 -13
- data/ext/pqcrypto/extconf.rb +16 -11
- data/ext/pqcrypto/pq_randombytes.c +56 -0
- data/ext/pqcrypto/pqcrypto_ruby_secure.c +31 -20
- data/ext/pqcrypto/pqcrypto_secure.c +269 -535
- data/ext/pqcrypto/pqcrypto_secure.h +13 -2
- data/lib/pq_crypto/errors.rb +12 -6
- data/lib/pq_crypto/hybrid_kem.rb +2 -2
- data/lib/pq_crypto/kem.rb +16 -4
- data/lib/pq_crypto/serialization.rb +2 -2
- data/lib/pq_crypto/signature.rb +26 -18
- data/lib/pq_crypto/version.rb +1 -1
- data/lib/pq_crypto.rb +42 -73
- data/script/vendor_libs.rb +0 -1
- metadata +3 -5
- data/ext/pqcrypto/vendor/pqclean/common/keccak4x/Makefile +0 -8
- data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/Makefile +0 -19
- data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-65/clean/Makefile +0 -19
|
@@ -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/
|
|
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
|
|
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
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
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,
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
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 =
|
|
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
|
-
|
|
305
|
-
|
|
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
|
-
|
|
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
|
-
|
|
324
|
-
return
|
|
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
|
-
|
|
328
|
-
|
|
329
|
-
|
|
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
|
-
|
|
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
|
-
|
|
347
|
-
|
|
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
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
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
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
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
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
263
|
+
int rc;
|
|
264
|
+
if (!signature || !signature_len || !secret_key || !seed) {
|
|
551
265
|
return PQ_ERROR_BUFFER;
|
|
552
266
|
}
|
|
553
267
|
|
|
554
|
-
|
|
555
|
-
|
|
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
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
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
|
|
575
|
-
hybrid_secret_key_t
|
|
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(
|
|
288
|
+
memset(&pk, 0, sizeof(pk));
|
|
289
|
+
memset(&sk, 0, sizeof(sk));
|
|
578
290
|
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
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
|
-
|
|
585
|
-
|
|
586
|
-
|
|
298
|
+
ret = x25519_keypair(pk.x25519_pk, sk.x25519_sk);
|
|
299
|
+
if (ret != PQ_SUCCESS) {
|
|
300
|
+
goto cleanup;
|
|
587
301
|
}
|
|
588
302
|
|
|
589
|
-
|
|
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
|
-
|
|
595
|
-
hybrid_ciphertext_t
|
|
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 (
|
|
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
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
652
|
-
|
|
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
|
|
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
|
-
|
|
664
|
-
if (
|
|
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 =
|
|
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
|
-
|
|
706
|
-
|
|
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
|
-
{"
|
|
721
|
-
|
|
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
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
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
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
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
|
-
|
|
939
|
-
|
|
940
|
-
if (
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
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
|
-
|
|
946
|
-
|
|
947
|
-
|
|
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
|
-
|
|
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
|
|
981
|
-
size_t der_capacity = 0;
|
|
982
|
-
int decoded_len;
|
|
719
|
+
size_t body_len = 0;
|
|
983
720
|
int ret;
|
|
984
|
-
|
|
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
|
-
|
|
1025
|
-
if (
|
|
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
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
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
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
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
|
-
|
|
1066
|
-
|
|
1067
|
-
if (
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
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
|
-
|
|
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.
|
|
902
|
+
return "0.3.0";
|
|
1169
903
|
}
|