pq_crypto 0.3.0 → 0.3.2
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 +40 -15
- data/GET_STARTED.md +26 -0
- data/README.md +62 -7
- data/SECURITY.md +33 -21
- data/ext/pqcrypto/extconf.rb +57 -4
- data/ext/pqcrypto/pq_externalmu.c +297 -0
- data/ext/pqcrypto/pqcrypto_ruby_secure.c +309 -9
- data/ext/pqcrypto/pqcrypto_secure.c +102 -42
- data/ext/pqcrypto/pqcrypto_secure.h +26 -2
- data/ext/pqcrypto/vendor/pqclean/common/keccak4x/Makefile +8 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/Makefile +19 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-65/clean/Makefile +19 -0
- data/lib/pq_crypto/kem.rb +7 -3
- data/lib/pq_crypto/serialization.rb +1 -1
- data/lib/pq_crypto/signature.rb +115 -3
- data/lib/pq_crypto/version.rb +1 -1
- data/lib/pq_crypto.rb +12 -0
- metadata +8 -4
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
#include "pqcrypto_secure.h"
|
|
2
|
+
|
|
3
|
+
#include <stdint.h>
|
|
4
|
+
#include <stddef.h>
|
|
5
|
+
#include <string.h>
|
|
6
|
+
|
|
7
|
+
#include "vendor/pqclean/crypto_sign/ml-dsa-65/clean/params.h"
|
|
8
|
+
#include "vendor/pqclean/crypto_sign/ml-dsa-65/clean/packing.h"
|
|
9
|
+
#include "vendor/pqclean/crypto_sign/ml-dsa-65/clean/polyvec.h"
|
|
10
|
+
#include "vendor/pqclean/crypto_sign/ml-dsa-65/clean/poly.h"
|
|
11
|
+
#include "vendor/pqclean/crypto_sign/ml-dsa-65/clean/symmetric.h"
|
|
12
|
+
#include "fips202.h"
|
|
13
|
+
#include "randombytes.h"
|
|
14
|
+
|
|
15
|
+
#if CRHBYTES != PQ_MLDSA_MUBYTES
|
|
16
|
+
#error "PQ_MLDSA_MUBYTES must match PQClean's CRHBYTES"
|
|
17
|
+
#endif
|
|
18
|
+
#if TRBYTES != PQ_MLDSA_TRBYTES
|
|
19
|
+
#error "PQ_MLDSA_TRBYTES must match PQClean's TRBYTES"
|
|
20
|
+
#endif
|
|
21
|
+
|
|
22
|
+
int pq_mldsa_extract_tr_from_secret_key(uint8_t *tr_out, const uint8_t *secret_key) {
|
|
23
|
+
if (tr_out == NULL || secret_key == NULL) {
|
|
24
|
+
return PQ_ERROR_BUFFER;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
uint8_t rho[SEEDBYTES];
|
|
28
|
+
uint8_t key[SEEDBYTES];
|
|
29
|
+
polyveck t0;
|
|
30
|
+
polyvecl s1;
|
|
31
|
+
polyveck s2;
|
|
32
|
+
|
|
33
|
+
PQCLEAN_MLDSA65_CLEAN_unpack_sk(rho, tr_out, key, &t0, &s1, &s2, secret_key);
|
|
34
|
+
|
|
35
|
+
pq_secure_wipe(rho, sizeof(rho));
|
|
36
|
+
pq_secure_wipe(key, sizeof(key));
|
|
37
|
+
pq_secure_wipe(&t0, sizeof(t0));
|
|
38
|
+
pq_secure_wipe(&s1, sizeof(s1));
|
|
39
|
+
pq_secure_wipe(&s2, sizeof(s2));
|
|
40
|
+
|
|
41
|
+
return PQ_SUCCESS;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
int pq_mldsa_compute_tr_from_public_key(uint8_t *tr_out, const uint8_t *public_key) {
|
|
45
|
+
if (tr_out == NULL || public_key == NULL) {
|
|
46
|
+
return PQ_ERROR_BUFFER;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
shake256(tr_out, TRBYTES, public_key, PQCLEAN_MLDSA65_CLEAN_CRYPTO_PUBLICKEYBYTES);
|
|
50
|
+
return PQ_SUCCESS;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
int pq_sign_mu(uint8_t *signature, size_t *signature_len, const uint8_t *mu,
|
|
54
|
+
const uint8_t *secret_key) {
|
|
55
|
+
if (signature == NULL || signature_len == NULL || mu == NULL || secret_key == NULL) {
|
|
56
|
+
return PQ_ERROR_BUFFER;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
unsigned int n;
|
|
60
|
+
uint8_t rho[SEEDBYTES];
|
|
61
|
+
uint8_t tr_unused[TRBYTES];
|
|
62
|
+
uint8_t key[SEEDBYTES];
|
|
63
|
+
uint8_t rnd[RNDBYTES];
|
|
64
|
+
uint8_t mu_local[CRHBYTES];
|
|
65
|
+
uint8_t rhoprime[CRHBYTES];
|
|
66
|
+
uint16_t nonce = 0;
|
|
67
|
+
polyvecl mat[K], s1, y, z;
|
|
68
|
+
polyveck t0, s2, w1, w0, h;
|
|
69
|
+
poly cp;
|
|
70
|
+
shake256incctx state;
|
|
71
|
+
|
|
72
|
+
PQCLEAN_MLDSA65_CLEAN_unpack_sk(rho, tr_unused, key, &t0, &s1, &s2, secret_key);
|
|
73
|
+
pq_secure_wipe(tr_unused, sizeof(tr_unused));
|
|
74
|
+
|
|
75
|
+
memcpy(mu_local, mu, CRHBYTES);
|
|
76
|
+
|
|
77
|
+
randombytes(rnd, RNDBYTES);
|
|
78
|
+
|
|
79
|
+
{
|
|
80
|
+
uint8_t kr[SEEDBYTES + RNDBYTES + CRHBYTES];
|
|
81
|
+
memcpy(kr, key, SEEDBYTES);
|
|
82
|
+
memcpy(kr + SEEDBYTES, rnd, RNDBYTES);
|
|
83
|
+
memcpy(kr + SEEDBYTES + RNDBYTES, mu_local, CRHBYTES);
|
|
84
|
+
shake256(rhoprime, CRHBYTES, kr, sizeof(kr));
|
|
85
|
+
pq_secure_wipe(kr, sizeof(kr));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
PQCLEAN_MLDSA65_CLEAN_polyvec_matrix_expand(mat, rho);
|
|
89
|
+
PQCLEAN_MLDSA65_CLEAN_polyvecl_ntt(&s1);
|
|
90
|
+
PQCLEAN_MLDSA65_CLEAN_polyveck_ntt(&s2);
|
|
91
|
+
PQCLEAN_MLDSA65_CLEAN_polyveck_ntt(&t0);
|
|
92
|
+
|
|
93
|
+
rej:
|
|
94
|
+
PQCLEAN_MLDSA65_CLEAN_polyvecl_uniform_gamma1(&y, rhoprime, nonce++);
|
|
95
|
+
|
|
96
|
+
z = y;
|
|
97
|
+
PQCLEAN_MLDSA65_CLEAN_polyvecl_ntt(&z);
|
|
98
|
+
PQCLEAN_MLDSA65_CLEAN_polyvec_matrix_pointwise_montgomery(&w1, mat, &z);
|
|
99
|
+
PQCLEAN_MLDSA65_CLEAN_polyveck_reduce(&w1);
|
|
100
|
+
PQCLEAN_MLDSA65_CLEAN_polyveck_invntt_tomont(&w1);
|
|
101
|
+
|
|
102
|
+
PQCLEAN_MLDSA65_CLEAN_polyveck_caddq(&w1);
|
|
103
|
+
PQCLEAN_MLDSA65_CLEAN_polyveck_decompose(&w1, &w0, &w1);
|
|
104
|
+
PQCLEAN_MLDSA65_CLEAN_polyveck_pack_w1(signature, &w1);
|
|
105
|
+
|
|
106
|
+
shake256_inc_init(&state);
|
|
107
|
+
shake256_inc_absorb(&state, mu_local, CRHBYTES);
|
|
108
|
+
shake256_inc_absorb(&state, signature, K * POLYW1_PACKEDBYTES);
|
|
109
|
+
shake256_inc_finalize(&state);
|
|
110
|
+
shake256_inc_squeeze(signature, CTILDEBYTES, &state);
|
|
111
|
+
shake256_inc_ctx_release(&state);
|
|
112
|
+
|
|
113
|
+
PQCLEAN_MLDSA65_CLEAN_poly_challenge(&cp, signature);
|
|
114
|
+
PQCLEAN_MLDSA65_CLEAN_poly_ntt(&cp);
|
|
115
|
+
|
|
116
|
+
PQCLEAN_MLDSA65_CLEAN_polyvecl_pointwise_poly_montgomery(&z, &cp, &s1);
|
|
117
|
+
PQCLEAN_MLDSA65_CLEAN_polyvecl_invntt_tomont(&z);
|
|
118
|
+
PQCLEAN_MLDSA65_CLEAN_polyvecl_add(&z, &z, &y);
|
|
119
|
+
PQCLEAN_MLDSA65_CLEAN_polyvecl_reduce(&z);
|
|
120
|
+
if (PQCLEAN_MLDSA65_CLEAN_polyvecl_chknorm(&z, GAMMA1 - BETA)) {
|
|
121
|
+
goto rej;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
PQCLEAN_MLDSA65_CLEAN_polyveck_pointwise_poly_montgomery(&h, &cp, &s2);
|
|
125
|
+
PQCLEAN_MLDSA65_CLEAN_polyveck_invntt_tomont(&h);
|
|
126
|
+
PQCLEAN_MLDSA65_CLEAN_polyveck_sub(&w0, &w0, &h);
|
|
127
|
+
PQCLEAN_MLDSA65_CLEAN_polyveck_reduce(&w0);
|
|
128
|
+
if (PQCLEAN_MLDSA65_CLEAN_polyveck_chknorm(&w0, GAMMA2 - BETA)) {
|
|
129
|
+
goto rej;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
PQCLEAN_MLDSA65_CLEAN_polyveck_pointwise_poly_montgomery(&h, &cp, &t0);
|
|
133
|
+
PQCLEAN_MLDSA65_CLEAN_polyveck_invntt_tomont(&h);
|
|
134
|
+
PQCLEAN_MLDSA65_CLEAN_polyveck_reduce(&h);
|
|
135
|
+
if (PQCLEAN_MLDSA65_CLEAN_polyveck_chknorm(&h, GAMMA2)) {
|
|
136
|
+
goto rej;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
PQCLEAN_MLDSA65_CLEAN_polyveck_add(&w0, &w0, &h);
|
|
140
|
+
n = PQCLEAN_MLDSA65_CLEAN_polyveck_make_hint(&h, &w0, &w1);
|
|
141
|
+
if (n > OMEGA) {
|
|
142
|
+
goto rej;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
PQCLEAN_MLDSA65_CLEAN_pack_sig(signature, signature, &z, &h);
|
|
146
|
+
*signature_len = PQCLEAN_MLDSA65_CLEAN_CRYPTO_BYTES;
|
|
147
|
+
|
|
148
|
+
pq_secure_wipe(rho, sizeof(rho));
|
|
149
|
+
pq_secure_wipe(key, sizeof(key));
|
|
150
|
+
pq_secure_wipe(rnd, sizeof(rnd));
|
|
151
|
+
pq_secure_wipe(mu_local, sizeof(mu_local));
|
|
152
|
+
pq_secure_wipe(rhoprime, sizeof(rhoprime));
|
|
153
|
+
pq_secure_wipe(&s1, sizeof(s1));
|
|
154
|
+
pq_secure_wipe(&s2, sizeof(s2));
|
|
155
|
+
pq_secure_wipe(&t0, sizeof(t0));
|
|
156
|
+
pq_secure_wipe(&y, sizeof(y));
|
|
157
|
+
pq_secure_wipe(&z, sizeof(z));
|
|
158
|
+
pq_secure_wipe(&w0, sizeof(w0));
|
|
159
|
+
pq_secure_wipe(&cp, sizeof(cp));
|
|
160
|
+
|
|
161
|
+
return PQ_SUCCESS;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
int pq_verify_mu(const uint8_t *signature, size_t signature_len, const uint8_t *mu,
|
|
165
|
+
const uint8_t *public_key) {
|
|
166
|
+
if (signature == NULL || mu == NULL || public_key == NULL) {
|
|
167
|
+
return PQ_ERROR_BUFFER;
|
|
168
|
+
}
|
|
169
|
+
if (signature_len != PQCLEAN_MLDSA65_CLEAN_CRYPTO_BYTES) {
|
|
170
|
+
return PQ_ERROR_VERIFY;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
unsigned int i;
|
|
174
|
+
uint8_t buf[K * POLYW1_PACKEDBYTES];
|
|
175
|
+
uint8_t rho[SEEDBYTES];
|
|
176
|
+
uint8_t c[CTILDEBYTES];
|
|
177
|
+
uint8_t c2[CTILDEBYTES];
|
|
178
|
+
poly cp;
|
|
179
|
+
polyvecl mat[K], z;
|
|
180
|
+
polyveck t1, w1, h;
|
|
181
|
+
shake256incctx state;
|
|
182
|
+
|
|
183
|
+
PQCLEAN_MLDSA65_CLEAN_unpack_pk(rho, &t1, public_key);
|
|
184
|
+
if (PQCLEAN_MLDSA65_CLEAN_unpack_sig(c, &z, &h, signature)) {
|
|
185
|
+
return PQ_ERROR_VERIFY;
|
|
186
|
+
}
|
|
187
|
+
if (PQCLEAN_MLDSA65_CLEAN_polyvecl_chknorm(&z, GAMMA1 - BETA)) {
|
|
188
|
+
return PQ_ERROR_VERIFY;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
PQCLEAN_MLDSA65_CLEAN_poly_challenge(&cp, c);
|
|
192
|
+
PQCLEAN_MLDSA65_CLEAN_polyvec_matrix_expand(mat, rho);
|
|
193
|
+
|
|
194
|
+
PQCLEAN_MLDSA65_CLEAN_polyvecl_ntt(&z);
|
|
195
|
+
PQCLEAN_MLDSA65_CLEAN_polyvec_matrix_pointwise_montgomery(&w1, mat, &z);
|
|
196
|
+
|
|
197
|
+
PQCLEAN_MLDSA65_CLEAN_poly_ntt(&cp);
|
|
198
|
+
PQCLEAN_MLDSA65_CLEAN_polyveck_shiftl(&t1);
|
|
199
|
+
PQCLEAN_MLDSA65_CLEAN_polyveck_ntt(&t1);
|
|
200
|
+
PQCLEAN_MLDSA65_CLEAN_polyveck_pointwise_poly_montgomery(&t1, &cp, &t1);
|
|
201
|
+
|
|
202
|
+
PQCLEAN_MLDSA65_CLEAN_polyveck_sub(&w1, &w1, &t1);
|
|
203
|
+
PQCLEAN_MLDSA65_CLEAN_polyveck_reduce(&w1);
|
|
204
|
+
PQCLEAN_MLDSA65_CLEAN_polyveck_invntt_tomont(&w1);
|
|
205
|
+
|
|
206
|
+
PQCLEAN_MLDSA65_CLEAN_polyveck_caddq(&w1);
|
|
207
|
+
PQCLEAN_MLDSA65_CLEAN_polyveck_use_hint(&w1, &w1, &h);
|
|
208
|
+
PQCLEAN_MLDSA65_CLEAN_polyveck_pack_w1(buf, &w1);
|
|
209
|
+
|
|
210
|
+
shake256_inc_init(&state);
|
|
211
|
+
shake256_inc_absorb(&state, mu, CRHBYTES);
|
|
212
|
+
shake256_inc_absorb(&state, buf, K * POLYW1_PACKEDBYTES);
|
|
213
|
+
shake256_inc_finalize(&state);
|
|
214
|
+
shake256_inc_squeeze(c2, CTILDEBYTES, &state);
|
|
215
|
+
shake256_inc_ctx_release(&state);
|
|
216
|
+
|
|
217
|
+
for (i = 0; i < CTILDEBYTES; ++i) {
|
|
218
|
+
if (c[i] != c2[i]) {
|
|
219
|
+
return PQ_ERROR_VERIFY;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return PQ_SUCCESS;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
void *pq_mu_builder_new(void) {
|
|
227
|
+
shake256incctx *state = (shake256incctx *)malloc(sizeof(shake256incctx));
|
|
228
|
+
if (state == NULL) {
|
|
229
|
+
return NULL;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
shake256_inc_init(state);
|
|
233
|
+
return state;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
int pq_mu_builder_init(void *state_ptr, const uint8_t *tr, const uint8_t *ctx, size_t ctxlen) {
|
|
237
|
+
if (state_ptr == NULL || tr == NULL) {
|
|
238
|
+
return PQ_ERROR_BUFFER;
|
|
239
|
+
}
|
|
240
|
+
if (ctxlen > 255) {
|
|
241
|
+
return PQ_ERROR_BUFFER;
|
|
242
|
+
}
|
|
243
|
+
if (ctxlen > 0 && ctx == NULL) {
|
|
244
|
+
return PQ_ERROR_BUFFER;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
shake256incctx *state = (shake256incctx *)state_ptr;
|
|
248
|
+
|
|
249
|
+
uint8_t prefix[2];
|
|
250
|
+
prefix[0] = 0x00;
|
|
251
|
+
prefix[1] = (uint8_t)ctxlen;
|
|
252
|
+
|
|
253
|
+
shake256_inc_absorb(state, tr, TRBYTES);
|
|
254
|
+
shake256_inc_absorb(state, prefix, sizeof(prefix));
|
|
255
|
+
if (ctxlen > 0) {
|
|
256
|
+
shake256_inc_absorb(state, ctx, ctxlen);
|
|
257
|
+
}
|
|
258
|
+
return PQ_SUCCESS;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
int pq_mu_builder_absorb(void *state_ptr, const uint8_t *chunk, size_t chunk_len) {
|
|
262
|
+
if (state_ptr == NULL) {
|
|
263
|
+
return PQ_ERROR_BUFFER;
|
|
264
|
+
}
|
|
265
|
+
if (chunk_len == 0) {
|
|
266
|
+
return PQ_SUCCESS;
|
|
267
|
+
}
|
|
268
|
+
if (chunk == NULL) {
|
|
269
|
+
return PQ_ERROR_BUFFER;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
shake256incctx *state = (shake256incctx *)state_ptr;
|
|
273
|
+
shake256_inc_absorb(state, chunk, chunk_len);
|
|
274
|
+
return PQ_SUCCESS;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
int pq_mu_builder_finalize(void *state_ptr, uint8_t *mu_out) {
|
|
278
|
+
if (state_ptr == NULL || mu_out == NULL) {
|
|
279
|
+
return PQ_ERROR_BUFFER;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
shake256incctx *state = (shake256incctx *)state_ptr;
|
|
283
|
+
shake256_inc_finalize(state);
|
|
284
|
+
shake256_inc_squeeze(mu_out, CRHBYTES, state);
|
|
285
|
+
shake256_inc_ctx_release(state);
|
|
286
|
+
free(state);
|
|
287
|
+
return PQ_SUCCESS;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
void pq_mu_builder_release(void *state_ptr) {
|
|
291
|
+
if (state_ptr == NULL) {
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
shake256incctx *state = (shake256incctx *)state_ptr;
|
|
295
|
+
shake256_inc_ctx_release(state);
|
|
296
|
+
free(state);
|
|
297
|
+
}
|
|
@@ -8,6 +8,10 @@
|
|
|
8
8
|
|
|
9
9
|
#include "pqcrypto_secure.h"
|
|
10
10
|
|
|
11
|
+
#ifndef RB_NOGVL_OFFLOAD_SAFE
|
|
12
|
+
#define RB_NOGVL_OFFLOAD_SAFE 0
|
|
13
|
+
#endif
|
|
14
|
+
|
|
11
15
|
typedef struct {
|
|
12
16
|
int result;
|
|
13
17
|
uint8_t *public_key;
|
|
@@ -219,6 +223,10 @@ static void pq_wipe_and_free(uint8_t *buffer, size_t len) {
|
|
|
219
223
|
}
|
|
220
224
|
}
|
|
221
225
|
|
|
226
|
+
static void pq_free_buffer(uint8_t *buffer) {
|
|
227
|
+
free(buffer);
|
|
228
|
+
}
|
|
229
|
+
|
|
222
230
|
static void pq_validate_bytes_argument(VALUE value, size_t expected_len, const char *what) {
|
|
223
231
|
StringValue(value);
|
|
224
232
|
if ((size_t)RSTRING_LEN(value) != expected_len) {
|
|
@@ -624,17 +632,17 @@ static VALUE pqcrypto__test_sign_from_seed(VALUE self, VALUE message, VALUE secr
|
|
|
624
632
|
|
|
625
633
|
rb_thread_call_without_gvl(pq_testing_sign_nogvl, &call, NULL, NULL);
|
|
626
634
|
|
|
627
|
-
|
|
635
|
+
pq_free_buffer(call.message);
|
|
628
636
|
pq_wipe_and_free((uint8_t *)call.secret_key, secret_key_len);
|
|
629
637
|
pq_wipe_and_free((uint8_t *)call.seed, call.seed_len);
|
|
630
638
|
|
|
631
639
|
if (call.result != PQ_SUCCESS) {
|
|
632
|
-
|
|
640
|
+
pq_free_buffer(call.signature);
|
|
633
641
|
pq_raise_general_error(call.result);
|
|
634
642
|
}
|
|
635
643
|
|
|
636
644
|
VALUE result = pq_string_from_buffer(call.signature, call.signature_len);
|
|
637
|
-
|
|
645
|
+
pq_free_buffer(call.signature);
|
|
638
646
|
return result;
|
|
639
647
|
}
|
|
640
648
|
|
|
@@ -657,16 +665,16 @@ static VALUE pqcrypto_sign(VALUE self, VALUE message, VALUE secret_key) {
|
|
|
657
665
|
|
|
658
666
|
rb_nogvl(pq_sign_nogvl, &call, NULL, NULL, RB_NOGVL_OFFLOAD_SAFE);
|
|
659
667
|
|
|
660
|
-
|
|
668
|
+
pq_free_buffer(call.message);
|
|
661
669
|
pq_wipe_and_free((uint8_t *)call.secret_key, secret_key_len);
|
|
662
670
|
|
|
663
671
|
if (call.result != PQ_SUCCESS) {
|
|
664
|
-
|
|
672
|
+
pq_free_buffer(call.signature);
|
|
665
673
|
pq_raise_general_error(call.result);
|
|
666
674
|
}
|
|
667
675
|
|
|
668
676
|
VALUE result = pq_string_from_buffer(call.signature, call.signature_len);
|
|
669
|
-
|
|
677
|
+
pq_free_buffer(call.signature);
|
|
670
678
|
return result;
|
|
671
679
|
}
|
|
672
680
|
|
|
@@ -685,9 +693,9 @@ static VALUE pqcrypto_verify(VALUE self, VALUE message, VALUE signature, VALUE p
|
|
|
685
693
|
|
|
686
694
|
rb_nogvl(pq_verify_nogvl, &call, NULL, NULL, RB_NOGVL_OFFLOAD_SAFE);
|
|
687
695
|
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
696
|
+
pq_free_buffer(call.message);
|
|
697
|
+
pq_free_buffer((uint8_t *)call.public_key);
|
|
698
|
+
pq_free_buffer((uint8_t *)call.signature);
|
|
691
699
|
|
|
692
700
|
if (call.result == PQ_SUCCESS) {
|
|
693
701
|
return Qtrue;
|
|
@@ -727,6 +735,282 @@ static VALUE pqcrypto_version(VALUE self) {
|
|
|
727
735
|
return rb_str_new_cstr(pq_version());
|
|
728
736
|
}
|
|
729
737
|
|
|
738
|
+
typedef struct {
|
|
739
|
+
void *builder;
|
|
740
|
+
} mu_builder_wrapper_t;
|
|
741
|
+
|
|
742
|
+
typedef struct {
|
|
743
|
+
int result;
|
|
744
|
+
void *builder;
|
|
745
|
+
const uint8_t *chunk;
|
|
746
|
+
size_t chunk_len;
|
|
747
|
+
} mu_absorb_call_t;
|
|
748
|
+
|
|
749
|
+
typedef struct {
|
|
750
|
+
int result;
|
|
751
|
+
void *builder;
|
|
752
|
+
uint8_t *mu_out;
|
|
753
|
+
} mu_finalize_call_t;
|
|
754
|
+
|
|
755
|
+
typedef struct {
|
|
756
|
+
int result;
|
|
757
|
+
uint8_t *signature;
|
|
758
|
+
size_t signature_len;
|
|
759
|
+
const uint8_t *mu;
|
|
760
|
+
const uint8_t *secret_key;
|
|
761
|
+
} sign_mu_call_t;
|
|
762
|
+
|
|
763
|
+
typedef struct {
|
|
764
|
+
int result;
|
|
765
|
+
const uint8_t *signature;
|
|
766
|
+
size_t signature_len;
|
|
767
|
+
const uint8_t *mu;
|
|
768
|
+
const uint8_t *public_key;
|
|
769
|
+
} verify_mu_call_t;
|
|
770
|
+
|
|
771
|
+
static void mu_builder_wrapper_free(void *ptr) {
|
|
772
|
+
mu_builder_wrapper_t *wrapper = (mu_builder_wrapper_t *)ptr;
|
|
773
|
+
if (wrapper == NULL) {
|
|
774
|
+
return;
|
|
775
|
+
}
|
|
776
|
+
if (wrapper->builder != NULL) {
|
|
777
|
+
pq_mu_builder_release(wrapper->builder);
|
|
778
|
+
wrapper->builder = NULL;
|
|
779
|
+
}
|
|
780
|
+
xfree(wrapper);
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
static size_t mu_builder_wrapper_size(const void *ptr) {
|
|
784
|
+
(void)ptr;
|
|
785
|
+
return sizeof(mu_builder_wrapper_t);
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
static const rb_data_type_t mu_builder_data_type = {
|
|
789
|
+
"PQCrypto::MLDSA::MuBuilder",
|
|
790
|
+
{NULL, mu_builder_wrapper_free, mu_builder_wrapper_size},
|
|
791
|
+
NULL,
|
|
792
|
+
NULL,
|
|
793
|
+
RUBY_TYPED_FREE_IMMEDIATELY};
|
|
794
|
+
|
|
795
|
+
static mu_builder_wrapper_t *mu_builder_unwrap(VALUE obj) {
|
|
796
|
+
mu_builder_wrapper_t *wrapper;
|
|
797
|
+
TypedData_Get_Struct(obj, mu_builder_wrapper_t, &mu_builder_data_type, wrapper);
|
|
798
|
+
if (wrapper == NULL || wrapper->builder == NULL) {
|
|
799
|
+
rb_raise(ePQCryptoError, "mu builder used after release");
|
|
800
|
+
}
|
|
801
|
+
return wrapper;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
static VALUE pqcrypto__native_mldsa_extract_tr(VALUE self, VALUE secret_key) {
|
|
805
|
+
(void)self;
|
|
806
|
+
pq_validate_bytes_argument(secret_key, PQ_MLDSA_SECRETKEYBYTES, "secret key");
|
|
807
|
+
|
|
808
|
+
uint8_t tr[PQ_MLDSA_TRBYTES];
|
|
809
|
+
int rc = pq_mldsa_extract_tr_from_secret_key(tr, (const uint8_t *)RSTRING_PTR(secret_key));
|
|
810
|
+
if (rc != PQ_SUCCESS) {
|
|
811
|
+
pq_secure_wipe(tr, sizeof(tr));
|
|
812
|
+
pq_raise_general_error(rc);
|
|
813
|
+
}
|
|
814
|
+
VALUE result = pq_string_from_buffer(tr, sizeof(tr));
|
|
815
|
+
pq_secure_wipe(tr, sizeof(tr));
|
|
816
|
+
return result;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
static VALUE pqcrypto__native_mldsa_compute_tr(VALUE self, VALUE public_key) {
|
|
820
|
+
(void)self;
|
|
821
|
+
pq_validate_bytes_argument(public_key, PQ_MLDSA_PUBLICKEYBYTES, "public key");
|
|
822
|
+
|
|
823
|
+
uint8_t tr[PQ_MLDSA_TRBYTES];
|
|
824
|
+
int rc = pq_mldsa_compute_tr_from_public_key(tr, (const uint8_t *)RSTRING_PTR(public_key));
|
|
825
|
+
if (rc != PQ_SUCCESS) {
|
|
826
|
+
pq_raise_general_error(rc);
|
|
827
|
+
}
|
|
828
|
+
return pq_string_from_buffer(tr, sizeof(tr));
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
static VALUE pqcrypto__native_mldsa_mu_builder_new(VALUE self, VALUE tr, VALUE ctx) {
|
|
832
|
+
(void)self;
|
|
833
|
+
pq_validate_bytes_argument(tr, PQ_MLDSA_TRBYTES, "tr");
|
|
834
|
+
StringValue(ctx);
|
|
835
|
+
|
|
836
|
+
size_t ctxlen = (size_t)RSTRING_LEN(ctx);
|
|
837
|
+
if (ctxlen > 255) {
|
|
838
|
+
rb_raise(rb_eArgError, "ML-DSA context length must be <= 255 bytes");
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
void *builder = pq_mu_builder_new();
|
|
842
|
+
if (builder == NULL) {
|
|
843
|
+
rb_raise(rb_eNoMemError, "Memory allocation failed (mu builder)");
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
int rc = pq_mu_builder_init(builder, (const uint8_t *)RSTRING_PTR(tr),
|
|
847
|
+
(const uint8_t *)RSTRING_PTR(ctx), ctxlen);
|
|
848
|
+
if (rc != PQ_SUCCESS) {
|
|
849
|
+
pq_mu_builder_release(builder);
|
|
850
|
+
pq_raise_general_error(rc);
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
mu_builder_wrapper_t *wrapper;
|
|
854
|
+
VALUE obj =
|
|
855
|
+
TypedData_Make_Struct(rb_cObject, mu_builder_wrapper_t, &mu_builder_data_type, wrapper);
|
|
856
|
+
wrapper->builder = builder;
|
|
857
|
+
return obj;
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
static void *pq_mu_absorb_nogvl(void *arg) {
|
|
861
|
+
mu_absorb_call_t *call = (mu_absorb_call_t *)arg;
|
|
862
|
+
call->result = pq_mu_builder_absorb(call->builder, call->chunk, call->chunk_len);
|
|
863
|
+
return NULL;
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
static VALUE pqcrypto__native_mldsa_mu_builder_update(VALUE self, VALUE builder_obj, VALUE chunk) {
|
|
867
|
+
(void)self;
|
|
868
|
+
mu_builder_wrapper_t *wrapper = mu_builder_unwrap(builder_obj);
|
|
869
|
+
StringValue(chunk);
|
|
870
|
+
|
|
871
|
+
size_t chunk_len = (size_t)RSTRING_LEN(chunk);
|
|
872
|
+
if (chunk_len == 0) {
|
|
873
|
+
return Qnil;
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
uint8_t *copy = pq_alloc_buffer(chunk_len);
|
|
877
|
+
memcpy(copy, RSTRING_PTR(chunk), chunk_len);
|
|
878
|
+
|
|
879
|
+
mu_absorb_call_t call = {0};
|
|
880
|
+
call.builder = wrapper->builder;
|
|
881
|
+
call.chunk = copy;
|
|
882
|
+
call.chunk_len = chunk_len;
|
|
883
|
+
|
|
884
|
+
rb_nogvl(pq_mu_absorb_nogvl, &call, NULL, NULL, RB_NOGVL_OFFLOAD_SAFE);
|
|
885
|
+
free(copy);
|
|
886
|
+
|
|
887
|
+
if (call.result != PQ_SUCCESS) {
|
|
888
|
+
pq_raise_general_error(call.result);
|
|
889
|
+
}
|
|
890
|
+
return Qnil;
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
static void *pq_mu_finalize_nogvl(void *arg) {
|
|
894
|
+
mu_finalize_call_t *call = (mu_finalize_call_t *)arg;
|
|
895
|
+
call->result = pq_mu_builder_finalize(call->builder, call->mu_out);
|
|
896
|
+
return NULL;
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
static VALUE pqcrypto__native_mldsa_mu_builder_finalize(VALUE self, VALUE builder_obj) {
|
|
900
|
+
(void)self;
|
|
901
|
+
mu_builder_wrapper_t *wrapper = mu_builder_unwrap(builder_obj);
|
|
902
|
+
|
|
903
|
+
uint8_t mu[PQ_MLDSA_MUBYTES];
|
|
904
|
+
|
|
905
|
+
mu_finalize_call_t call = {0};
|
|
906
|
+
call.builder = wrapper->builder;
|
|
907
|
+
call.mu_out = mu;
|
|
908
|
+
|
|
909
|
+
rb_nogvl(pq_mu_finalize_nogvl, &call, NULL, NULL, RB_NOGVL_OFFLOAD_SAFE);
|
|
910
|
+
|
|
911
|
+
if (call.result != PQ_SUCCESS) {
|
|
912
|
+
pq_mu_builder_release(wrapper->builder);
|
|
913
|
+
}
|
|
914
|
+
wrapper->builder = NULL;
|
|
915
|
+
|
|
916
|
+
if (call.result != PQ_SUCCESS) {
|
|
917
|
+
pq_secure_wipe(mu, sizeof(mu));
|
|
918
|
+
pq_raise_general_error(call.result);
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
VALUE result = pq_string_from_buffer(mu, sizeof(mu));
|
|
922
|
+
pq_secure_wipe(mu, sizeof(mu));
|
|
923
|
+
return result;
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
static VALUE pqcrypto__native_mldsa_mu_builder_release(VALUE self, VALUE builder_obj) {
|
|
927
|
+
(void)self;
|
|
928
|
+
mu_builder_wrapper_t *wrapper;
|
|
929
|
+
TypedData_Get_Struct(builder_obj, mu_builder_wrapper_t, &mu_builder_data_type, wrapper);
|
|
930
|
+
if (wrapper != NULL && wrapper->builder != NULL) {
|
|
931
|
+
pq_mu_builder_release(wrapper->builder);
|
|
932
|
+
wrapper->builder = NULL;
|
|
933
|
+
}
|
|
934
|
+
return Qnil;
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
static void *pq_sign_mu_nogvl(void *arg) {
|
|
938
|
+
sign_mu_call_t *call = (sign_mu_call_t *)arg;
|
|
939
|
+
call->result = pq_sign_mu(call->signature, &call->signature_len, call->mu, call->secret_key);
|
|
940
|
+
return NULL;
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
static VALUE pqcrypto__native_mldsa_sign_mu(VALUE self, VALUE mu, VALUE secret_key) {
|
|
944
|
+
(void)self;
|
|
945
|
+
pq_validate_bytes_argument(mu, PQ_MLDSA_MUBYTES, "mu");
|
|
946
|
+
pq_validate_bytes_argument(secret_key, PQ_MLDSA_SECRETKEYBYTES, "secret key");
|
|
947
|
+
|
|
948
|
+
sign_mu_call_t call = {0};
|
|
949
|
+
size_t secret_key_len = 0;
|
|
950
|
+
size_t mu_len = 0;
|
|
951
|
+
uint8_t *mu_copy = pq_copy_ruby_string(mu, &mu_len);
|
|
952
|
+
uint8_t *sk_copy = pq_copy_ruby_string(secret_key, &secret_key_len);
|
|
953
|
+
|
|
954
|
+
call.mu = mu_copy;
|
|
955
|
+
call.secret_key = sk_copy;
|
|
956
|
+
call.signature_len = PQ_MLDSA_BYTES;
|
|
957
|
+
call.signature = pq_alloc_buffer(PQ_MLDSA_BYTES);
|
|
958
|
+
|
|
959
|
+
rb_nogvl(pq_sign_mu_nogvl, &call, NULL, NULL, RB_NOGVL_OFFLOAD_SAFE);
|
|
960
|
+
|
|
961
|
+
pq_wipe_and_free(mu_copy, mu_len);
|
|
962
|
+
pq_wipe_and_free(sk_copy, secret_key_len);
|
|
963
|
+
|
|
964
|
+
if (call.result != PQ_SUCCESS) {
|
|
965
|
+
pq_free_buffer(call.signature);
|
|
966
|
+
pq_raise_general_error(call.result);
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
VALUE result = pq_string_from_buffer(call.signature, call.signature_len);
|
|
970
|
+
pq_free_buffer(call.signature);
|
|
971
|
+
return result;
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
static void *pq_verify_mu_nogvl(void *arg) {
|
|
975
|
+
verify_mu_call_t *call = (verify_mu_call_t *)arg;
|
|
976
|
+
call->result = pq_verify_mu(call->signature, call->signature_len, call->mu, call->public_key);
|
|
977
|
+
return NULL;
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
static VALUE pqcrypto__native_mldsa_verify_mu(VALUE self, VALUE mu, VALUE signature,
|
|
981
|
+
VALUE public_key) {
|
|
982
|
+
(void)self;
|
|
983
|
+
StringValue(signature);
|
|
984
|
+
pq_validate_bytes_argument(mu, PQ_MLDSA_MUBYTES, "mu");
|
|
985
|
+
pq_validate_bytes_argument(public_key, PQ_MLDSA_PUBLICKEYBYTES, "public key");
|
|
986
|
+
|
|
987
|
+
verify_mu_call_t call = {0};
|
|
988
|
+
size_t public_key_len = 0;
|
|
989
|
+
size_t signature_len = 0;
|
|
990
|
+
size_t mu_len = 0;
|
|
991
|
+
uint8_t *mu_copy = pq_copy_ruby_string(mu, &mu_len);
|
|
992
|
+
uint8_t *pk_copy = pq_copy_ruby_string(public_key, &public_key_len);
|
|
993
|
+
uint8_t *sig_copy = pq_copy_ruby_string(signature, &signature_len);
|
|
994
|
+
|
|
995
|
+
call.mu = mu_copy;
|
|
996
|
+
call.public_key = pk_copy;
|
|
997
|
+
call.signature = sig_copy;
|
|
998
|
+
call.signature_len = signature_len;
|
|
999
|
+
|
|
1000
|
+
rb_nogvl(pq_verify_mu_nogvl, &call, NULL, NULL, RB_NOGVL_OFFLOAD_SAFE);
|
|
1001
|
+
pq_wipe_and_free(mu_copy, mu_len);
|
|
1002
|
+
pq_free_buffer(pk_copy);
|
|
1003
|
+
pq_free_buffer(sig_copy);
|
|
1004
|
+
|
|
1005
|
+
if (call.result == PQ_SUCCESS) {
|
|
1006
|
+
return Qtrue;
|
|
1007
|
+
}
|
|
1008
|
+
if (call.result == PQ_ERROR_VERIFY) {
|
|
1009
|
+
return Qfalse;
|
|
1010
|
+
}
|
|
1011
|
+
pq_raise_general_error(call.result);
|
|
1012
|
+
}
|
|
1013
|
+
|
|
730
1014
|
static void define_constants(void) {
|
|
731
1015
|
rb_define_const(mPQCrypto, "ML_KEM_PUBLIC_KEY_BYTES", INT2NUM(PQ_MLKEM_PUBLICKEYBYTES));
|
|
732
1016
|
rb_define_const(mPQCrypto, "ML_KEM_SECRET_KEY_BYTES", INT2NUM(PQ_MLKEM_SECRETKEYBYTES));
|
|
@@ -830,6 +1114,22 @@ void Init_pqcrypto_secure(void) {
|
|
|
830
1114
|
pqcrypto_secret_key_from_pqc_container_der, 1);
|
|
831
1115
|
rb_define_module_function(mPQCrypto, "secret_key_from_pqc_container_pem",
|
|
832
1116
|
pqcrypto_secret_key_from_pqc_container_pem, 1);
|
|
1117
|
+
rb_define_module_function(mPQCrypto, "_native_mldsa_extract_tr",
|
|
1118
|
+
pqcrypto__native_mldsa_extract_tr, 1);
|
|
1119
|
+
rb_define_module_function(mPQCrypto, "_native_mldsa_compute_tr",
|
|
1120
|
+
pqcrypto__native_mldsa_compute_tr, 1);
|
|
1121
|
+
rb_define_module_function(mPQCrypto, "_native_mldsa_mu_builder_new",
|
|
1122
|
+
pqcrypto__native_mldsa_mu_builder_new, 2);
|
|
1123
|
+
rb_define_module_function(mPQCrypto, "_native_mldsa_mu_builder_update",
|
|
1124
|
+
pqcrypto__native_mldsa_mu_builder_update, 2);
|
|
1125
|
+
rb_define_module_function(mPQCrypto, "_native_mldsa_mu_builder_finalize",
|
|
1126
|
+
pqcrypto__native_mldsa_mu_builder_finalize, 1);
|
|
1127
|
+
rb_define_module_function(mPQCrypto, "_native_mldsa_mu_builder_release",
|
|
1128
|
+
pqcrypto__native_mldsa_mu_builder_release, 1);
|
|
1129
|
+
rb_define_module_function(mPQCrypto, "_native_mldsa_sign_mu", pqcrypto__native_mldsa_sign_mu,
|
|
1130
|
+
2);
|
|
1131
|
+
rb_define_module_function(mPQCrypto, "_native_mldsa_verify_mu",
|
|
1132
|
+
pqcrypto__native_mldsa_verify_mu, 3);
|
|
833
1133
|
|
|
834
1134
|
define_constants();
|
|
835
1135
|
}
|