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.
@@ -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
- pq_wipe_and_free(call.message, call.message_len);
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
- pq_wipe_and_free(call.signature, PQ_MLDSA_BYTES);
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
- pq_wipe_and_free(call.signature, PQ_MLDSA_BYTES);
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
- pq_wipe_and_free(call.message, call.message_len);
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
- pq_wipe_and_free(call.signature, PQ_MLDSA_BYTES);
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
- free(call.signature);
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
- pq_wipe_and_free(call.message, call.message_len);
689
- pq_wipe_and_free((uint8_t *)call.public_key, public_key_len);
690
- pq_wipe_and_free((uint8_t *)call.signature, signature_len);
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
  }