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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +100 -0
- data/GET_STARTED.md +19 -9
- data/README.md +90 -26
- data/SECURITY.md +84 -13
- data/ext/pqcrypto/extconf.rb +27 -12
- data/ext/pqcrypto/pq_randombytes.c +56 -0
- data/ext/pqcrypto/pqcrypto_ruby_secure.c +35 -20
- data/ext/pqcrypto/pqcrypto_secure.c +319 -525
- data/ext/pqcrypto/pqcrypto_secure.h +23 -4
- data/lib/pq_crypto/errors.rb +12 -6
- data/lib/pq_crypto/hybrid_kem.rb +2 -2
- data/lib/pq_crypto/kem.rb +20 -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 +5 -7
- 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;
|
|
@@ -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
|
|
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
|
-
|
|
180
|
-
|
|
181
|
-
|
|
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 (
|
|
185
|
-
|
|
186
|
-
return ret;
|
|
182
|
+
if (!ctx) {
|
|
183
|
+
return PQ_ERROR_OPENSSL;
|
|
187
184
|
}
|
|
188
185
|
|
|
189
|
-
|
|
190
|
-
|
|
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
|
-
|
|
194
|
-
if (value > INT_MAX) {
|
|
195
|
-
return PQ_ERROR_BUFFER;
|
|
196
|
-
}
|
|
203
|
+
ret = PQ_SUCCESS;
|
|
197
204
|
|
|
198
|
-
|
|
199
|
-
|
|
205
|
+
cleanup:
|
|
206
|
+
EVP_MD_CTX_free(ctx);
|
|
207
|
+
return ret;
|
|
200
208
|
}
|
|
201
209
|
|
|
202
|
-
static int
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
int
|
|
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 (
|
|
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
|
-
|
|
225
|
-
|
|
226
|
-
return PQ_ERROR_OPENSSL;
|
|
227
|
-
}
|
|
220
|
+
memset(expanded_key, 0, sizeof(*expanded_key));
|
|
221
|
+
memset(expanded, 0, sizeof(expanded));
|
|
228
222
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
}
|
|
223
|
+
ctx = EVP_MD_CTX_new();
|
|
224
|
+
if (!ctx)
|
|
225
|
+
goto cleanup;
|
|
233
226
|
|
|
234
|
-
if (
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
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
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
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
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
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
|
-
|
|
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
|
-
|
|
267
|
-
|
|
268
|
-
EVP_DigestFinal_ex(ctx, output, &out_len) != 1) {
|
|
249
|
+
cleanup:
|
|
250
|
+
if (ctx)
|
|
269
251
|
EVP_MD_CTX_free(ctx);
|
|
270
|
-
|
|
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
|
-
|
|
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
|
-
|
|
328
|
-
|
|
329
|
-
|
|
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
|
-
|
|
333
|
-
|
|
334
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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;
|
|
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
|
-
|
|
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;
|
|
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
|
-
|
|
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
|
-
}
|
|
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
|
-
|
|
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;
|
|
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
|
-
|
|
319
|
+
int rc;
|
|
320
|
+
if (!public_key || !secret_key || !seed) {
|
|
540
321
|
return PQ_ERROR_BUFFER;
|
|
541
322
|
}
|
|
542
323
|
|
|
543
|
-
|
|
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
|
-
|
|
338
|
+
int rc;
|
|
339
|
+
if (!signature || !signature_len || !secret_key || !seed) {
|
|
551
340
|
return PQ_ERROR_BUFFER;
|
|
552
341
|
}
|
|
553
342
|
|
|
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
|
-
}
|
|
343
|
+
if (seed_len != 32) {
|
|
344
|
+
return PQ_ERROR_BUFFER;
|
|
345
|
+
}
|
|
566
346
|
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
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
|
|
575
|
-
|
|
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
|
-
|
|
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 (
|
|
580
|
-
|
|
581
|
-
|
|
368
|
+
if (RAND_bytes(seed, sizeof(seed)) != 1) {
|
|
369
|
+
ret = PQ_ERROR_RANDOM;
|
|
370
|
+
goto cleanup;
|
|
582
371
|
}
|
|
583
372
|
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
373
|
+
ret = xwing_expand_secret_key(&expanded, seed);
|
|
374
|
+
if (ret != PQ_SUCCESS) {
|
|
375
|
+
goto cleanup;
|
|
587
376
|
}
|
|
588
377
|
|
|
589
|
-
|
|
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
|
-
|
|
595
|
-
hybrid_ciphertext_t
|
|
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 (
|
|
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
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
640
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
706
|
-
|
|
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
|
-
{"
|
|
721
|
-
|
|
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
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
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
|
-
|
|
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;
|
|
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
|
-
|
|
939
|
-
|
|
940
|
-
if (
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
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
|
-
|
|
946
|
-
|
|
947
|
-
|
|
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
|
-
|
|
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
|
|
981
|
-
size_t der_capacity = 0;
|
|
982
|
-
int decoded_len;
|
|
779
|
+
size_t body_len = 0;
|
|
983
780
|
int ret;
|
|
984
|
-
|
|
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
|
-
|
|
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);
|
|
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
|
-
|
|
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
|
-
}
|
|
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
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
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
|
-
|
|
1066
|
-
|
|
1067
|
-
if (
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
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
|
-
|
|
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.
|
|
962
|
+
return "0.3.0";
|
|
1169
963
|
}
|