pq_crypto 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/ci.yml +37 -0
  3. data/CHANGELOG.md +29 -0
  4. data/GET_STARTED.md +65 -0
  5. data/LICENSE.txt +21 -0
  6. data/README.md +135 -0
  7. data/SECURITY.md +57 -0
  8. data/ext/pqcrypto/extconf.rb +157 -0
  9. data/ext/pqcrypto/mldsa_api.h +51 -0
  10. data/ext/pqcrypto/mlkem_api.h +21 -0
  11. data/ext/pqcrypto/pqcrypto_ruby_secure.c +889 -0
  12. data/ext/pqcrypto/pqcrypto_secure.c +1178 -0
  13. data/ext/pqcrypto/pqcrypto_secure.h +135 -0
  14. data/ext/pqcrypto/vendor/.vendored +5 -0
  15. data/ext/pqcrypto/vendor/pqclean/common/aes.c +639 -0
  16. data/ext/pqcrypto/vendor/pqclean/common/aes.h +64 -0
  17. data/ext/pqcrypto/vendor/pqclean/common/compat.h +73 -0
  18. data/ext/pqcrypto/vendor/pqclean/common/crypto_declassify.h +7 -0
  19. data/ext/pqcrypto/vendor/pqclean/common/fips202.c +928 -0
  20. data/ext/pqcrypto/vendor/pqclean/common/fips202.h +166 -0
  21. data/ext/pqcrypto/vendor/pqclean/common/keccak2x/feat.S +168 -0
  22. data/ext/pqcrypto/vendor/pqclean/common/keccak2x/fips202x2.c +684 -0
  23. data/ext/pqcrypto/vendor/pqclean/common/keccak2x/fips202x2.h +60 -0
  24. data/ext/pqcrypto/vendor/pqclean/common/keccak4x/KeccakP-1600-times4-SIMD256.c +1028 -0
  25. data/ext/pqcrypto/vendor/pqclean/common/keccak4x/KeccakP-1600-times4-SnP.h +50 -0
  26. data/ext/pqcrypto/vendor/pqclean/common/keccak4x/KeccakP-1600-unrolling.macros +198 -0
  27. data/ext/pqcrypto/vendor/pqclean/common/keccak4x/Makefile +8 -0
  28. data/ext/pqcrypto/vendor/pqclean/common/keccak4x/Makefile.Microsoft_nmake +8 -0
  29. data/ext/pqcrypto/vendor/pqclean/common/keccak4x/SIMD256-config.h +3 -0
  30. data/ext/pqcrypto/vendor/pqclean/common/keccak4x/align.h +34 -0
  31. data/ext/pqcrypto/vendor/pqclean/common/keccak4x/brg_endian.h +142 -0
  32. data/ext/pqcrypto/vendor/pqclean/common/nistseedexpander.c +101 -0
  33. data/ext/pqcrypto/vendor/pqclean/common/nistseedexpander.h +39 -0
  34. data/ext/pqcrypto/vendor/pqclean/common/randombytes.c +355 -0
  35. data/ext/pqcrypto/vendor/pqclean/common/randombytes.h +27 -0
  36. data/ext/pqcrypto/vendor/pqclean/common/sha2.c +769 -0
  37. data/ext/pqcrypto/vendor/pqclean/common/sha2.h +173 -0
  38. data/ext/pqcrypto/vendor/pqclean/common/sp800-185.c +156 -0
  39. data/ext/pqcrypto/vendor/pqclean/common/sp800-185.h +27 -0
  40. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/LICENSE +5 -0
  41. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/Makefile +19 -0
  42. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/Makefile.Microsoft_nmake +23 -0
  43. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/api.h +18 -0
  44. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/cbd.c +83 -0
  45. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/cbd.h +11 -0
  46. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/indcpa.c +327 -0
  47. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/indcpa.h +22 -0
  48. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/kem.c +164 -0
  49. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/kem.h +23 -0
  50. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/ntt.c +146 -0
  51. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/ntt.h +14 -0
  52. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/params.h +36 -0
  53. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/poly.c +299 -0
  54. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/poly.h +37 -0
  55. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/polyvec.c +188 -0
  56. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/polyvec.h +26 -0
  57. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/reduce.c +41 -0
  58. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/reduce.h +13 -0
  59. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/symmetric-shake.c +71 -0
  60. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/symmetric.h +30 -0
  61. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/verify.c +67 -0
  62. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/verify.h +13 -0
  63. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-65/clean/LICENSE +5 -0
  64. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-65/clean/Makefile +19 -0
  65. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-65/clean/Makefile.Microsoft_nmake +23 -0
  66. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-65/clean/api.h +50 -0
  67. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-65/clean/ntt.c +98 -0
  68. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-65/clean/ntt.h +10 -0
  69. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-65/clean/packing.c +261 -0
  70. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-65/clean/packing.h +31 -0
  71. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-65/clean/params.h +44 -0
  72. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-65/clean/poly.c +799 -0
  73. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-65/clean/poly.h +52 -0
  74. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-65/clean/polyvec.c +415 -0
  75. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-65/clean/polyvec.h +65 -0
  76. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-65/clean/reduce.c +69 -0
  77. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-65/clean/reduce.h +17 -0
  78. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-65/clean/rounding.c +92 -0
  79. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-65/clean/rounding.h +14 -0
  80. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-65/clean/sign.c +407 -0
  81. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-65/clean/sign.h +47 -0
  82. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-65/clean/symmetric-shake.c +26 -0
  83. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-65/clean/symmetric.h +34 -0
  84. data/lib/pq_crypto/errors.rb +10 -0
  85. data/lib/pq_crypto/hybrid_kem.rb +106 -0
  86. data/lib/pq_crypto/kem.rb +199 -0
  87. data/lib/pq_crypto/serialization.rb +102 -0
  88. data/lib/pq_crypto/signature.rb +198 -0
  89. data/lib/pq_crypto/version.rb +5 -0
  90. data/lib/pq_crypto.rb +177 -0
  91. data/lib/pqcrypto.rb +3 -0
  92. data/script/vendor_libs.rb +199 -0
  93. metadata +195 -0
