pq_crypto 0.5.0 → 0.5.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/.github/workflows/ci.yml +27 -2
- data/CHANGELOG.md +34 -0
- data/ext/pqcrypto/extconf.rb +56 -21
- data/ext/pqcrypto/pqcrypto_native_api.h +3 -0
- data/ext/pqcrypto/pqcrypto_ruby_secure.c +234 -9
- data/ext/pqcrypto/pqcrypto_secure.c +295 -218
- data/ext/pqcrypto/pqcrypto_secure.h +12 -3
- data/ext/pqcrypto/pqcrypto_version.h +1 -1
- data/ext/pqcrypto/vendor/.vendored +4 -2
- data/lib/pq_crypto/hybrid_kem.rb +10 -1
- data/lib/pq_crypto/version.rb +1 -1
- data/lib/pq_crypto.rb +4 -0
- data/script/vendor_libs.rb +218 -73
- metadata +1 -1
|
@@ -15,8 +15,6 @@
|
|
|
15
15
|
#include <openssl/crypto.h>
|
|
16
16
|
#include <openssl/evp.h>
|
|
17
17
|
#include <openssl/rand.h>
|
|
18
|
-
#include <openssl/bio.h>
|
|
19
|
-
#include <openssl/buffer.h>
|
|
20
18
|
|
|
21
19
|
#if OPENSSL_VERSION_NUMBER < 0x30000000L
|
|
22
20
|
#error "OpenSSL 3.0 or later is required for pq_crypto"
|
|
@@ -49,43 +47,6 @@ static int pq_is_pem_whitespace(char c) {
|
|
|
49
47
|
return c == '\n' || c == '\r' || c == ' ' || c == '\t';
|
|
50
48
|
}
|
|
51
49
|
|
|
52
|
-
static int x25519_keypair(uint8_t *pk, uint8_t *sk) {
|
|
53
|
-
EVP_PKEY_CTX *ctx = NULL;
|
|
54
|
-
EVP_PKEY *pkey = NULL;
|
|
55
|
-
size_t pklen = X25519_PUBLICKEYBYTES;
|
|
56
|
-
size_t sklen = X25519_SECRETKEYBYTES;
|
|
57
|
-
int ret = PQ_ERROR_KEYPAIR;
|
|
58
|
-
|
|
59
|
-
ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_X25519, NULL);
|
|
60
|
-
if (!ctx)
|
|
61
|
-
goto cleanup;
|
|
62
|
-
|
|
63
|
-
if (EVP_PKEY_keygen_init(ctx) <= 0)
|
|
64
|
-
goto cleanup;
|
|
65
|
-
|
|
66
|
-
if (EVP_PKEY_keygen(ctx, &pkey) <= 0)
|
|
67
|
-
goto cleanup;
|
|
68
|
-
|
|
69
|
-
if (EVP_PKEY_get_raw_private_key(pkey, sk, &sklen) <= 0)
|
|
70
|
-
goto cleanup;
|
|
71
|
-
if (sklen != X25519_SECRETKEYBYTES)
|
|
72
|
-
goto cleanup;
|
|
73
|
-
|
|
74
|
-
if (EVP_PKEY_get_raw_public_key(pkey, pk, &pklen) <= 0)
|
|
75
|
-
goto cleanup;
|
|
76
|
-
if (pklen != X25519_PUBLICKEYBYTES)
|
|
77
|
-
goto cleanup;
|
|
78
|
-
|
|
79
|
-
ret = PQ_SUCCESS;
|
|
80
|
-
|
|
81
|
-
cleanup:
|
|
82
|
-
if (pkey)
|
|
83
|
-
EVP_PKEY_free(pkey);
|
|
84
|
-
if (ctx)
|
|
85
|
-
EVP_PKEY_CTX_free(ctx);
|
|
86
|
-
return ret;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
50
|
static int x25519_public_from_private(uint8_t *pk, const uint8_t *sk) {
|
|
90
51
|
EVP_PKEY *pkey = NULL;
|
|
91
52
|
size_t pklen = X25519_PUBLICKEYBYTES;
|
|
@@ -112,16 +73,15 @@ cleanup:
|
|
|
112
73
|
return ret;
|
|
113
74
|
}
|
|
114
75
|
|
|
115
|
-
static int
|
|
76
|
+
static int x25519_shared_secret_with_pkey(uint8_t *shared, const uint8_t *their_pk, EVP_PKEY *pkey) {
|
|
116
77
|
EVP_PKEY_CTX *ctx = NULL;
|
|
117
|
-
EVP_PKEY *pkey = NULL;
|
|
118
78
|
EVP_PKEY *peer_key = NULL;
|
|
119
79
|
size_t shared_len = X25519_SHAREDSECRETBYTES;
|
|
120
80
|
int ret = PQ_ERROR_ENCAPSULATE;
|
|
121
81
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
82
|
+
if (!shared || !their_pk || !pkey) {
|
|
83
|
+
return PQ_ERROR_BUFFER;
|
|
84
|
+
}
|
|
125
85
|
|
|
126
86
|
peer_key = EVP_PKEY_new_raw_public_key(EVP_PKEY_X25519, NULL, their_pk, X25519_PUBLICKEYBYTES);
|
|
127
87
|
if (!peer_key)
|
|
@@ -150,53 +110,94 @@ cleanup:
|
|
|
150
110
|
EVP_PKEY_CTX_free(ctx);
|
|
151
111
|
if (peer_key)
|
|
152
112
|
EVP_PKEY_free(peer_key);
|
|
153
|
-
if (pkey)
|
|
154
|
-
EVP_PKEY_free(pkey);
|
|
155
113
|
return ret;
|
|
156
114
|
}
|
|
157
115
|
|
|
158
|
-
static
|
|
116
|
+
static int x25519_shared_secret(uint8_t *shared, const uint8_t *their_pk, const uint8_t *my_sk) {
|
|
117
|
+
EVP_PKEY *pkey = NULL;
|
|
118
|
+
int ret;
|
|
159
119
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
const uint8_t ct_X[X25519_PUBLICKEYBYTES],
|
|
164
|
-
const uint8_t pk_X[X25519_PUBLICKEYBYTES]) {
|
|
165
|
-
EVP_MD_CTX *ctx = EVP_MD_CTX_new();
|
|
166
|
-
unsigned int out_len = 0;
|
|
167
|
-
int ret = PQ_ERROR_OPENSSL;
|
|
120
|
+
if (!shared || !their_pk || !my_sk) {
|
|
121
|
+
return PQ_ERROR_BUFFER;
|
|
122
|
+
}
|
|
168
123
|
|
|
169
|
-
|
|
170
|
-
|
|
124
|
+
pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_X25519, NULL, my_sk, X25519_SECRETKEYBYTES);
|
|
125
|
+
if (!pkey)
|
|
126
|
+
return PQ_ERROR_ENCAPSULATE;
|
|
127
|
+
|
|
128
|
+
ret = x25519_shared_secret_with_pkey(shared, their_pk, pkey);
|
|
129
|
+
EVP_PKEY_free(pkey);
|
|
130
|
+
return ret;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
static int x25519_ephemeral_keypair_and_shared_secret(uint8_t *ephemeral_pk, uint8_t *shared,
|
|
134
|
+
const uint8_t *their_pk) {
|
|
135
|
+
EVP_PKEY_CTX *keygen_ctx = NULL;
|
|
136
|
+
EVP_PKEY *ephemeral_pkey = NULL;
|
|
137
|
+
size_t pklen = X25519_PUBLICKEYBYTES;
|
|
138
|
+
int ret = PQ_ERROR_ENCAPSULATE;
|
|
139
|
+
|
|
140
|
+
if (!ephemeral_pk || !shared || !their_pk) {
|
|
141
|
+
return PQ_ERROR_BUFFER;
|
|
171
142
|
}
|
|
172
143
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
if (EVP_DigestUpdate(ctx, ss_M, MLKEM_SHAREDSECRETBYTES) != 1)
|
|
176
|
-
goto cleanup;
|
|
177
|
-
if (EVP_DigestUpdate(ctx, ss_X, X25519_SHAREDSECRETBYTES) != 1)
|
|
178
|
-
goto cleanup;
|
|
179
|
-
if (EVP_DigestUpdate(ctx, ct_X, X25519_PUBLICKEYBYTES) != 1)
|
|
144
|
+
keygen_ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_X25519, NULL);
|
|
145
|
+
if (!keygen_ctx)
|
|
180
146
|
goto cleanup;
|
|
181
|
-
|
|
147
|
+
|
|
148
|
+
if (EVP_PKEY_keygen_init(keygen_ctx) <= 0)
|
|
182
149
|
goto cleanup;
|
|
183
|
-
|
|
150
|
+
|
|
151
|
+
if (EVP_PKEY_keygen(keygen_ctx, &ephemeral_pkey) <= 0)
|
|
184
152
|
goto cleanup;
|
|
185
|
-
|
|
153
|
+
|
|
154
|
+
if (EVP_PKEY_get_raw_public_key(ephemeral_pkey, ephemeral_pk, &pklen) <= 0)
|
|
186
155
|
goto cleanup;
|
|
187
|
-
if (
|
|
156
|
+
if (pklen != X25519_PUBLICKEYBYTES)
|
|
188
157
|
goto cleanup;
|
|
189
158
|
|
|
190
|
-
ret =
|
|
159
|
+
ret = x25519_shared_secret_with_pkey(shared, their_pk, ephemeral_pkey);
|
|
191
160
|
|
|
192
161
|
cleanup:
|
|
193
|
-
|
|
162
|
+
if (ephemeral_pkey)
|
|
163
|
+
EVP_PKEY_free(ephemeral_pkey);
|
|
164
|
+
if (keygen_ctx)
|
|
165
|
+
EVP_PKEY_CTX_free(keygen_ctx);
|
|
194
166
|
return ret;
|
|
195
167
|
}
|
|
196
168
|
|
|
169
|
+
static const uint8_t XWING_LABEL[6] = {0x5c, 0x2e, 0x2f, 0x2f, 0x5e, 0x5c};
|
|
170
|
+
|
|
171
|
+
static int xwing_combiner(uint8_t shared_secret[HYBRID_SHAREDSECRETBYTES],
|
|
172
|
+
const uint8_t ss_M[MLKEM_SHAREDSECRETBYTES],
|
|
173
|
+
const uint8_t ss_X[X25519_SHAREDSECRETBYTES],
|
|
174
|
+
const uint8_t ct_X[X25519_PUBLICKEYBYTES],
|
|
175
|
+
const uint8_t pk_X[X25519_PUBLICKEYBYTES]) {
|
|
176
|
+
uint8_t input[MLKEM_SHAREDSECRETBYTES + X25519_SHAREDSECRETBYTES +
|
|
177
|
+
X25519_PUBLICKEYBYTES + X25519_PUBLICKEYBYTES + sizeof(XWING_LABEL)];
|
|
178
|
+
uint8_t *cur = input;
|
|
179
|
+
|
|
180
|
+
if (!shared_secret || !ss_M || !ss_X || !ct_X || !pk_X) {
|
|
181
|
+
return PQ_ERROR_BUFFER;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
memcpy(cur, ss_M, MLKEM_SHAREDSECRETBYTES);
|
|
185
|
+
cur += MLKEM_SHAREDSECRETBYTES;
|
|
186
|
+
memcpy(cur, ss_X, X25519_SHAREDSECRETBYTES);
|
|
187
|
+
cur += X25519_SHAREDSECRETBYTES;
|
|
188
|
+
memcpy(cur, ct_X, X25519_PUBLICKEYBYTES);
|
|
189
|
+
cur += X25519_PUBLICKEYBYTES;
|
|
190
|
+
memcpy(cur, pk_X, X25519_PUBLICKEYBYTES);
|
|
191
|
+
cur += X25519_PUBLICKEYBYTES;
|
|
192
|
+
memcpy(cur, XWING_LABEL, sizeof(XWING_LABEL));
|
|
193
|
+
|
|
194
|
+
pqcr_mlkem_sha3_256(shared_secret, input, sizeof(input));
|
|
195
|
+
pq_secure_wipe(input, sizeof(input));
|
|
196
|
+
return PQ_SUCCESS;
|
|
197
|
+
}
|
|
198
|
+
|
|
197
199
|
static int xwing_expand_secret_key(hybrid_expanded_secret_key_t *expanded_key,
|
|
198
200
|
const uint8_t seed[HYBRID_SECRETKEYBYTES]) {
|
|
199
|
-
EVP_MD_CTX *ctx = NULL;
|
|
200
201
|
uint8_t expanded[XWING_EXPANDEDBYTES];
|
|
201
202
|
int ret = PQ_ERROR_OPENSSL;
|
|
202
203
|
|
|
@@ -207,16 +208,7 @@ static int xwing_expand_secret_key(hybrid_expanded_secret_key_t *expanded_key,
|
|
|
207
208
|
memset(expanded_key, 0, sizeof(*expanded_key));
|
|
208
209
|
memset(expanded, 0, sizeof(expanded));
|
|
209
210
|
|
|
210
|
-
|
|
211
|
-
if (!ctx)
|
|
212
|
-
goto cleanup;
|
|
213
|
-
|
|
214
|
-
if (EVP_DigestInit_ex(ctx, EVP_shake256(), NULL) != 1)
|
|
215
|
-
goto cleanup;
|
|
216
|
-
if (EVP_DigestUpdate(ctx, seed, HYBRID_SECRETKEYBYTES) != 1)
|
|
217
|
-
goto cleanup;
|
|
218
|
-
if (EVP_DigestFinalXOF(ctx, expanded, sizeof(expanded)) != 1)
|
|
219
|
-
goto cleanup;
|
|
211
|
+
pqcr_mlkem_shake256(expanded, sizeof(expanded), seed, HYBRID_SECRETKEYBYTES);
|
|
220
212
|
|
|
221
213
|
ret = pqcr_mlkem768_keypair_derand(expanded_key->mlkem_pk, expanded_key->mlkem_sk, expanded);
|
|
222
214
|
if (ret != 0) {
|
|
@@ -233,8 +225,6 @@ static int xwing_expand_secret_key(hybrid_expanded_secret_key_t *expanded_key,
|
|
|
233
225
|
ret = PQ_SUCCESS;
|
|
234
226
|
|
|
235
227
|
cleanup:
|
|
236
|
-
if (ctx)
|
|
237
|
-
EVP_MD_CTX_free(ctx);
|
|
238
228
|
pq_secure_wipe(expanded, sizeof(expanded));
|
|
239
229
|
if (ret != PQ_SUCCESS && expanded_key) {
|
|
240
230
|
pq_secure_wipe(expanded_key, sizeof(*expanded_key));
|
|
@@ -517,7 +507,6 @@ int pq_hybrid_kem_encapsulate(uint8_t *ciphertext, uint8_t *shared_secret,
|
|
|
517
507
|
hybrid_ciphertext_t ct;
|
|
518
508
|
uint8_t mlkem_ss[MLKEM_SHAREDSECRETBYTES];
|
|
519
509
|
uint8_t x25519_ss[X25519_SHAREDSECRETBYTES];
|
|
520
|
-
uint8_t x25519_ephemeral_sk[X25519_SECRETKEYBYTES];
|
|
521
510
|
int ret = PQ_SUCCESS;
|
|
522
511
|
|
|
523
512
|
if (!ciphertext || !shared_secret || !public_key) {
|
|
@@ -528,20 +517,14 @@ int pq_hybrid_kem_encapsulate(uint8_t *ciphertext, uint8_t *shared_secret,
|
|
|
528
517
|
memset(&ct, 0, sizeof(ct));
|
|
529
518
|
memset(mlkem_ss, 0, sizeof(mlkem_ss));
|
|
530
519
|
memset(x25519_ss, 0, sizeof(x25519_ss));
|
|
531
|
-
memset(x25519_ephemeral_sk, 0, sizeof(x25519_ephemeral_sk));
|
|
532
520
|
|
|
533
521
|
if (pqcr_mlkem768_enc(ct.mlkem_ct, mlkem_ss, pk.mlkem_pk) != 0) {
|
|
534
522
|
ret = PQ_ERROR_ENCAPSULATE;
|
|
535
523
|
goto cleanup;
|
|
536
524
|
}
|
|
537
525
|
|
|
538
|
-
ret =
|
|
539
|
-
|
|
540
|
-
ret = PQ_ERROR_ENCAPSULATE;
|
|
541
|
-
goto cleanup;
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
ret = x25519_shared_secret(x25519_ss, pk.x25519_pk, x25519_ephemeral_sk);
|
|
526
|
+
ret = x25519_ephemeral_keypair_and_shared_secret(ct.x25519_ephemeral, x25519_ss,
|
|
527
|
+
pk.x25519_pk);
|
|
545
528
|
if (ret != PQ_SUCCESS) {
|
|
546
529
|
ret = PQ_ERROR_ENCAPSULATE;
|
|
547
530
|
goto cleanup;
|
|
@@ -557,51 +540,121 @@ int pq_hybrid_kem_encapsulate(uint8_t *ciphertext, uint8_t *shared_secret,
|
|
|
557
540
|
cleanup:
|
|
558
541
|
pq_secure_wipe(mlkem_ss, sizeof(mlkem_ss));
|
|
559
542
|
pq_secure_wipe(x25519_ss, sizeof(x25519_ss));
|
|
560
|
-
pq_secure_wipe(x25519_ephemeral_sk, sizeof(x25519_ephemeral_sk));
|
|
561
543
|
return ret;
|
|
562
544
|
}
|
|
563
545
|
|
|
564
|
-
int
|
|
565
|
-
const uint8_t *secret_key) {
|
|
566
|
-
hybrid_ciphertext_t ct;
|
|
546
|
+
int pq_hybrid_kem_expand_secret_key(uint8_t *expanded_secret_key, const uint8_t *secret_key) {
|
|
567
547
|
hybrid_expanded_secret_key_t expanded;
|
|
548
|
+
int ret;
|
|
549
|
+
|
|
550
|
+
if (!expanded_secret_key || !secret_key) {
|
|
551
|
+
return PQ_ERROR_BUFFER;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
memset(&expanded, 0, sizeof(expanded));
|
|
555
|
+
ret = xwing_expand_secret_key(&expanded, secret_key);
|
|
556
|
+
if (ret == PQ_SUCCESS) {
|
|
557
|
+
memcpy(expanded_secret_key, &expanded, sizeof(expanded));
|
|
558
|
+
}
|
|
559
|
+
pq_secure_wipe(&expanded, sizeof(expanded));
|
|
560
|
+
return ret == PQ_SUCCESS ? PQ_SUCCESS : PQ_ERROR_DECAPSULATE;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
int pq_hybrid_kem_decapsulate_expanded(uint8_t *shared_secret, const uint8_t *ciphertext,
|
|
564
|
+
const uint8_t *expanded_secret_key) {
|
|
565
|
+
hybrid_ciphertext_t ct;
|
|
566
|
+
const hybrid_expanded_secret_key_t *expanded;
|
|
568
567
|
uint8_t mlkem_ss[MLKEM_SHAREDSECRETBYTES];
|
|
569
568
|
uint8_t x25519_ss[X25519_SHAREDSECRETBYTES];
|
|
570
569
|
int ret = PQ_SUCCESS;
|
|
571
570
|
|
|
572
|
-
if (!shared_secret || !ciphertext || !
|
|
571
|
+
if (!shared_secret || !ciphertext || !expanded_secret_key) {
|
|
573
572
|
return PQ_ERROR_BUFFER;
|
|
574
573
|
}
|
|
575
574
|
|
|
575
|
+
expanded = (const hybrid_expanded_secret_key_t *)expanded_secret_key;
|
|
576
576
|
memcpy(&ct, ciphertext, HYBRID_CIPHERTEXTBYTES);
|
|
577
|
-
memset(&expanded, 0, sizeof(expanded));
|
|
578
577
|
memset(mlkem_ss, 0, sizeof(mlkem_ss));
|
|
579
578
|
memset(x25519_ss, 0, sizeof(x25519_ss));
|
|
580
579
|
|
|
581
|
-
|
|
580
|
+
if (pqcr_mlkem768_dec(mlkem_ss, ct.mlkem_ct, expanded->mlkem_sk) != 0) {
|
|
581
|
+
ret = PQ_ERROR_DECAPSULATE;
|
|
582
|
+
goto cleanup;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
ret = x25519_shared_secret(x25519_ss, ct.x25519_ephemeral, expanded->x25519_sk);
|
|
582
586
|
if (ret != PQ_SUCCESS) {
|
|
583
587
|
ret = PQ_ERROR_DECAPSULATE;
|
|
584
588
|
goto cleanup;
|
|
585
589
|
}
|
|
586
590
|
|
|
587
|
-
|
|
591
|
+
ret = xwing_combiner(shared_secret, mlkem_ss, x25519_ss, ct.x25519_ephemeral,
|
|
592
|
+
expanded->x25519_pk);
|
|
593
|
+
|
|
594
|
+
cleanup:
|
|
595
|
+
pq_secure_wipe(mlkem_ss, sizeof(mlkem_ss));
|
|
596
|
+
pq_secure_wipe(x25519_ss, sizeof(x25519_ss));
|
|
597
|
+
return ret;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
int pq_hybrid_kem_decapsulate_expanded_pkey(uint8_t *shared_secret, const uint8_t *ciphertext,
|
|
601
|
+
const uint8_t *expanded_secret_key,
|
|
602
|
+
void *x25519_private_pkey) {
|
|
603
|
+
hybrid_ciphertext_t ct;
|
|
604
|
+
const hybrid_expanded_secret_key_t *expanded;
|
|
605
|
+
uint8_t mlkem_ss[MLKEM_SHAREDSECRETBYTES];
|
|
606
|
+
uint8_t x25519_ss[X25519_SHAREDSECRETBYTES];
|
|
607
|
+
int ret = PQ_SUCCESS;
|
|
608
|
+
|
|
609
|
+
if (!shared_secret || !ciphertext || !expanded_secret_key || !x25519_private_pkey) {
|
|
610
|
+
return PQ_ERROR_BUFFER;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
expanded = (const hybrid_expanded_secret_key_t *)expanded_secret_key;
|
|
614
|
+
memcpy(&ct, ciphertext, HYBRID_CIPHERTEXTBYTES);
|
|
615
|
+
memset(mlkem_ss, 0, sizeof(mlkem_ss));
|
|
616
|
+
memset(x25519_ss, 0, sizeof(x25519_ss));
|
|
617
|
+
|
|
618
|
+
if (pqcr_mlkem768_dec(mlkem_ss, ct.mlkem_ct, expanded->mlkem_sk) != 0) {
|
|
588
619
|
ret = PQ_ERROR_DECAPSULATE;
|
|
589
620
|
goto cleanup;
|
|
590
621
|
}
|
|
591
622
|
|
|
592
|
-
ret =
|
|
623
|
+
ret = x25519_shared_secret_with_pkey(x25519_ss, ct.x25519_ephemeral,
|
|
624
|
+
(EVP_PKEY *)x25519_private_pkey);
|
|
593
625
|
if (ret != PQ_SUCCESS) {
|
|
594
626
|
ret = PQ_ERROR_DECAPSULATE;
|
|
595
627
|
goto cleanup;
|
|
596
628
|
}
|
|
597
629
|
|
|
598
|
-
ret =
|
|
599
|
-
|
|
630
|
+
ret = xwing_combiner(shared_secret, mlkem_ss, x25519_ss, ct.x25519_ephemeral,
|
|
631
|
+
expanded->x25519_pk);
|
|
600
632
|
|
|
601
633
|
cleanup:
|
|
602
634
|
pq_secure_wipe(mlkem_ss, sizeof(mlkem_ss));
|
|
603
635
|
pq_secure_wipe(x25519_ss, sizeof(x25519_ss));
|
|
604
|
-
|
|
636
|
+
return ret;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
int pq_hybrid_kem_decapsulate(uint8_t *shared_secret, const uint8_t *ciphertext,
|
|
640
|
+
const uint8_t *secret_key) {
|
|
641
|
+
uint8_t expanded[HYBRID_EXPANDED_SECRETKEYBYTES];
|
|
642
|
+
int ret;
|
|
643
|
+
|
|
644
|
+
if (!shared_secret || !ciphertext || !secret_key) {
|
|
645
|
+
return PQ_ERROR_BUFFER;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
memset(expanded, 0, sizeof(expanded));
|
|
649
|
+
ret = pq_hybrid_kem_expand_secret_key(expanded, secret_key);
|
|
650
|
+
if (ret != PQ_SUCCESS) {
|
|
651
|
+
goto cleanup;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
ret = pq_hybrid_kem_decapsulate_expanded(shared_secret, ciphertext, expanded);
|
|
655
|
+
|
|
656
|
+
cleanup:
|
|
657
|
+
pq_secure_wipe(expanded, sizeof(expanded));
|
|
605
658
|
return ret;
|
|
606
659
|
}
|
|
607
660
|
|
|
@@ -805,24 +858,51 @@ static int pq_decode_serialized_key(const uint8_t *input, size_t input_len, uint
|
|
|
805
858
|
return PQ_SUCCESS;
|
|
806
859
|
}
|
|
807
860
|
|
|
861
|
+
static int pq_base64_char_value(unsigned char c) {
|
|
862
|
+
if (c >= 'A' && c <= 'Z')
|
|
863
|
+
return (int)(c - 'A');
|
|
864
|
+
if (c >= 'a' && c <= 'z')
|
|
865
|
+
return (int)(c - 'a' + 26);
|
|
866
|
+
if (c >= '0' && c <= '9')
|
|
867
|
+
return (int)(c - '0' + 52);
|
|
868
|
+
if (c == '+')
|
|
869
|
+
return 62;
|
|
870
|
+
if (c == '/')
|
|
871
|
+
return 63;
|
|
872
|
+
if (c == '=')
|
|
873
|
+
return 64;
|
|
874
|
+
return -1;
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
static const char *pq_find_pem_footer(const char *start, size_t len, const char *footer,
|
|
878
|
+
size_t footer_len) {
|
|
879
|
+
if (!start || !footer || footer_len == 0 || len < footer_len)
|
|
880
|
+
return NULL;
|
|
881
|
+
|
|
882
|
+
for (size_t i = 0; i <= len - footer_len; ++i) {
|
|
883
|
+
if (start[i] == '-' && memcmp(start + i, footer, footer_len) == 0)
|
|
884
|
+
return start + i;
|
|
885
|
+
}
|
|
886
|
+
return NULL;
|
|
887
|
+
}
|
|
888
|
+
|
|
808
889
|
static int pq_der_to_pem(const char *label, const uint8_t *der, size_t der_len, char **output,
|
|
809
890
|
size_t *output_len) {
|
|
810
|
-
BIO *bio_mem = NULL;
|
|
811
|
-
BIO *bio_b64 = NULL;
|
|
812
|
-
BIO *bio_chain = NULL;
|
|
813
|
-
BUF_MEM *bptr = NULL;
|
|
814
891
|
char header[64];
|
|
815
892
|
char footer[64];
|
|
816
893
|
int header_len, footer_len;
|
|
817
|
-
int ret = PQ_ERROR_OPENSSL;
|
|
818
894
|
char *pem = NULL;
|
|
819
|
-
|
|
820
|
-
size_t
|
|
895
|
+
unsigned char *encoded = NULL;
|
|
896
|
+
size_t encoded_len;
|
|
897
|
+
size_t line_count;
|
|
898
|
+
size_t needed;
|
|
899
|
+
char *cur;
|
|
821
900
|
|
|
822
901
|
if (!label || !der || !output || !output_len)
|
|
823
902
|
return PQ_ERROR_BUFFER;
|
|
824
903
|
*output = NULL;
|
|
825
904
|
*output_len = 0;
|
|
905
|
+
|
|
826
906
|
header_len = snprintf(header, sizeof(header), "-----BEGIN %s-----", label);
|
|
827
907
|
footer_len = snprintf(footer, sizeof(footer), "-----END %s-----", label);
|
|
828
908
|
if (header_len <= 0 || footer_len <= 0)
|
|
@@ -830,68 +910,60 @@ static int pq_der_to_pem(const char *label, const uint8_t *der, size_t der_len,
|
|
|
830
910
|
if (der_len > (size_t)INT_MAX)
|
|
831
911
|
return PQ_ERROR_BUFFER;
|
|
832
912
|
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
913
|
+
encoded_len = 4 * ((der_len + 2) / 3);
|
|
914
|
+
if (encoded_len > (size_t)INT_MAX)
|
|
915
|
+
return PQ_ERROR_BUFFER;
|
|
916
|
+
|
|
917
|
+
encoded = malloc(encoded_len + 1);
|
|
918
|
+
if (!encoded)
|
|
919
|
+
return PQ_ERROR_NOMEM;
|
|
920
|
+
|
|
921
|
+
if (EVP_EncodeBlock(encoded, der, (int)der_len) != (int)encoded_len) {
|
|
922
|
+
pq_secure_wipe(encoded, encoded_len + 1);
|
|
923
|
+
free(encoded);
|
|
924
|
+
return PQ_ERROR_OPENSSL;
|
|
838
925
|
}
|
|
839
926
|
|
|
840
|
-
|
|
927
|
+
line_count = encoded_len == 0 ? 0 : ((encoded_len + 63) / 64);
|
|
928
|
+
if (SIZE_MAX - (size_t)header_len < 1 || SIZE_MAX - ((size_t)header_len + 1) < encoded_len ||
|
|
929
|
+
SIZE_MAX - ((size_t)header_len + 1 + encoded_len) < line_count ||
|
|
930
|
+
SIZE_MAX - ((size_t)header_len + 1 + encoded_len + line_count) < (size_t)footer_len + 1) {
|
|
931
|
+
pq_secure_wipe(encoded, encoded_len + 1);
|
|
932
|
+
free(encoded);
|
|
933
|
+
return PQ_ERROR_BUFFER;
|
|
934
|
+
}
|
|
935
|
+
needed = (size_t)header_len + 1 + encoded_len + line_count + (size_t)footer_len + 1;
|
|
841
936
|
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
goto cleanup;
|
|
937
|
+
pem = malloc(needed);
|
|
938
|
+
if (!pem) {
|
|
939
|
+
pq_secure_wipe(encoded, encoded_len + 1);
|
|
940
|
+
free(encoded);
|
|
941
|
+
return PQ_ERROR_NOMEM;
|
|
942
|
+
}
|
|
849
943
|
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
}
|
|
862
|
-
char *cur = pem;
|
|
863
|
-
memcpy(cur, header, (size_t)header_len);
|
|
864
|
-
cur += header_len;
|
|
944
|
+
cur = pem;
|
|
945
|
+
memcpy(cur, header, (size_t)header_len);
|
|
946
|
+
cur += header_len;
|
|
947
|
+
*cur++ = '\n';
|
|
948
|
+
|
|
949
|
+
for (size_t offset = 0; offset < encoded_len; offset += 64) {
|
|
950
|
+
size_t line_len = encoded_len - offset;
|
|
951
|
+
if (line_len > 64)
|
|
952
|
+
line_len = 64;
|
|
953
|
+
memcpy(cur, encoded + offset, line_len);
|
|
954
|
+
cur += line_len;
|
|
865
955
|
*cur++ = '\n';
|
|
866
|
-
memcpy(cur, bptr->data, body_len);
|
|
867
|
-
cur += body_len;
|
|
868
|
-
if (body_len == 0 || bptr->data[body_len - 1] != '\n')
|
|
869
|
-
*cur++ = '\n';
|
|
870
|
-
memcpy(cur, footer, (size_t)footer_len);
|
|
871
|
-
cur += footer_len;
|
|
872
|
-
*cur = '\0';
|
|
873
|
-
total_len = (size_t)(cur - pem);
|
|
874
956
|
}
|
|
875
957
|
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
ret = PQ_SUCCESS;
|
|
958
|
+
memcpy(cur, footer, (size_t)footer_len);
|
|
959
|
+
cur += footer_len;
|
|
960
|
+
*cur = '\0';
|
|
880
961
|
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
BIO_free(bio_b64);
|
|
887
|
-
if (bio_mem)
|
|
888
|
-
BIO_free(bio_mem);
|
|
889
|
-
}
|
|
890
|
-
if (pem) {
|
|
891
|
-
pq_secure_wipe(pem, needed);
|
|
892
|
-
free(pem);
|
|
893
|
-
}
|
|
894
|
-
return ret;
|
|
962
|
+
*output = pem;
|
|
963
|
+
*output_len = (size_t)(cur - pem);
|
|
964
|
+
pq_secure_wipe(encoded, encoded_len + 1);
|
|
965
|
+
free(encoded);
|
|
966
|
+
return PQ_SUCCESS;
|
|
895
967
|
}
|
|
896
968
|
|
|
897
969
|
static int pq_pem_to_der(const char *label, const char *input, size_t input_len, uint8_t **der_out,
|
|
@@ -901,17 +973,19 @@ static int pq_pem_to_der(const char *label, const char *input, size_t input_len,
|
|
|
901
973
|
const char *body_start, *footer_pos;
|
|
902
974
|
const char *tail;
|
|
903
975
|
uint8_t *der = NULL;
|
|
976
|
+
unsigned char *compact = NULL;
|
|
904
977
|
size_t body_len = 0;
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
BIO *bio_mem = NULL;
|
|
908
|
-
BIO *bio_chain = NULL;
|
|
978
|
+
size_t compact_len = 0;
|
|
979
|
+
size_t der_cap = 0;
|
|
909
980
|
int decoded_len = 0;
|
|
981
|
+
int padding = 0;
|
|
982
|
+
int saw_padding = 0;
|
|
910
983
|
|
|
911
984
|
if (!label || !input || !der_out || !der_len_out)
|
|
912
985
|
return PQ_ERROR_BUFFER;
|
|
913
986
|
*der_out = NULL;
|
|
914
987
|
*der_len_out = 0;
|
|
988
|
+
|
|
915
989
|
header_len = snprintf(header, sizeof(header), "-----BEGIN %s-----", label);
|
|
916
990
|
footer_len = snprintf(footer, sizeof(footer), "-----END %s-----", label);
|
|
917
991
|
if (header_len <= 0 || footer_len <= 0)
|
|
@@ -920,22 +994,13 @@ static int pq_pem_to_der(const char *label, const char *input, size_t input_len,
|
|
|
920
994
|
return PQ_ERROR_BUFFER;
|
|
921
995
|
if (strncmp(input, header, (size_t)header_len) != 0)
|
|
922
996
|
return PQ_ERROR_BUFFER;
|
|
997
|
+
|
|
923
998
|
body_start = input + header_len;
|
|
924
999
|
while ((size_t)(body_start - input) < input_len && pq_is_pem_whitespace(*body_start))
|
|
925
1000
|
body_start++;
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
size_t footer_size = (size_t)footer_len;
|
|
930
|
-
if (remaining < footer_size)
|
|
931
|
-
return PQ_ERROR_BUFFER;
|
|
932
|
-
for (size_t i = 0; i <= remaining - footer_size; ++i) {
|
|
933
|
-
if (memcmp(body_start + i, footer, footer_size) == 0) {
|
|
934
|
-
footer_pos = body_start + i;
|
|
935
|
-
break;
|
|
936
|
-
}
|
|
937
|
-
}
|
|
938
|
-
}
|
|
1001
|
+
|
|
1002
|
+
footer_pos = pq_find_pem_footer(body_start, input_len - (size_t)(body_start - input), footer,
|
|
1003
|
+
(size_t)footer_len);
|
|
939
1004
|
if (!footer_pos)
|
|
940
1005
|
return PQ_ERROR_BUFFER;
|
|
941
1006
|
|
|
@@ -950,53 +1015,65 @@ static int pq_pem_to_der(const char *label, const char *input, size_t input_len,
|
|
|
950
1015
|
if (body_len > (size_t)INT_MAX)
|
|
951
1016
|
return PQ_ERROR_BUFFER;
|
|
952
1017
|
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
if (!der)
|
|
957
|
-
return PQ_ERROR_NOMEM;
|
|
1018
|
+
compact = malloc(body_len ? body_len : 1);
|
|
1019
|
+
if (!compact)
|
|
1020
|
+
return PQ_ERROR_NOMEM;
|
|
958
1021
|
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
}
|
|
965
|
-
bio_chain = BIO_push(bio_b64, bio_mem);
|
|
1022
|
+
for (size_t i = 0; i < body_len; ++i) {
|
|
1023
|
+
unsigned char c = (unsigned char)body_start[i];
|
|
1024
|
+
int value;
|
|
1025
|
+
if (pq_is_pem_whitespace((char)c))
|
|
1026
|
+
continue;
|
|
966
1027
|
|
|
967
|
-
|
|
968
|
-
if (
|
|
969
|
-
|
|
970
|
-
|
|
1028
|
+
value = pq_base64_char_value(c);
|
|
1029
|
+
if (value < 0) {
|
|
1030
|
+
pq_secure_wipe(compact, body_len);
|
|
1031
|
+
free(compact);
|
|
1032
|
+
return PQ_ERROR_BUFFER;
|
|
971
1033
|
}
|
|
972
|
-
{
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
if (
|
|
976
|
-
|
|
977
|
-
|
|
1034
|
+
if (c == '=') {
|
|
1035
|
+
saw_padding = 1;
|
|
1036
|
+
padding++;
|
|
1037
|
+
if (padding > 2) {
|
|
1038
|
+
pq_secure_wipe(compact, body_len);
|
|
1039
|
+
free(compact);
|
|
1040
|
+
return PQ_ERROR_BUFFER;
|
|
978
1041
|
}
|
|
1042
|
+
} else if (saw_padding) {
|
|
1043
|
+
pq_secure_wipe(compact, body_len);
|
|
1044
|
+
free(compact);
|
|
1045
|
+
return PQ_ERROR_BUFFER;
|
|
979
1046
|
}
|
|
1047
|
+
compact[compact_len++] = c;
|
|
1048
|
+
}
|
|
980
1049
|
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
1050
|
+
if (compact_len == 0 || (compact_len % 4) != 0 || compact_len > (size_t)INT_MAX) {
|
|
1051
|
+
pq_secure_wipe(compact, body_len);
|
|
1052
|
+
free(compact);
|
|
1053
|
+
return PQ_ERROR_BUFFER;
|
|
985
1054
|
}
|
|
986
1055
|
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
if (bio_mem)
|
|
994
|
-
BIO_free(bio_mem);
|
|
1056
|
+
der_cap = (compact_len / 4) * 3;
|
|
1057
|
+
der = malloc(der_cap ? der_cap : 1);
|
|
1058
|
+
if (!der) {
|
|
1059
|
+
pq_secure_wipe(compact, body_len);
|
|
1060
|
+
free(compact);
|
|
1061
|
+
return PQ_ERROR_NOMEM;
|
|
995
1062
|
}
|
|
996
|
-
|
|
1063
|
+
|
|
1064
|
+
decoded_len = EVP_DecodeBlock(der, compact, (int)compact_len);
|
|
1065
|
+
pq_secure_wipe(compact, body_len);
|
|
1066
|
+
free(compact);
|
|
1067
|
+
compact = NULL;
|
|
1068
|
+
if (decoded_len <= 0 || decoded_len < padding) {
|
|
1069
|
+
pq_secure_wipe(der, der_cap);
|
|
997
1070
|
free(der);
|
|
1071
|
+
return PQ_ERROR_BUFFER;
|
|
998
1072
|
}
|
|
999
|
-
|
|
1073
|
+
|
|
1074
|
+
*der_len_out = (size_t)(decoded_len - padding);
|
|
1075
|
+
*der_out = der;
|
|
1076
|
+
return PQ_SUCCESS;
|
|
1000
1077
|
}
|
|
1001
1078
|
|
|
1002
1079
|
int pq_public_key_to_pqc_container_der(uint8_t **output, size_t *output_len,
|