@@ -0,0 +1,889 @@
1
+ #include <ruby.h>
2
+ #include <ruby/thread.h>
3
+ #include <ruby/encoding.h>
4
+ #include <stdlib.h>
5
+ #include <string.h>
6
+
7
+ #include "pqcrypto_secure.h"
8
+
9
+ typedef struct {
10
+ int result;
11
+ uint8_t *public_key;
12
+ uint8_t *secret_key;
13
+ const uint8_t *seed;
14
+ size_t seed_len;
15
+ } kem_keypair_call_t;
16
+
17
+ typedef struct {
18
+ int result;
19
+ uint8_t *ciphertext;
20
+ uint8_t *shared_secret;
21
+ const uint8_t *public_key;
22
+ const uint8_t *seed;
23
+ size_t seed_len;
24
+ } kem_encapsulate_call_t;
25
+
26
+ typedef struct {
27
+ int result;
28
+ uint8_t *shared_secret;
29
+ const uint8_t *ciphertext;
30
+ const uint8_t *secret_key;
31
+ } kem_decapsulate_call_t;
32
+
33
+ typedef struct {
34
+ int result;
35
+ uint8_t *public_key;
36
+ uint8_t *secret_key;
37
+ const uint8_t *seed;
38
+ size_t seed_len;
39
+ } sign_keypair_call_t;
40
+
41
+ typedef struct {
42
+ int result;
43
+ uint8_t *signature;
44
+ size_t signature_len;
45
+ uint8_t *message;
46
+ size_t message_len;
47
+ const uint8_t *secret_key;
48
+ const uint8_t *seed;
49
+ size_t seed_len;
50
+ } sign_call_t;
51
+
52
+ typedef struct {
53
+ int result;
54
+ const uint8_t *signature;
55
+ size_t signature_len;
56
+ uint8_t *message;
57
+ size_t message_len;
58
+ const uint8_t *public_key;
59
+ } verify_call_t;
60
+
61
+ static VALUE mPQCrypto;
62
+ static VALUE ePQCryptoError;
63
+ static VALUE ePQCryptoVerificationError;
64
+
65
+ static const char *pq_algorithm_symbol_to_cstr(VALUE algorithm) {
66
+ ID id;
67
+ if (SYMBOL_P(algorithm)) {
68
+ id = SYM2ID(algorithm);
69
+ } else {
70
+ VALUE str = StringValue(algorithm);
71
+ id = rb_intern_str(str);
72
+ }
73
+ if (id == rb_intern("ml_kem_768"))
74
+ return "ml_kem_768";
75
+ if (id == rb_intern("ml_kem_768_x25519_hkdf_sha256"))
76
+ return "ml_kem_768_x25519_hkdf_sha256";
77
+ if (id == rb_intern("ml_dsa_65"))
78
+ return "ml_dsa_65";
79
+ rb_raise(rb_eArgError, "Unsupported serialization algorithm");
80
+ }
81
+
82
+ static VALUE pq_algorithm_cstr_to_symbol(const char *algorithm) {
83
+ if (strcmp(algorithm, "ml_kem_768") == 0)
84
+ return ID2SYM(rb_intern("ml_kem_768"));
85
+ if (strcmp(algorithm, "ml_kem_768_x25519_hkdf_sha256") == 0)
86
+ return ID2SYM(rb_intern("ml_kem_768_x25519_hkdf_sha256"));
87
+ if (strcmp(algorithm, "ml_dsa_65") == 0)
88
+ return ID2SYM(rb_intern("ml_dsa_65"));
89
+ rb_raise(rb_eArgError, "Unsupported serialization algorithm");
90
+ }
91
+
92
+ static void *pq_ml_kem_keypair_nogvl(void *arg) {
93
+ kem_keypair_call_t *call = (kem_keypair_call_t *)arg;
94
+ call->result = pq_mlkem_keypair(call->public_key, call->secret_key);
95
+ return NULL;
96
+ }
97
+
98
+ static void *pq_ml_kem_encapsulate_nogvl(void *arg) {
99
+ kem_encapsulate_call_t *call = (kem_encapsulate_call_t *)arg;
100
+ call->result = pq_mlkem_encapsulate(call->ciphertext, call->shared_secret, call->public_key);
101
+ return NULL;
102
+ }
103
+
104
+ static void *pq_ml_kem_decapsulate_nogvl(void *arg) {
105
+ kem_decapsulate_call_t *call = (kem_decapsulate_call_t *)arg;
106
+ call->result = pq_mlkem_decapsulate(call->shared_secret, call->ciphertext, call->secret_key);
107
+ return NULL;
108
+ }
109
+
110
+ static void *pq_testing_ml_kem_keypair_nogvl(void *arg) {
111
+ kem_keypair_call_t *call = (kem_keypair_call_t *)arg;
112
+ call->result = pq_testing_mlkem_keypair_from_seed(call->public_key, call->secret_key,
113
+ call->seed, call->seed_len);
114
+ return NULL;
115
+ }
116
+
117
+ static void *pq_testing_ml_kem_encapsulate_nogvl(void *arg) {
118
+ kem_encapsulate_call_t *call = (kem_encapsulate_call_t *)arg;
119
+ call->result = pq_testing_mlkem_encapsulate_from_seed(
120
+ call->ciphertext, call->shared_secret, call->public_key, call->seed, call->seed_len);
121
+ return NULL;
122
+ }
123
+
124
+ static void *pq_hybrid_kem_keypair_nogvl(void *arg) {
125
+ kem_keypair_call_t *call = (kem_keypair_call_t *)arg;
126
+ call->result = pq_hybrid_kem_keypair(call->public_key, call->secret_key);
127
+ return NULL;
128
+ }
129
+
130
+ static void *pq_hybrid_kem_encapsulate_nogvl(void *arg) {
131
+ kem_encapsulate_call_t *call = (kem_encapsulate_call_t *)arg;
132
+ call->result =
133
+ pq_hybrid_kem_encapsulate(call->ciphertext, call->shared_secret, call->public_key);
134
+ return NULL;
135
+ }
136
+
137
+ static void *pq_hybrid_kem_decapsulate_nogvl(void *arg) {
138
+ kem_decapsulate_call_t *call = (kem_decapsulate_call_t *)arg;
139
+ call->result =
140
+ pq_hybrid_kem_decapsulate(call->shared_secret, call->ciphertext, call->secret_key);
141
+ return NULL;
142
+ }
143
+
144
+ static void *pq_sign_keypair_nogvl(void *arg) {
145
+ sign_keypair_call_t *call = (sign_keypair_call_t *)arg;
146
+ call->result = pq_sign_keypair(call->public_key, call->secret_key);
147
+ return NULL;
148
+ }
149
+
150
+ static void *pq_sign_nogvl(void *arg) {
151
+ sign_call_t *call = (sign_call_t *)arg;
152
+ call->result = pq_sign(call->signature, &call->signature_len, call->message, call->message_len,
153
+ call->secret_key);
154
+ return NULL;
155
+ }
156
+
157
+ static void *pq_testing_sign_keypair_nogvl(void *arg) {
158
+ sign_keypair_call_t *call = (sign_keypair_call_t *)arg;
159
+ call->result = pq_testing_mldsa_keypair_from_seed(call->public_key, call->secret_key,
160
+ call->seed, call->seed_len);
161
+ return NULL;
162
+ }
163
+
164
+ static void *pq_testing_sign_nogvl(void *arg) {
165
+ sign_call_t *call = (sign_call_t *)arg;
166
+ call->result = pq_testing_mldsa_sign_from_seed(call->signature, &call->signature_len,
167
+ call->message, call->message_len,
168
+ call->secret_key, call->seed, call->seed_len);
169
+ return NULL;
170
+ }
171
+
172
+ static void *pq_verify_nogvl(void *arg) {
173
+ verify_call_t *call = (verify_call_t *)arg;
174
+ call->result = pq_verify(call->signature, call->signature_len, call->message, call->message_len,
175
+ call->public_key);
176
+ return NULL;
177
+ }
178
+
179
+ static uint8_t *pq_alloc_buffer(size_t len) {
180
+ if (len == 0) {
181
+ return NULL;
182
+ }
183
+
184
+ uint8_t *buffer = malloc(len);
185
+ if (!buffer) {
186
+ rb_raise(rb_eNoMemError, "Memory allocation failed");
187
+ }
188
+
189
+ return buffer;
190
+ }
191
+
192
+ static uint8_t *pq_copy_ruby_string(VALUE string, size_t *len_out) {
193
+ StringValue(string);
194
+
195
+ size_t len = (size_t)RSTRING_LEN(string);
196
+ *len_out = len;
197
+
198
+ if (len == 0) {
199
+ return NULL;
200
+ }
201
+
202
+ uint8_t *copy = pq_alloc_buffer(len);
203
+ memcpy(copy, RSTRING_PTR(string), len);
204
+ return copy;
205
+ }
206
+
207
+ static VALUE pq_string_from_buffer(const uint8_t *buffer, size_t len) {
208
+ return rb_enc_str_new((const char *)buffer, (long)len, rb_ascii8bit_encoding());
209
+ }
210
+
211
+ static void pq_wipe_and_free(uint8_t *buffer, size_t len) {
212
+ if (buffer) {
213
+ pq_secure_wipe(buffer, len);
214
+ free(buffer);
215
+ }
216
+ }
217
+
218
+ __attribute__((noreturn)) static void pq_raise_general_error(int err) {
219
+ switch (err) {
220
+ case PQ_ERROR_KEYPAIR:
221
+ rb_raise(ePQCryptoError, "Keypair generation failed");
222
+ break;
223
+ case PQ_ERROR_ENCAPSULATE:
224
+ rb_raise(ePQCryptoError, "Encapsulation failed");
225
+ break;
226
+ case PQ_ERROR_DECAPSULATE:
227
+ rb_raise(ePQCryptoError, "Decapsulation failed");
228
+ break;
229
+ case PQ_ERROR_SIGN:
230
+ rb_raise(ePQCryptoError, "Signing failed");
231
+ break;
232
+ case PQ_ERROR_VERIFY:
233
+ rb_raise(ePQCryptoError, "Verification failed");
234
+ break;
235
+ case PQ_ERROR_RANDOM:
236
+ rb_raise(ePQCryptoError, "Random number generation failed");
237
+ break;
238
+ case PQ_ERROR_KDF:
239
+ rb_raise(ePQCryptoError, "Key derivation failed");
240
+ break;
241
+ case PQ_ERROR_BUFFER:
242
+ rb_raise(ePQCryptoError, "Buffer error");
243
+ break;
244
+ case PQ_ERROR_NOMEM:
245
+ rb_raise(rb_eNoMemError, "Memory allocation failed");
246
+ break;
247
+ case PQ_ERROR_OPENSSL:
248
+ rb_raise(ePQCryptoError, "OpenSSL error");
249
+ break;
250
+ default:
251
+ rb_raise(ePQCryptoError, "Unknown error: %d", err);
252
+ break;
253
+ }
254
+ }
255
+
256
+ __attribute__((noreturn)) static void pq_raise_verification_error(int err) {
257
+ switch (err) {
258
+ case PQ_ERROR_VERIFY:
259
+ rb_raise(ePQCryptoVerificationError, "Verification failed");
260
+ break;
261
+ default:
262
+ pq_raise_general_error(err);
263
+ }
264
+ }
265
+
266
+ static VALUE pqcrypto_ml_kem_keypair(VALUE self) {
267
+ (void)self;
268
+
269
+ kem_keypair_call_t call = {0};
270
+ call.public_key = pq_alloc_buffer(PQ_MLKEM_PUBLICKEYBYTES);
271
+ call.secret_key = pq_alloc_buffer(PQ_MLKEM_SECRETKEYBYTES);
272
+
273
+ rb_thread_call_without_gvl(pq_ml_kem_keypair_nogvl, &call, NULL, NULL);
274
+
275
+ if (call.result != PQ_SUCCESS) {
276
+ pq_wipe_and_free(call.secret_key, PQ_MLKEM_SECRETKEYBYTES);
277
+ free(call.public_key);
278
+ pq_raise_general_error(call.result);
279
+ }
280
+
281
+ VALUE result = rb_ary_new2(2);
282
+ rb_ary_push(result, pq_string_from_buffer(call.public_key, PQ_MLKEM_PUBLICKEYBYTES));
283
+ rb_ary_push(result, pq_string_from_buffer(call.secret_key, PQ_MLKEM_SECRETKEYBYTES));
284
+
285
+ free(call.public_key);
286
+ pq_wipe_and_free(call.secret_key, PQ_MLKEM_SECRETKEYBYTES);
287
+ return result;
288
+ }
289
+
290
+ static VALUE pqcrypto_ml_kem_encapsulate(VALUE self, VALUE public_key) {
291
+ (void)self;
292
+ StringValue(public_key);
293
+
294
+ if ((size_t)RSTRING_LEN(public_key) != PQ_MLKEM_PUBLICKEYBYTES) {
295
+ rb_raise(rb_eArgError, "Invalid public key length");
296
+ }
297
+
298
+ kem_encapsulate_call_t call = {0};
299
+ size_t public_key_len = 0;
300
+ call.public_key = pq_copy_ruby_string(public_key, &public_key_len);
301
+ call.ciphertext = pq_alloc_buffer(PQ_MLKEM_CIPHERTEXTBYTES);
302
+ call.shared_secret = pq_alloc_buffer(PQ_MLKEM_SHAREDSECRETBYTES);
303
+
304
+ rb_thread_call_without_gvl(pq_ml_kem_encapsulate_nogvl, &call, NULL, NULL);
305
+ pq_wipe_and_free((uint8_t *)call.public_key, public_key_len);
306
+
307
+ if (call.result != PQ_SUCCESS) {
308
+ pq_wipe_and_free(call.shared_secret, PQ_MLKEM_SHAREDSECRETBYTES);
309
+ free(call.ciphertext);
310
+ pq_raise_general_error(call.result);
311
+ }
312
+
313
+ VALUE result = rb_ary_new2(2);
314
+ rb_ary_push(result, pq_string_from_buffer(call.ciphertext, PQ_MLKEM_CIPHERTEXTBYTES));
315
+ rb_ary_push(result, pq_string_from_buffer(call.shared_secret, PQ_MLKEM_SHAREDSECRETBYTES));
316
+
317
+ free(call.ciphertext);
318
+ pq_wipe_and_free(call.shared_secret, PQ_MLKEM_SHAREDSECRETBYTES);
319
+ return result;
320
+ }
321
+
322
+ static VALUE pqcrypto_ml_kem_decapsulate(VALUE self, VALUE ciphertext, VALUE secret_key) {
323
+ (void)self;
324
+ StringValue(ciphertext);
325
+ StringValue(secret_key);
326
+
327
+ if ((size_t)RSTRING_LEN(ciphertext) != PQ_MLKEM_CIPHERTEXTBYTES) {
328
+ rb_raise(rb_eArgError, "Invalid ciphertext length");
329
+ }
330
+ if ((size_t)RSTRING_LEN(secret_key) != PQ_MLKEM_SECRETKEYBYTES) {
331
+ rb_raise(rb_eArgError, "Invalid secret key length");
332
+ }
333
+
334
+ kem_decapsulate_call_t call = {0};
335
+ size_t ciphertext_len = 0;
336
+ size_t secret_key_len = 0;
337
+ call.ciphertext = pq_copy_ruby_string(ciphertext, &ciphertext_len);
338
+ call.secret_key = pq_copy_ruby_string(secret_key, &secret_key_len);
339
+ call.shared_secret = pq_alloc_buffer(PQ_MLKEM_SHAREDSECRETBYTES);
340
+
341
+ rb_thread_call_without_gvl(pq_ml_kem_decapsulate_nogvl, &call, NULL, NULL);
342
+ pq_wipe_and_free((uint8_t *)call.ciphertext, ciphertext_len);
343
+ pq_wipe_and_free((uint8_t *)call.secret_key, secret_key_len);
344
+
345
+ if (call.result != PQ_SUCCESS) {
346
+ pq_wipe_and_free(call.shared_secret, PQ_MLKEM_SHAREDSECRETBYTES);
347
+ pq_raise_general_error(call.result);
348
+ }
349
+
350
+ VALUE result = pq_string_from_buffer(call.shared_secret, PQ_MLKEM_SHAREDSECRETBYTES);
351
+ pq_wipe_and_free(call.shared_secret, PQ_MLKEM_SHAREDSECRETBYTES);
352
+ return result;
353
+ }
354
+
355
+ static VALUE pqcrypto_hybrid_kem_keypair(VALUE self) {
356
+ (void)self;
357
+
358
+ kem_keypair_call_t call = {0};
359
+ call.public_key = pq_alloc_buffer(PQ_HYBRID_PUBLICKEYBYTES);
360
+ call.secret_key = pq_alloc_buffer(PQ_HYBRID_SECRETKEYBYTES);
361
+
362
+ rb_thread_call_without_gvl(pq_hybrid_kem_keypair_nogvl, &call, NULL, NULL);
363
+
364
+ if (call.result != PQ_SUCCESS) {
365
+ pq_wipe_and_free(call.secret_key, PQ_HYBRID_SECRETKEYBYTES);
366
+ free(call.public_key);
367
+ pq_raise_general_error(call.result);
368
+ }
369
+
370
+ VALUE result = rb_ary_new2(2);
371
+ rb_ary_push(result, pq_string_from_buffer(call.public_key, PQ_HYBRID_PUBLICKEYBYTES));
372
+ rb_ary_push(result, pq_string_from_buffer(call.secret_key, PQ_HYBRID_SECRETKEYBYTES));
373
+
374
+ free(call.public_key);
375
+ pq_wipe_and_free(call.secret_key, PQ_HYBRID_SECRETKEYBYTES);
376
+ return result;
377
+ }
378
+
379
+ static VALUE pqcrypto_hybrid_kem_encapsulate(VALUE self, VALUE public_key) {
380
+ (void)self;
381
+ StringValue(public_key);
382
+
383
+ if ((size_t)RSTRING_LEN(public_key) != PQ_HYBRID_PUBLICKEYBYTES) {
384
+ rb_raise(rb_eArgError, "Invalid public key length");
385
+ }
386
+
387
+ kem_encapsulate_call_t call = {0};
388
+ size_t public_key_len = 0;
389
+ call.public_key = pq_copy_ruby_string(public_key, &public_key_len);
390
+ call.ciphertext = pq_alloc_buffer(PQ_HYBRID_CIPHERTEXTBYTES);
391
+ call.shared_secret = pq_alloc_buffer(PQ_HYBRID_SHAREDSECRETBYTES);
392
+
393
+ rb_thread_call_without_gvl(pq_hybrid_kem_encapsulate_nogvl, &call, NULL, NULL);
394
+ pq_wipe_and_free((uint8_t *)call.public_key, public_key_len);
395
+
396
+ if (call.result != PQ_SUCCESS) {
397
+ pq_wipe_and_free(call.shared_secret, PQ_HYBRID_SHAREDSECRETBYTES);
398
+ free(call.ciphertext);
399
+ pq_raise_general_error(call.result);
400
+ }
401
+
402
+ VALUE result = rb_ary_new2(2);
403
+ rb_ary_push(result, pq_string_from_buffer(call.ciphertext, PQ_HYBRID_CIPHERTEXTBYTES));
404
+ rb_ary_push(result, pq_string_from_buffer(call.shared_secret, PQ_HYBRID_SHAREDSECRETBYTES));
405
+
406
+ free(call.ciphertext);
407
+ pq_wipe_and_free(call.shared_secret, PQ_HYBRID_SHAREDSECRETBYTES);
408
+ return result;
409
+ }
410
+
411
+ static VALUE pqcrypto_hybrid_kem_decapsulate(VALUE self, VALUE ciphertext, VALUE secret_key) {
412
+ (void)self;
413
+ StringValue(ciphertext);
414
+ StringValue(secret_key);
415
+
416
+ if ((size_t)RSTRING_LEN(ciphertext) != PQ_HYBRID_CIPHERTEXTBYTES) {
417
+ rb_raise(rb_eArgError, "Invalid ciphertext length");
418
+ }
419
+ if ((size_t)RSTRING_LEN(secret_key) != PQ_HYBRID_SECRETKEYBYTES) {
420
+ rb_raise(rb_eArgError, "Invalid secret key length");
421
+ }
422
+
423
+ kem_decapsulate_call_t call = {0};
424
+ size_t ciphertext_len = 0;
425
+ size_t secret_key_len = 0;
426
+ call.ciphertext = pq_copy_ruby_string(ciphertext, &ciphertext_len);
427
+ call.secret_key = pq_copy_ruby_string(secret_key, &secret_key_len);
428
+ call.shared_secret = pq_alloc_buffer(PQ_HYBRID_SHAREDSECRETBYTES);
429
+
430
+ rb_thread_call_without_gvl(pq_hybrid_kem_decapsulate_nogvl, &call, NULL, NULL);
431
+ pq_wipe_and_free((uint8_t *)call.ciphertext, ciphertext_len);
432
+ pq_wipe_and_free((uint8_t *)call.secret_key, secret_key_len);
433
+
434
+ if (call.result != PQ_SUCCESS) {
435
+ pq_wipe_and_free(call.shared_secret, PQ_HYBRID_SHAREDSECRETBYTES);
436
+ pq_raise_general_error(call.result);
437
+ }
438
+
439
+ VALUE result = pq_string_from_buffer(call.shared_secret, PQ_HYBRID_SHAREDSECRETBYTES);
440
+ pq_wipe_and_free(call.shared_secret, PQ_HYBRID_SHAREDSECRETBYTES);
441
+ return result;
442
+ }
443
+
444
+ static VALUE pqcrypto__test_ml_kem_keypair_from_seed(VALUE self, VALUE seed) {
445
+ (void)self;
446
+ StringValue(seed);
447
+
448
+ if ((size_t)RSTRING_LEN(seed) != 32 && (size_t)RSTRING_LEN(seed) != 64) {
449
+ rb_raise(rb_eArgError, "Deterministic ML-KEM test seed must be 32 or 64 bytes");
450
+ }
451
+
452
+ kem_keypair_call_t call = {0};
453
+ size_t seed_len = 0;
454
+ call.public_key = pq_alloc_buffer(PQ_MLKEM_PUBLICKEYBYTES);
455
+ call.secret_key = pq_alloc_buffer(PQ_MLKEM_SECRETKEYBYTES);
456
+ call.seed = pq_copy_ruby_string(seed, &seed_len);
457
+ call.seed_len = seed_len;
458
+
459
+ rb_thread_call_without_gvl(pq_testing_ml_kem_keypair_nogvl, &call, NULL, NULL);
460
+ pq_wipe_and_free((uint8_t *)call.seed, call.seed_len);
461
+
462
+ if (call.result != PQ_SUCCESS) {
463
+ pq_wipe_and_free(call.secret_key, PQ_MLKEM_SECRETKEYBYTES);
464
+ free(call.public_key);
465
+ pq_raise_general_error(call.result);
466
+ }
467
+
468
+ VALUE result = rb_ary_new2(2);
469
+ rb_ary_push(result, pq_string_from_buffer(call.public_key, PQ_MLKEM_PUBLICKEYBYTES));
470
+ rb_ary_push(result, pq_string_from_buffer(call.secret_key, PQ_MLKEM_SECRETKEYBYTES));
471
+
472
+ free(call.public_key);
473
+ pq_wipe_and_free(call.secret_key, PQ_MLKEM_SECRETKEYBYTES);
474
+ return result;
475
+ }
476
+
477
+ static VALUE pqcrypto__test_ml_kem_encapsulate_from_seed(VALUE self, VALUE public_key, VALUE seed) {
478
+ (void)self;
479
+ StringValue(public_key);
480
+ StringValue(seed);
481
+
482
+ if ((size_t)RSTRING_LEN(public_key) != PQ_MLKEM_PUBLICKEYBYTES) {
483
+ rb_raise(rb_eArgError, "Invalid public key length");
484
+ }
485
+ if ((size_t)RSTRING_LEN(seed) != 32) {
486
+ rb_raise(rb_eArgError, "Deterministic test seed must be 32 bytes");
487
+ }
488
+
489
+ kem_encapsulate_call_t call = {0};
490
+ size_t public_key_len = 0;
491
+ size_t seed_len = 0;
492
+ call.public_key = pq_copy_ruby_string(public_key, &public_key_len);
493
+ call.ciphertext = pq_alloc_buffer(PQ_MLKEM_CIPHERTEXTBYTES);
494
+ call.shared_secret = pq_alloc_buffer(PQ_MLKEM_SHAREDSECRETBYTES);
495
+ call.seed = pq_copy_ruby_string(seed, &seed_len);
496
+ call.seed_len = seed_len;
497
+
498
+ rb_thread_call_without_gvl(pq_testing_ml_kem_encapsulate_nogvl, &call, NULL, NULL);
499
+ pq_wipe_and_free((uint8_t *)call.public_key, public_key_len);
500
+ pq_wipe_and_free((uint8_t *)call.seed, call.seed_len);
501
+
502
+ if (call.result != PQ_SUCCESS) {
503
+ pq_wipe_and_free(call.shared_secret, PQ_MLKEM_SHAREDSECRETBYTES);
504
+ free(call.ciphertext);
505
+ pq_raise_general_error(call.result);
506
+ }
507
+
508
+ VALUE result = rb_ary_new2(2);
509
+ rb_ary_push(result, pq_string_from_buffer(call.ciphertext, PQ_MLKEM_CIPHERTEXTBYTES));
510
+ rb_ary_push(result, pq_string_from_buffer(call.shared_secret, PQ_MLKEM_SHAREDSECRETBYTES));
511
+
512
+ free(call.ciphertext);
513
+ pq_wipe_and_free(call.shared_secret, PQ_MLKEM_SHAREDSECRETBYTES);
514
+ return result;
515
+ }
516
+
517
+ static VALUE pqcrypto__test_sign_keypair_from_seed(VALUE self, VALUE seed) {
518
+ (void)self;
519
+ StringValue(seed);
520
+
521
+ if ((size_t)RSTRING_LEN(seed) != 32) {
522
+ rb_raise(rb_eArgError, "Deterministic test seed must be 32 bytes");
523
+ }
524
+
525
+ sign_keypair_call_t call = {0};
526
+ size_t seed_len = 0;
527
+ call.public_key = pq_alloc_buffer(PQ_MLDSA_PUBLICKEYBYTES);
528
+ call.secret_key = pq_alloc_buffer(PQ_MLDSA_SECRETKEYBYTES);
529
+ call.seed = pq_copy_ruby_string(seed, &seed_len);
530
+ call.seed_len = seed_len;
531
+
532
+ rb_thread_call_without_gvl(pq_testing_sign_keypair_nogvl, &call, NULL, NULL);
533
+ pq_wipe_and_free((uint8_t *)call.seed, call.seed_len);
534
+
535
+ if (call.result != PQ_SUCCESS) {
536
+ pq_wipe_and_free(call.secret_key, PQ_MLDSA_SECRETKEYBYTES);
537
+ free(call.public_key);
538
+ pq_raise_general_error(call.result);
539
+ }
540
+
541
+ VALUE result = rb_ary_new2(2);
542
+ rb_ary_push(result, pq_string_from_buffer(call.public_key, PQ_MLDSA_PUBLICKEYBYTES));
543
+ rb_ary_push(result, pq_string_from_buffer(call.secret_key, PQ_MLDSA_SECRETKEYBYTES));
544
+
545
+ free(call.public_key);
546
+ pq_wipe_and_free(call.secret_key, PQ_MLDSA_SECRETKEYBYTES);
547
+ return result;
548
+ }
549
+
550
+ static VALUE pqcrypto__test_sign_from_seed(VALUE self, VALUE message, VALUE secret_key,
551
+ VALUE seed) {
552
+ (void)self;
553
+ StringValue(secret_key);
554
+ StringValue(seed);
555
+
556
+ if ((size_t)RSTRING_LEN(secret_key) != PQ_MLDSA_SECRETKEYBYTES) {
557
+ rb_raise(rb_eArgError, "Invalid secret key length");
558
+ }
559
+ if ((size_t)RSTRING_LEN(seed) != 32) {
560
+ rb_raise(rb_eArgError, "Deterministic test seed must be 32 bytes");
561
+ }
562
+
563
+ sign_call_t call = {0};
564
+ size_t secret_key_len = 0;
565
+ size_t seed_len = 0;
566
+ call.secret_key = pq_copy_ruby_string(secret_key, &secret_key_len);
567
+ call.signature_len = PQ_MLDSA_BYTES;
568
+ call.signature = pq_alloc_buffer(PQ_MLDSA_BYTES);
569
+ call.message = pq_copy_ruby_string(message, &call.message_len);
570
+ call.seed = pq_copy_ruby_string(seed, &seed_len);
571
+ call.seed_len = seed_len;
572
+
573
+ rb_thread_call_without_gvl(pq_testing_sign_nogvl, &call, NULL, NULL);
574
+
575
+ pq_wipe_and_free(call.message, call.message_len);
576
+ pq_wipe_and_free((uint8_t *)call.secret_key, secret_key_len);
577
+ pq_wipe_and_free((uint8_t *)call.seed, call.seed_len);
578
+
579
+ if (call.result != PQ_SUCCESS) {
580
+ pq_wipe_and_free(call.signature, PQ_MLDSA_BYTES);
581
+ pq_raise_general_error(call.result);
582
+ }
583
+
584
+ VALUE result = pq_string_from_buffer(call.signature, call.signature_len);
585
+ pq_wipe_and_free(call.signature, PQ_MLDSA_BYTES);
586
+ return result;
587
+ }
588
+
589
+ static VALUE pqcrypto_sign_keypair(VALUE self) {
590
+ (void)self;
591
+
592
+ sign_keypair_call_t call = {0};
593
+ call.public_key = pq_alloc_buffer(PQ_MLDSA_PUBLICKEYBYTES);
594
+ call.secret_key = pq_alloc_buffer(PQ_MLDSA_SECRETKEYBYTES);
595
+
596
+ rb_thread_call_without_gvl(pq_sign_keypair_nogvl, &call, NULL, NULL);
597
+
598
+ if (call.result != PQ_SUCCESS) {
599
+ pq_wipe_and_free(call.secret_key, PQ_MLDSA_SECRETKEYBYTES);
600
+ free(call.public_key);
601
+ pq_raise_general_error(call.result);
602
+ }
603
+
604
+ VALUE result = rb_ary_new2(2);
605
+ rb_ary_push(result, pq_string_from_buffer(call.public_key, PQ_MLDSA_PUBLICKEYBYTES));
606
+ rb_ary_push(result, pq_string_from_buffer(call.secret_key, PQ_MLDSA_SECRETKEYBYTES));
607
+
608
+ free(call.public_key);
609
+ pq_wipe_and_free(call.secret_key, PQ_MLDSA_SECRETKEYBYTES);
610
+ return result;
611
+ }
612
+
613
+ static VALUE pqcrypto_sign(VALUE self, VALUE message, VALUE secret_key) {
614
+ (void)self;
615
+ StringValue(secret_key);
616
+
617
+ if ((size_t)RSTRING_LEN(secret_key) != PQ_MLDSA_SECRETKEYBYTES) {
618
+ rb_raise(rb_eArgError, "Invalid secret key length");
619
+ }
620
+
621
+ sign_call_t call = {0};
622
+ size_t secret_key_len = 0;
623
+ call.secret_key = pq_copy_ruby_string(secret_key, &secret_key_len);
624
+ call.signature_len = PQ_MLDSA_BYTES;
625
+ call.signature = pq_alloc_buffer(PQ_MLDSA_BYTES);
626
+ call.message = pq_copy_ruby_string(message, &call.message_len);
627
+
628
+ rb_thread_call_without_gvl(pq_sign_nogvl, &call, NULL, NULL);
629
+
630
+ pq_wipe_and_free(call.message, call.message_len);
631
+ pq_wipe_and_free((uint8_t *)call.secret_key, secret_key_len);
632
+
633
+ if (call.result != PQ_SUCCESS) {
634
+ pq_wipe_and_free(call.signature, PQ_MLDSA_BYTES);
635
+ pq_raise_general_error(call.result);
636
+ }
637
+
638
+ VALUE result = pq_string_from_buffer(call.signature, call.signature_len);
639
+ free(call.signature);
640
+ return result;
641
+ }
642
+
643
+ static VALUE pqcrypto_verify(VALUE self, VALUE message, VALUE signature, VALUE public_key) {
644
+ (void)self;
645
+ StringValue(signature);
646
+ StringValue(public_key);
647
+
648
+ if ((size_t)RSTRING_LEN(public_key) != PQ_MLDSA_PUBLICKEYBYTES) {
649
+ rb_raise(rb_eArgError, "Invalid public key length");
650
+ }
651
+
652
+ verify_call_t call = {0};
653
+ size_t public_key_len = 0;
654
+ size_t signature_len = 0;
655
+ call.public_key = pq_copy_ruby_string(public_key, &public_key_len);
656
+ call.signature = pq_copy_ruby_string(signature, &signature_len);
657
+ call.signature_len = signature_len;
658
+ call.message = pq_copy_ruby_string(message, &call.message_len);
659
+
660
+ rb_thread_call_without_gvl(pq_verify_nogvl, &call, NULL, NULL);
661
+
662
+ pq_wipe_and_free(call.message, call.message_len);
663
+ pq_wipe_and_free((uint8_t *)call.public_key, public_key_len);
664
+ pq_wipe_and_free((uint8_t *)call.signature, signature_len);
665
+
666
+ if (call.result != PQ_SUCCESS) {
667
+ pq_raise_verification_error(call.result);
668
+ }
669
+
670
+ return Qtrue;
671
+ }
672
+
673
+ static VALUE pqcrypto_secure_wipe(VALUE self, VALUE str) {
674
+ (void)self;
675
+ StringValue(str);
676
+ rb_str_modify(str);
677
+ pq_secure_wipe((uint8_t *)RSTRING_PTR(str), (size_t)RSTRING_LEN(str));
678
+ return Qnil;
679
+ }
680
+
681
+ static VALUE pqcrypto_version(VALUE self) {
682
+ (void)self;
683
+ return rb_str_new_cstr(pq_version());
684
+ }
685
+
686
+ static void define_constants(void) {
687
+ rb_define_const(mPQCrypto, "ML_KEM_PUBLIC_KEY_BYTES", INT2NUM(PQ_MLKEM_PUBLICKEYBYTES));
688
+ rb_define_const(mPQCrypto, "ML_KEM_SECRET_KEY_BYTES", INT2NUM(PQ_MLKEM_SECRETKEYBYTES));
689
+ rb_define_const(mPQCrypto, "ML_KEM_CIPHERTEXT_BYTES", INT2NUM(PQ_MLKEM_CIPHERTEXTBYTES));
690
+ rb_define_const(mPQCrypto, "ML_KEM_SHARED_SECRET_BYTES", INT2NUM(PQ_MLKEM_SHAREDSECRETBYTES));
691
+ rb_define_const(mPQCrypto, "HYBRID_KEM_PUBLIC_KEY_BYTES", INT2NUM(PQ_HYBRID_PUBLICKEYBYTES));
692
+ rb_define_const(mPQCrypto, "HYBRID_KEM_SECRET_KEY_BYTES", INT2NUM(PQ_HYBRID_SECRETKEYBYTES));
693
+ rb_define_const(mPQCrypto, "HYBRID_KEM_CIPHERTEXT_BYTES", INT2NUM(PQ_HYBRID_CIPHERTEXTBYTES));
694
+ rb_define_const(mPQCrypto, "HYBRID_KEM_SHARED_SECRET_BYTES",
695
+ INT2NUM(PQ_HYBRID_SHAREDSECRETBYTES));
696
+ rb_define_const(mPQCrypto, "SIGN_PUBLIC_KEY_BYTES", INT2NUM(PQ_MLDSA_PUBLICKEYBYTES));
697
+ rb_define_const(mPQCrypto, "SIGN_SECRET_KEY_BYTES", INT2NUM(PQ_MLDSA_SECRETKEYBYTES));
698
+ rb_define_const(mPQCrypto, "SIGN_BYTES", INT2NUM(PQ_MLDSA_BYTES));
699
+ }
700
+
701
+ static VALUE pqcrypto_public_key_to_pqc_container_der(VALUE self, VALUE algorithm,
702
+ VALUE key_bytes) {
703
+ uint8_t *out = NULL;
704
+ size_t out_len = 0;
705
+ VALUE result;
706
+ StringValue(key_bytes);
707
+ int ret = pq_public_key_to_pqc_container_der(
708
+ &out, &out_len, (const uint8_t *)RSTRING_PTR(key_bytes), (size_t)RSTRING_LEN(key_bytes),
709
+ pq_algorithm_symbol_to_cstr(algorithm));
710
+ if (ret != PQ_SUCCESS)
711
+ pq_raise_general_error(ret);
712
+ result = pq_string_from_buffer(out, out_len);
713
+ pq_secure_wipe(out, out_len);
714
+ free(out);
715
+ return result;
716
+ }
717
+
718
+ static VALUE pqcrypto_public_key_to_pqc_container_pem(VALUE self, VALUE algorithm,
719
+ VALUE key_bytes) {
720
+ char *out = NULL;
721
+ size_t out_len = 0;
722
+ VALUE result;
723
+ StringValue(key_bytes);
724
+ int ret = pq_public_key_to_pqc_container_pem(
725
+ &out, &out_len, (const uint8_t *)RSTRING_PTR(key_bytes), (size_t)RSTRING_LEN(key_bytes),
726
+ pq_algorithm_symbol_to_cstr(algorithm));
727
+ if (ret != PQ_SUCCESS)
728
+ pq_raise_general_error(ret);
729
+ result = rb_utf8_str_new(out, (long)out_len);
730
+ pq_secure_wipe(out, out_len);
731
+ free(out);
732
+ return result;
733
+ }
734
+
735
+ static VALUE pqcrypto_secret_key_to_pqc_container_der(VALUE self, VALUE algorithm,
736
+ VALUE key_bytes) {
737
+ uint8_t *out = NULL;
738
+ size_t out_len = 0;
739
+ VALUE result;
740
+ StringValue(key_bytes);
741
+ int ret = pq_secret_key_to_pqc_container_der(
742
+ &out, &out_len, (const uint8_t *)RSTRING_PTR(key_bytes), (size_t)RSTRING_LEN(key_bytes),
743
+ pq_algorithm_symbol_to_cstr(algorithm));
744
+ if (ret != PQ_SUCCESS)
745
+ pq_raise_general_error(ret);
746
+ result = pq_string_from_buffer(out, out_len);
747
+ pq_secure_wipe(out, out_len);
748
+ free(out);
749
+ return result;
750
+ }
751
+
752
+ static VALUE pqcrypto_secret_key_to_pqc_container_pem(VALUE self, VALUE algorithm,
753
+ VALUE key_bytes) {
754
+ char *out = NULL;
755
+ size_t out_len = 0;
756
+ VALUE result;
757
+ StringValue(key_bytes);
758
+ int ret = pq_secret_key_to_pqc_container_pem(
759
+ &out, &out_len, (const uint8_t *)RSTRING_PTR(key_bytes), (size_t)RSTRING_LEN(key_bytes),
760
+ pq_algorithm_symbol_to_cstr(algorithm));
761
+ if (ret != PQ_SUCCESS)
762
+ pq_raise_general_error(ret);
763
+ result = rb_utf8_str_new(out, (long)out_len);
764
+ pq_secure_wipe(out, out_len);
765
+ free(out);
766
+ return result;
767
+ }
768
+
769
+ static VALUE pqcrypto_public_key_from_pqc_container_der(VALUE self, VALUE der) {
770
+ char *algorithm = NULL;
771
+ uint8_t *key = NULL;
772
+ size_t key_len = 0;
773
+ VALUE ary;
774
+ StringValue(der);
775
+ int ret = pq_public_key_from_pqc_container_der(
776
+ &algorithm, &key, &key_len, (const uint8_t *)RSTRING_PTR(der), (size_t)RSTRING_LEN(der));
777
+ if (ret != PQ_SUCCESS)
778
+ pq_raise_general_error(ret);
779
+ ary = rb_ary_new_capa(2);
780
+ rb_ary_push(ary, pq_algorithm_cstr_to_symbol(algorithm));
781
+ rb_ary_push(ary, pq_string_from_buffer(key, key_len));
782
+ free(algorithm);
783
+ pq_secure_wipe(key, key_len);
784
+ free(key);
785
+ return ary;
786
+ }
787
+
788
+ static VALUE pqcrypto_public_key_from_pqc_container_pem(VALUE self, VALUE pem) {
789
+ char *algorithm = NULL;
790
+ uint8_t *key = NULL;
791
+ size_t key_len = 0;
792
+ VALUE ary;
793
+ StringValue(pem);
794
+ int ret = pq_public_key_from_pqc_container_pem(&algorithm, &key, &key_len, RSTRING_PTR(pem),
795
+ (size_t)RSTRING_LEN(pem));
796
+ if (ret != PQ_SUCCESS)
797
+ pq_raise_general_error(ret);
798
+ ary = rb_ary_new_capa(2);
799
+ rb_ary_push(ary, pq_algorithm_cstr_to_symbol(algorithm));
800
+ rb_ary_push(ary, pq_string_from_buffer(key, key_len));
801
+ free(algorithm);
802
+ pq_secure_wipe(key, key_len);
803
+ free(key);
804
+ return ary;
805
+ }
806
+
807
+ static VALUE pqcrypto_secret_key_from_pqc_container_der(VALUE self, VALUE der) {
808
+ char *algorithm = NULL;
809
+ uint8_t *key = NULL;
810
+ size_t key_len = 0;
811
+ VALUE ary;
812
+ StringValue(der);
813
+ int ret = pq_secret_key_from_pqc_container_der(
814
+ &algorithm, &key, &key_len, (const uint8_t *)RSTRING_PTR(der), (size_t)RSTRING_LEN(der));
815
+ if (ret != PQ_SUCCESS)
816
+ pq_raise_general_error(ret);
817
+ ary = rb_ary_new_capa(2);
818
+ rb_ary_push(ary, pq_algorithm_cstr_to_symbol(algorithm));
819
+ rb_ary_push(ary, pq_string_from_buffer(key, key_len));
820
+ free(algorithm);
821
+ pq_secure_wipe(key, key_len);
822
+ free(key);
823
+ return ary;
824
+ }
825
+
826
+ static VALUE pqcrypto_secret_key_from_pqc_container_pem(VALUE self, VALUE pem) {
827
+ char *algorithm = NULL;
828
+ uint8_t *key = NULL;
829
+ size_t key_len = 0;
830
+ VALUE ary;
831
+ StringValue(pem);
832
+ int ret = pq_secret_key_from_pqc_container_pem(&algorithm, &key, &key_len, RSTRING_PTR(pem),
833
+ (size_t)RSTRING_LEN(pem));
834
+ if (ret != PQ_SUCCESS)
835
+ pq_raise_general_error(ret);
836
+ ary = rb_ary_new_capa(2);
837
+ rb_ary_push(ary, pq_algorithm_cstr_to_symbol(algorithm));
838
+ rb_ary_push(ary, pq_string_from_buffer(key, key_len));
839
+ free(algorithm);
840
+ pq_secure_wipe(key, key_len);
841
+ free(key);
842
+ return ary;
843
+ }
844
+
845
+ void Init_pqcrypto_secure(void) {
846
+ mPQCrypto = rb_define_module("PQCrypto");
847
+ ePQCryptoError = rb_define_class_under(mPQCrypto, "Error", rb_eStandardError);
848
+ ePQCryptoVerificationError =
849
+ rb_define_class_under(mPQCrypto, "VerificationError", ePQCryptoError);
850
+
851
+ rb_define_module_function(mPQCrypto, "__test_ml_kem_keypair_from_seed",
852
+ pqcrypto__test_ml_kem_keypair_from_seed, 1);
853
+ rb_define_module_function(mPQCrypto, "__test_ml_kem_encapsulate_from_seed",
854
+ pqcrypto__test_ml_kem_encapsulate_from_seed, 2);
855
+ rb_define_module_function(mPQCrypto, "__test_sign_keypair_from_seed",
856
+ pqcrypto__test_sign_keypair_from_seed, 1);
857
+ rb_define_module_function(mPQCrypto, "__test_sign_from_seed", pqcrypto__test_sign_from_seed, 3);
858
+ rb_define_module_function(mPQCrypto, "ml_kem_keypair", pqcrypto_ml_kem_keypair, 0);
859
+ rb_define_module_function(mPQCrypto, "ml_kem_encapsulate", pqcrypto_ml_kem_encapsulate, 1);
860
+ rb_define_module_function(mPQCrypto, "ml_kem_decapsulate", pqcrypto_ml_kem_decapsulate, 2);
861
+ rb_define_module_function(mPQCrypto, "hybrid_kem_keypair", pqcrypto_hybrid_kem_keypair, 0);
862
+ rb_define_module_function(mPQCrypto, "hybrid_kem_encapsulate", pqcrypto_hybrid_kem_encapsulate,
863
+ 1);
864
+ rb_define_module_function(mPQCrypto, "hybrid_kem_decapsulate", pqcrypto_hybrid_kem_decapsulate,
865
+ 2);
866
+ rb_define_module_function(mPQCrypto, "sign_keypair", pqcrypto_sign_keypair, 0);
867
+ rb_define_module_function(mPQCrypto, "sign", pqcrypto_sign, 2);
868
+ rb_define_module_function(mPQCrypto, "verify", pqcrypto_verify, 3);
869
+ rb_define_module_function(mPQCrypto, "secure_wipe", pqcrypto_secure_wipe, 1);
870
+ rb_define_module_function(mPQCrypto, "version", pqcrypto_version, 0);
871
+ rb_define_module_function(mPQCrypto, "public_key_to_pqc_container_der",
872
+ pqcrypto_public_key_to_pqc_container_der, 2);
873
+ rb_define_module_function(mPQCrypto, "public_key_to_pqc_container_pem",
874
+ pqcrypto_public_key_to_pqc_container_pem, 2);
875
+ rb_define_module_function(mPQCrypto, "secret_key_to_pqc_container_der",
876
+ pqcrypto_secret_key_to_pqc_container_der, 2);
877
+ rb_define_module_function(mPQCrypto, "secret_key_to_pqc_container_pem",
878
+ pqcrypto_secret_key_to_pqc_container_pem, 2);
879
+ rb_define_module_function(mPQCrypto, "public_key_from_pqc_container_der",
880
+ pqcrypto_public_key_from_pqc_container_der, 1);
881
+ rb_define_module_function(mPQCrypto, "public_key_from_pqc_container_pem",
882
+ pqcrypto_public_key_from_pqc_container_pem, 1);
883
+ rb_define_module_function(mPQCrypto, "secret_key_from_pqc_container_der",
884
+ pqcrypto_secret_key_from_pqc_container_der, 1);
885
+ rb_define_module_function(mPQCrypto, "secret_key_from_pqc_container_pem",
886
+ pqcrypto_secret_key_from_pqc_container_pem, 1);
887
+
888
+ define_constants();
889
+ }