pq_crypto 0.3.1 → 0.4.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.
Files changed (117) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +56 -0
  3. data/CHANGELOG.md +50 -0
  4. data/GET_STARTED.md +374 -30
  5. data/README.md +59 -195
  6. data/SECURITY.md +101 -82
  7. data/ext/pqcrypto/extconf.rb +85 -9
  8. data/ext/pqcrypto/mldsa_api.h +71 -1
  9. data/ext/pqcrypto/mlkem_api.h +24 -0
  10. data/ext/pqcrypto/pq_externalmu.c +310 -0
  11. data/ext/pqcrypto/pqcrypto_ruby_secure.c +784 -85
  12. data/ext/pqcrypto/pqcrypto_secure.c +179 -72
  13. data/ext/pqcrypto/pqcrypto_secure.h +103 -7
  14. data/ext/pqcrypto/pqcrypto_version.h +7 -0
  15. data/ext/pqcrypto/vendor/.vendored +1 -1
  16. data/ext/pqcrypto/vendor/pqclean/common/keccak4x/Makefile +8 -0
  17. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-1024/clean/LICENSE +5 -0
  18. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-1024/clean/Makefile +19 -0
  19. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-1024/clean/Makefile.Microsoft_nmake +23 -0
  20. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-1024/clean/api.h +18 -0
  21. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-1024/clean/cbd.c +83 -0
  22. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-1024/clean/cbd.h +11 -0
  23. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-1024/clean/indcpa.c +327 -0
  24. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-1024/clean/indcpa.h +22 -0
  25. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-1024/clean/kem.c +164 -0
  26. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-1024/clean/kem.h +23 -0
  27. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-1024/clean/ntt.c +146 -0
  28. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-1024/clean/ntt.h +14 -0
  29. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-1024/clean/params.h +36 -0
  30. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-1024/clean/poly.c +311 -0
  31. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-1024/clean/poly.h +37 -0
  32. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-1024/clean/polyvec.c +198 -0
  33. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-1024/clean/polyvec.h +26 -0
  34. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-1024/clean/reduce.c +41 -0
  35. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-1024/clean/reduce.h +13 -0
  36. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-1024/clean/symmetric-shake.c +71 -0
  37. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-1024/clean/symmetric.h +30 -0
  38. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-1024/clean/verify.c +67 -0
  39. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-1024/clean/verify.h +13 -0
  40. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-512/clean/LICENSE +5 -0
  41. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-512/clean/Makefile +19 -0
  42. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-512/clean/Makefile.Microsoft_nmake +23 -0
  43. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-512/clean/api.h +18 -0
  44. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-512/clean/cbd.c +108 -0
  45. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-512/clean/cbd.h +11 -0
  46. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-512/clean/indcpa.c +327 -0
  47. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-512/clean/indcpa.h +22 -0
  48. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-512/clean/kem.c +164 -0
  49. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-512/clean/kem.h +23 -0
  50. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-512/clean/ntt.c +146 -0
  51. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-512/clean/ntt.h +14 -0
  52. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-512/clean/params.h +36 -0
  53. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-512/clean/poly.c +299 -0
  54. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-512/clean/poly.h +37 -0
  55. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-512/clean/polyvec.c +188 -0
  56. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-512/clean/polyvec.h +26 -0
  57. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-512/clean/reduce.c +41 -0
  58. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-512/clean/reduce.h +13 -0
  59. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-512/clean/symmetric-shake.c +71 -0
  60. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-512/clean/symmetric.h +30 -0
  61. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-512/clean/verify.c +67 -0
  62. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-512/clean/verify.h +13 -0
  63. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/Makefile +19 -0
  64. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-44/clean/LICENSE +5 -0
  65. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-44/clean/Makefile +19 -0
  66. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-44/clean/Makefile.Microsoft_nmake +23 -0
  67. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-44/clean/api.h +50 -0
  68. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-44/clean/ntt.c +98 -0
  69. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-44/clean/ntt.h +10 -0
  70. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-44/clean/packing.c +261 -0
  71. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-44/clean/packing.h +31 -0
  72. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-44/clean/params.h +44 -0
  73. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-44/clean/poly.c +848 -0
  74. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-44/clean/poly.h +52 -0
  75. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-44/clean/polyvec.c +415 -0
  76. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-44/clean/polyvec.h +65 -0
  77. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-44/clean/reduce.c +69 -0
  78. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-44/clean/reduce.h +17 -0
  79. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-44/clean/rounding.c +98 -0
  80. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-44/clean/rounding.h +14 -0
  81. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-44/clean/sign.c +407 -0
  82. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-44/clean/sign.h +47 -0
  83. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-44/clean/symmetric-shake.c +26 -0
  84. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-44/clean/symmetric.h +34 -0
  85. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-65/clean/Makefile +19 -0
  86. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-87/clean/LICENSE +5 -0
  87. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-87/clean/Makefile +19 -0
  88. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-87/clean/Makefile.Microsoft_nmake +23 -0
  89. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-87/clean/api.h +50 -0
  90. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-87/clean/ntt.c +98 -0
  91. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-87/clean/ntt.h +10 -0
  92. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-87/clean/packing.c +261 -0
  93. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-87/clean/packing.h +31 -0
  94. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-87/clean/params.h +44 -0
  95. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-87/clean/poly.c +823 -0
  96. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-87/clean/poly.h +52 -0
  97. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-87/clean/polyvec.c +415 -0
  98. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-87/clean/polyvec.h +65 -0
  99. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-87/clean/reduce.c +69 -0
  100. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-87/clean/reduce.h +17 -0
  101. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-87/clean/rounding.c +92 -0
  102. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-87/clean/rounding.h +14 -0
  103. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-87/clean/sign.c +407 -0
  104. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-87/clean/sign.h +47 -0
  105. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-87/clean/symmetric-shake.c +26 -0
  106. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-87/clean/symmetric.h +34 -0
  107. data/lib/pq_crypto/algorithm_registry.rb +200 -0
  108. data/lib/pq_crypto/hybrid_kem.rb +1 -12
  109. data/lib/pq_crypto/kem.rb +104 -13
  110. data/lib/pq_crypto/pkcs8.rb +387 -0
  111. data/lib/pq_crypto/serialization.rb +1 -14
  112. data/lib/pq_crypto/signature.rb +231 -13
  113. data/lib/pq_crypto/spki.rb +131 -0
  114. data/lib/pq_crypto/version.rb +1 -1
  115. data/lib/pq_crypto.rb +90 -19
  116. data/script/vendor_libs.rb +4 -0
  117. metadata +99 -3
@@ -70,6 +70,12 @@ static VALUE ePQCryptoVerificationError;
70
70
 
71
71
  __attribute__((noreturn)) static void pq_raise_general_error(int err);
72
72
 
73
+ static const char *const PQC_CONTAINER_ALGORITHMS[] = {
74
+ "ml_kem_768",
75
+ "ml_kem_768_x25519_xwing",
76
+ "ml_dsa_65",
77
+ };
78
+
73
79
  static const char *pq_algorithm_symbol_to_cstr(VALUE algorithm) {
74
80
  ID id;
75
81
  if (SYMBOL_P(algorithm)) {
@@ -78,42 +84,64 @@ static const char *pq_algorithm_symbol_to_cstr(VALUE algorithm) {
78
84
  VALUE str = StringValue(algorithm);
79
85
  id = rb_intern_str(str);
80
86
  }
81
- if (id == rb_intern("ml_kem_768"))
82
- return "ml_kem_768";
83
- if (id == rb_intern("ml_kem_768_x25519_xwing"))
84
- return "ml_kem_768_x25519_xwing";
85
- if (id == rb_intern("ml_dsa_65"))
86
- return "ml_dsa_65";
87
+ for (size_t i = 0; i < sizeof(PQC_CONTAINER_ALGORITHMS) / sizeof(PQC_CONTAINER_ALGORITHMS[0]);
88
+ ++i) {
89
+ if (id == rb_intern(PQC_CONTAINER_ALGORITHMS[i])) {
90
+ return PQC_CONTAINER_ALGORITHMS[i];
91
+ }
92
+ }
87
93
  rb_raise(rb_eArgError, "Unsupported serialization algorithm");
88
94
  }
89
95
 
90
96
  static VALUE pq_algorithm_cstr_to_symbol(const char *algorithm) {
91
- if (strcmp(algorithm, "ml_kem_768") == 0)
92
- return ID2SYM(rb_intern("ml_kem_768"));
93
- if (strcmp(algorithm, "ml_kem_768_x25519_xwing") == 0)
94
- return ID2SYM(rb_intern("ml_kem_768_x25519_xwing"));
95
- if (strcmp(algorithm, "ml_dsa_65") == 0)
96
- return ID2SYM(rb_intern("ml_dsa_65"));
97
+ for (size_t i = 0; i < sizeof(PQC_CONTAINER_ALGORITHMS) / sizeof(PQC_CONTAINER_ALGORITHMS[0]);
98
+ ++i) {
99
+ if (strcmp(algorithm, PQC_CONTAINER_ALGORITHMS[i]) == 0) {
100
+ return ID2SYM(rb_intern(PQC_CONTAINER_ALGORITHMS[i]));
101
+ }
102
+ }
97
103
  rb_raise(rb_eArgError, "Unsupported serialization algorithm");
98
104
  }
99
105
 
100
- static void *pq_ml_kem_keypair_nogvl(void *arg) {
101
- kem_keypair_call_t *call = (kem_keypair_call_t *)arg;
102
- call->result = pq_mlkem_keypair(call->public_key, call->secret_key);
103
- return NULL;
104
- }
106
+ #define PQ_KEM_KEYPAIR_NOGVL_VARIANTS(X) \
107
+ X(ml_kem, mlkem) \
108
+ X(ml_kem_512, mlkem512) \
109
+ X(ml_kem_1024, mlkem1024)
110
+
111
+ #define PQ_DEFINE_KEM_KEYPAIR_NOGVL(rb_name, c_name) \
112
+ static void *pq_##rb_name##_keypair_nogvl(void *arg) { \
113
+ kem_keypair_call_t *call = (kem_keypair_call_t *)arg; \
114
+ call->result = pq_##c_name##_keypair(call->public_key, call->secret_key); \
115
+ return NULL; \
116
+ } \
117
+ static void *pq_##rb_name##_keypair_from_seed_nogvl(void *arg) { \
118
+ kem_keypair_call_t *call = (kem_keypair_call_t *)arg; \
119
+ call->result = \
120
+ pq_##c_name##_keypair_from_seed(call->public_key, call->secret_key, call->seed); \
121
+ return NULL; \
122
+ }
105
123
 
106
- static void *pq_ml_kem_encapsulate_nogvl(void *arg) {
107
- kem_encapsulate_call_t *call = (kem_encapsulate_call_t *)arg;
108
- call->result = pq_mlkem_encapsulate(call->ciphertext, call->shared_secret, call->public_key);
109
- return NULL;
110
- }
124
+ PQ_KEM_KEYPAIR_NOGVL_VARIANTS(PQ_DEFINE_KEM_KEYPAIR_NOGVL)
125
+
126
+ #undef PQ_DEFINE_KEM_KEYPAIR_NOGVL
127
+
128
+ #define PQ_DEFINE_KEM_ENCAP_DECAP_NOGVL(rb_name, c_name) \
129
+ static void *pq_##rb_name##_encapsulate_nogvl(void *arg) { \
130
+ kem_encapsulate_call_t *call = (kem_encapsulate_call_t *)arg; \
131
+ call->result = \
132
+ pq_##c_name##_encapsulate(call->ciphertext, call->shared_secret, call->public_key); \
133
+ return NULL; \
134
+ } \
135
+ static void *pq_##rb_name##_decapsulate_nogvl(void *arg) { \
136
+ kem_decapsulate_call_t *call = (kem_decapsulate_call_t *)arg; \
137
+ call->result = \
138
+ pq_##c_name##_decapsulate(call->shared_secret, call->ciphertext, call->secret_key); \
139
+ return NULL; \
140
+ }
111
141
 
112
- static void *pq_ml_kem_decapsulate_nogvl(void *arg) {
113
- kem_decapsulate_call_t *call = (kem_decapsulate_call_t *)arg;
114
- call->result = pq_mlkem_decapsulate(call->shared_secret, call->ciphertext, call->secret_key);
115
- return NULL;
116
- }
142
+ PQ_KEM_KEYPAIR_NOGVL_VARIANTS(PQ_DEFINE_KEM_ENCAP_DECAP_NOGVL)
143
+
144
+ #undef PQ_DEFINE_KEM_ENCAP_DECAP_NOGVL
117
145
 
118
146
  static void *pq_testing_ml_kem_keypair_nogvl(void *arg) {
119
147
  kem_keypair_call_t *call = (kem_keypair_call_t *)arg;
@@ -122,12 +150,17 @@ static void *pq_testing_ml_kem_keypair_nogvl(void *arg) {
122
150
  return NULL;
123
151
  }
124
152
 
125
- static void *pq_testing_ml_kem_encapsulate_nogvl(void *arg) {
126
- kem_encapsulate_call_t *call = (kem_encapsulate_call_t *)arg;
127
- call->result = pq_testing_mlkem_encapsulate_from_seed(
128
- call->ciphertext, call->shared_secret, call->public_key, call->seed, call->seed_len);
129
- return NULL;
130
- }
153
+ #define PQ_DEFINE_KEM_TESTING_ENCAP_NOGVL(rb_name, c_name) \
154
+ static void *pq_testing_##rb_name##_encapsulate_nogvl(void *arg) { \
155
+ kem_encapsulate_call_t *call = (kem_encapsulate_call_t *)arg; \
156
+ call->result = pq_testing_##c_name##_encapsulate_from_seed( \
157
+ call->ciphertext, call->shared_secret, call->public_key, call->seed, call->seed_len); \
158
+ return NULL; \
159
+ }
160
+
161
+ PQ_KEM_KEYPAIR_NOGVL_VARIANTS(PQ_DEFINE_KEM_TESTING_ENCAP_NOGVL)
162
+
163
+ #undef PQ_DEFINE_KEM_TESTING_ENCAP_NOGVL
131
164
 
132
165
  static void *pq_hybrid_kem_keypair_nogvl(void *arg) {
133
166
  kem_keypair_call_t *call = (kem_keypair_call_t *)arg;
@@ -149,18 +182,45 @@ static void *pq_hybrid_kem_decapsulate_nogvl(void *arg) {
149
182
  return NULL;
150
183
  }
151
184
 
152
- static void *pq_sign_keypair_nogvl(void *arg) {
153
- sign_keypair_call_t *call = (sign_keypair_call_t *)arg;
154
- call->result = pq_sign_keypair(call->public_key, call->secret_key);
155
- return NULL;
156
- }
185
+ #define PQ_DEFINE_SIGN_KEYPAIR_NOGVL(rb_name, c_call) \
186
+ static void *pq_##rb_name##_nogvl(void *arg) { \
187
+ sign_keypair_call_t *call = (sign_keypair_call_t *)arg; \
188
+ call->result = c_call(call->public_key, call->secret_key); \
189
+ return NULL; \
190
+ }
157
191
 
158
- static void *pq_sign_nogvl(void *arg) {
159
- sign_call_t *call = (sign_call_t *)arg;
160
- call->result = pq_sign(call->signature, &call->signature_len, call->message, call->message_len,
161
- call->secret_key);
162
- return NULL;
163
- }
192
+ PQ_DEFINE_SIGN_KEYPAIR_NOGVL(sign_keypair, pq_sign_keypair)
193
+ PQ_DEFINE_SIGN_KEYPAIR_NOGVL(mldsa_44_sign_keypair, pq_mldsa44_sign_keypair)
194
+ PQ_DEFINE_SIGN_KEYPAIR_NOGVL(mldsa_87_sign_keypair, pq_mldsa87_sign_keypair)
195
+
196
+ #undef PQ_DEFINE_SIGN_KEYPAIR_NOGVL
197
+
198
+ #define PQ_DEFINE_SIGN_KEYPAIR_FROM_SEED_NOGVL(rb_name, c_call) \
199
+ static void *pq_##rb_name##_nogvl(void *arg) { \
200
+ sign_keypair_call_t *call = (sign_keypair_call_t *)arg; \
201
+ call->result = c_call(call->public_key, call->secret_key, call->seed); \
202
+ return NULL; \
203
+ }
204
+
205
+ PQ_DEFINE_SIGN_KEYPAIR_FROM_SEED_NOGVL(mldsa_44_keypair_from_seed, pq_mldsa44_keypair_from_seed)
206
+ PQ_DEFINE_SIGN_KEYPAIR_FROM_SEED_NOGVL(mldsa_keypair_from_seed, pq_mldsa_keypair_from_seed)
207
+ PQ_DEFINE_SIGN_KEYPAIR_FROM_SEED_NOGVL(mldsa_87_keypair_from_seed, pq_mldsa87_keypair_from_seed)
208
+
209
+ #undef PQ_DEFINE_SIGN_KEYPAIR_FROM_SEED_NOGVL
210
+
211
+ #define PQ_DEFINE_SIGN_NOGVL(rb_name, c_call) \
212
+ static void *pq_##rb_name##_nogvl(void *arg) { \
213
+ sign_call_t *call = (sign_call_t *)arg; \
214
+ call->result = c_call(call->signature, &call->signature_len, call->message, \
215
+ call->message_len, call->secret_key); \
216
+ return NULL; \
217
+ }
218
+
219
+ PQ_DEFINE_SIGN_NOGVL(sign, pq_sign)
220
+ PQ_DEFINE_SIGN_NOGVL(mldsa_44_sign, pq_mldsa44_sign)
221
+ PQ_DEFINE_SIGN_NOGVL(mldsa_87_sign, pq_mldsa87_sign)
222
+
223
+ #undef PQ_DEFINE_SIGN_NOGVL
164
224
 
165
225
  static void *pq_testing_sign_keypair_nogvl(void *arg) {
166
226
  sign_keypair_call_t *call = (sign_keypair_call_t *)arg;
@@ -177,12 +237,43 @@ static void *pq_testing_sign_nogvl(void *arg) {
177
237
  return NULL;
178
238
  }
179
239
 
180
- static void *pq_verify_nogvl(void *arg) {
181
- verify_call_t *call = (verify_call_t *)arg;
182
- call->result = pq_verify(call->signature, call->signature_len, call->message, call->message_len,
183
- call->public_key);
184
- return NULL;
185
- }
240
+ #define PQ_DEFINE_TESTING_KEYPAIR_FROM_SEED(rb_name, c_call) \
241
+ static void *pq_testing_##rb_name##_keypair_nogvl(void *arg) { \
242
+ sign_keypair_call_t *call = (sign_keypair_call_t *)arg; \
243
+ call->result = c_call(call->public_key, call->secret_key, call->seed, call->seed_len); \
244
+ return NULL; \
245
+ }
246
+
247
+ #define PQ_DEFINE_TESTING_SIGN_FROM_SEED(rb_name, c_call) \
248
+ static void *pq_testing_##rb_name##_sign_nogvl(void *arg) { \
249
+ sign_call_t *call = (sign_call_t *)arg; \
250
+ call->result = c_call(call->signature, &call->signature_len, call->message, \
251
+ call->message_len, call->secret_key, call->seed, call->seed_len); \
252
+ return NULL; \
253
+ }
254
+
255
+ PQ_DEFINE_TESTING_KEYPAIR_FROM_SEED(mldsa_44, pq_testing_mldsa44_keypair_from_seed)
256
+ PQ_DEFINE_TESTING_KEYPAIR_FROM_SEED(mldsa_87, pq_testing_mldsa87_keypair_from_seed)
257
+
258
+ PQ_DEFINE_TESTING_SIGN_FROM_SEED(mldsa_44, pq_testing_mldsa44_sign_from_seed)
259
+ PQ_DEFINE_TESTING_SIGN_FROM_SEED(mldsa_87, pq_testing_mldsa87_sign_from_seed)
260
+
261
+ #undef PQ_DEFINE_TESTING_KEYPAIR_FROM_SEED
262
+ #undef PQ_DEFINE_TESTING_SIGN_FROM_SEED
263
+
264
+ #define PQ_DEFINE_VERIFY_NOGVL(rb_name, c_call) \
265
+ static void *pq_##rb_name##_nogvl(void *arg) { \
266
+ verify_call_t *call = (verify_call_t *)arg; \
267
+ call->result = c_call(call->signature, call->signature_len, call->message, \
268
+ call->message_len, call->public_key); \
269
+ return NULL; \
270
+ }
271
+
272
+ PQ_DEFINE_VERIFY_NOGVL(verify, pq_verify)
273
+ PQ_DEFINE_VERIFY_NOGVL(mldsa_44_verify, pq_mldsa44_verify)
274
+ PQ_DEFINE_VERIFY_NOGVL(mldsa_87_verify, pq_mldsa87_verify)
275
+
276
+ #undef PQ_DEFINE_VERIFY_NOGVL
186
277
 
187
278
  static uint8_t *pq_alloc_buffer(size_t len) {
188
279
  if (len == 0) {
@@ -223,6 +314,10 @@ static void pq_wipe_and_free(uint8_t *buffer, size_t len) {
223
314
  }
224
315
  }
225
316
 
317
+ static void pq_free_buffer(uint8_t *buffer) {
318
+ free(buffer);
319
+ }
320
+
226
321
  static void pq_validate_bytes_argument(VALUE value, size_t expected_len, const char *what) {
227
322
  StringValue(value);
228
323
  if ((size_t)RSTRING_LEN(value) != expected_len) {
@@ -465,24 +560,73 @@ __attribute__((noreturn)) static void pq_raise_general_error(int err) {
465
560
  }
466
561
  }
467
562
 
468
- static VALUE pqcrypto_ml_kem_keypair(VALUE self) {
469
- (void)self;
470
- return pq_run_kem_keypair(pq_ml_kem_keypair_nogvl, PQ_MLKEM_PUBLICKEYBYTES,
471
- PQ_MLKEM_SECRETKEYBYTES);
472
- }
563
+ static VALUE pq_run_kem_keypair_from_seed(void *(*nogvl)(void *), VALUE seed, size_t public_key_len,
564
+ size_t secret_key_len) {
565
+ StringValue(seed);
473
566
 
474
- static VALUE pqcrypto_ml_kem_encapsulate(VALUE self, VALUE public_key) {
475
- (void)self;
476
- return pq_run_kem_encapsulate(pq_ml_kem_encapsulate_nogvl, public_key, PQ_MLKEM_PUBLICKEYBYTES,
477
- PQ_MLKEM_CIPHERTEXTBYTES, PQ_MLKEM_SHAREDSECRETBYTES);
478
- }
567
+ if ((size_t)RSTRING_LEN(seed) != 64) {
568
+ rb_raise(rb_eArgError, "ML-KEM seed must be 64 bytes (FIPS 203 d||z)");
569
+ }
479
570
 
480
- static VALUE pqcrypto_ml_kem_decapsulate(VALUE self, VALUE ciphertext, VALUE secret_key) {
481
- (void)self;
482
- return pq_run_kem_decapsulate(pq_ml_kem_decapsulate_nogvl, ciphertext, PQ_MLKEM_CIPHERTEXTBYTES,
483
- secret_key, PQ_MLKEM_SECRETKEYBYTES, PQ_MLKEM_SHAREDSECRETBYTES);
571
+ kem_keypair_call_t call = {0};
572
+ size_t seed_len = 0;
573
+ call.public_key = pq_alloc_buffer(public_key_len);
574
+ call.secret_key = pq_alloc_buffer(secret_key_len);
575
+ call.seed = pq_copy_ruby_string(seed, &seed_len);
576
+ call.seed_len = seed_len;
577
+
578
+ rb_thread_call_without_gvl(nogvl, &call, NULL, NULL);
579
+ pq_wipe_and_free((uint8_t *)call.seed, call.seed_len);
580
+
581
+ if (call.result != PQ_SUCCESS) {
582
+ pq_wipe_and_free(call.secret_key, secret_key_len);
583
+ free(call.public_key);
584
+ pq_raise_general_error(call.result);
585
+ }
586
+
587
+ VALUE result = rb_ary_new2(2);
588
+ rb_ary_push(result, pq_string_from_buffer(call.public_key, public_key_len));
589
+ rb_ary_push(result, pq_string_from_buffer(call.secret_key, secret_key_len));
590
+
591
+ free(call.public_key);
592
+ pq_wipe_and_free(call.secret_key, secret_key_len);
593
+ return result;
484
594
  }
485
595
 
596
+ #define PQ_RUBY_KEM_VARIANTS(X) \
597
+ X(ml_kem, PQ_MLKEM_PUBLICKEYBYTES, PQ_MLKEM_SECRETKEYBYTES, PQ_MLKEM_CIPHERTEXTBYTES, \
598
+ PQ_MLKEM_SHAREDSECRETBYTES) \
599
+ X(ml_kem_512, MLKEM512_PUBLICKEYBYTES, MLKEM512_SECRETKEYBYTES, MLKEM512_CIPHERTEXTBYTES, \
600
+ MLKEM512_SHAREDSECRETBYTES) \
601
+ X(ml_kem_1024, MLKEM1024_PUBLICKEYBYTES, MLKEM1024_SECRETKEYBYTES, MLKEM1024_CIPHERTEXTBYTES, \
602
+ MLKEM1024_SHAREDSECRETBYTES)
603
+
604
+ #define PQ_DEFINE_RUBY_KEM(rb_name, pk_bytes, sk_bytes, ct_bytes, ss_bytes) \
605
+ static VALUE pqcrypto_##rb_name##_keypair(VALUE self) { \
606
+ (void)self; \
607
+ return pq_run_kem_keypair(pq_##rb_name##_keypair_nogvl, pk_bytes, sk_bytes); \
608
+ } \
609
+ static VALUE pqcrypto_##rb_name##_keypair_from_seed(VALUE self, VALUE seed) { \
610
+ (void)self; \
611
+ return pq_run_kem_keypair_from_seed(pq_##rb_name##_keypair_from_seed_nogvl, seed, \
612
+ pk_bytes, sk_bytes); \
613
+ } \
614
+ static VALUE pqcrypto_##rb_name##_encapsulate(VALUE self, VALUE public_key) { \
615
+ (void)self; \
616
+ return pq_run_kem_encapsulate(pq_##rb_name##_encapsulate_nogvl, public_key, pk_bytes, \
617
+ ct_bytes, ss_bytes); \
618
+ } \
619
+ static VALUE pqcrypto_##rb_name##_decapsulate(VALUE self, VALUE ciphertext, \
620
+ VALUE secret_key) { \
621
+ (void)self; \
622
+ return pq_run_kem_decapsulate(pq_##rb_name##_decapsulate_nogvl, ciphertext, ct_bytes, \
623
+ secret_key, sk_bytes, ss_bytes); \
624
+ }
625
+
626
+ PQ_RUBY_KEM_VARIANTS(PQ_DEFINE_RUBY_KEM)
627
+
628
+ #undef PQ_DEFINE_RUBY_KEM
629
+
486
630
  static VALUE pqcrypto_hybrid_kem_keypair(VALUE self) {
487
631
  (void)self;
488
632
  return pq_run_kem_keypair(pq_hybrid_kem_keypair_nogvl, PQ_HYBRID_PUBLICKEYBYTES,
@@ -573,6 +717,61 @@ static VALUE pqcrypto__test_ml_kem_encapsulate_from_seed(VALUE self, VALUE publi
573
717
  return result;
574
718
  }
575
719
 
720
+ static VALUE pq_run_test_kem_encapsulate_from_seed(void *(*nogvl)(void *), VALUE public_key,
721
+ VALUE seed, size_t public_key_len,
722
+ size_t ciphertext_len,
723
+ size_t shared_secret_len) {
724
+ pq_validate_bytes_argument(public_key, public_key_len, "public key");
725
+ StringValue(seed);
726
+
727
+ if ((size_t)RSTRING_LEN(seed) != 32) {
728
+ rb_raise(rb_eArgError, "Deterministic test seed must be 32 bytes");
729
+ }
730
+
731
+ kem_encapsulate_call_t call = {0};
732
+ size_t public_key_copy_len = 0;
733
+ size_t seed_len = 0;
734
+ call.public_key = pq_copy_ruby_string(public_key, &public_key_copy_len);
735
+ call.ciphertext = pq_alloc_buffer(ciphertext_len);
736
+ call.shared_secret = pq_alloc_buffer(shared_secret_len);
737
+ call.seed = pq_copy_ruby_string(seed, &seed_len);
738
+ call.seed_len = seed_len;
739
+
740
+ rb_thread_call_without_gvl(nogvl, &call, NULL, NULL);
741
+ pq_wipe_and_free((uint8_t *)call.public_key, public_key_copy_len);
742
+ pq_wipe_and_free((uint8_t *)call.seed, call.seed_len);
743
+
744
+ if (call.result != PQ_SUCCESS) {
745
+ pq_wipe_and_free(call.shared_secret, shared_secret_len);
746
+ free(call.ciphertext);
747
+ pq_raise_general_error(call.result);
748
+ }
749
+
750
+ VALUE result = rb_ary_new2(2);
751
+ rb_ary_push(result, pq_string_from_buffer(call.ciphertext, ciphertext_len));
752
+ rb_ary_push(result, pq_string_from_buffer(call.shared_secret, shared_secret_len));
753
+
754
+ free(call.ciphertext);
755
+ pq_wipe_and_free(call.shared_secret, shared_secret_len);
756
+ return result;
757
+ }
758
+
759
+ static VALUE pqcrypto__test_ml_kem_512_encapsulate_from_seed(VALUE self, VALUE public_key,
760
+ VALUE seed) {
761
+ (void)self;
762
+ return pq_run_test_kem_encapsulate_from_seed(
763
+ pq_testing_ml_kem_512_encapsulate_nogvl, public_key, seed, MLKEM512_PUBLICKEYBYTES,
764
+ MLKEM512_CIPHERTEXTBYTES, MLKEM512_SHAREDSECRETBYTES);
765
+ }
766
+
767
+ static VALUE pqcrypto__test_ml_kem_1024_encapsulate_from_seed(VALUE self, VALUE public_key,
768
+ VALUE seed) {
769
+ (void)self;
770
+ return pq_run_test_kem_encapsulate_from_seed(
771
+ pq_testing_ml_kem_1024_encapsulate_nogvl, public_key, seed, MLKEM1024_PUBLICKEYBYTES,
772
+ MLKEM1024_CIPHERTEXTBYTES, MLKEM1024_SHAREDSECRETBYTES);
773
+ }
774
+
576
775
  static VALUE pqcrypto__test_sign_keypair_from_seed(VALUE self, VALUE seed) {
577
776
  (void)self;
578
777
  StringValue(seed);
@@ -628,56 +827,187 @@ static VALUE pqcrypto__test_sign_from_seed(VALUE self, VALUE message, VALUE secr
628
827
 
629
828
  rb_thread_call_without_gvl(pq_testing_sign_nogvl, &call, NULL, NULL);
630
829
 
631
- pq_wipe_and_free(call.message, call.message_len);
830
+ pq_free_buffer(call.message);
632
831
  pq_wipe_and_free((uint8_t *)call.secret_key, secret_key_len);
633
832
  pq_wipe_and_free((uint8_t *)call.seed, call.seed_len);
634
833
 
635
834
  if (call.result != PQ_SUCCESS) {
636
- pq_wipe_and_free(call.signature, PQ_MLDSA_BYTES);
835
+ pq_free_buffer(call.signature);
637
836
  pq_raise_general_error(call.result);
638
837
  }
639
838
 
640
839
  VALUE result = pq_string_from_buffer(call.signature, call.signature_len);
641
- pq_wipe_and_free(call.signature, PQ_MLDSA_BYTES);
840
+ pq_free_buffer(call.signature);
642
841
  return result;
643
842
  }
644
843
 
645
- static VALUE pqcrypto_sign_keypair(VALUE self) {
844
+ static VALUE pq_run_test_sign_keypair_from_seed(void *(*nogvl)(void *), VALUE seed,
845
+ size_t public_key_len, size_t secret_key_len) {
846
+ StringValue(seed);
847
+
848
+ if ((size_t)RSTRING_LEN(seed) != 32) {
849
+ rb_raise(rb_eArgError, "Deterministic test seed must be 32 bytes");
850
+ }
851
+
852
+ sign_keypair_call_t call = {0};
853
+ size_t seed_len = 0;
854
+ call.public_key = pq_alloc_buffer(public_key_len);
855
+ call.secret_key = pq_alloc_buffer(secret_key_len);
856
+ call.seed = pq_copy_ruby_string(seed, &seed_len);
857
+ call.seed_len = seed_len;
858
+
859
+ rb_thread_call_without_gvl(nogvl, &call, NULL, NULL);
860
+ pq_wipe_and_free((uint8_t *)call.seed, call.seed_len);
861
+
862
+ if (call.result != PQ_SUCCESS) {
863
+ pq_wipe_and_free(call.secret_key, secret_key_len);
864
+ free(call.public_key);
865
+ pq_raise_general_error(call.result);
866
+ }
867
+
868
+ VALUE result = rb_ary_new2(2);
869
+ rb_ary_push(result, pq_string_from_buffer(call.public_key, public_key_len));
870
+ rb_ary_push(result, pq_string_from_buffer(call.secret_key, secret_key_len));
871
+
872
+ free(call.public_key);
873
+ pq_wipe_and_free(call.secret_key, secret_key_len);
874
+ return result;
875
+ }
876
+
877
+ static VALUE pqcrypto__test_ml_dsa_44_keypair_from_seed(VALUE self, VALUE seed) {
646
878
  (void)self;
647
- return pq_run_sign_keypair(pq_sign_keypair_nogvl, PQ_MLDSA_PUBLICKEYBYTES,
648
- PQ_MLDSA_SECRETKEYBYTES);
879
+ return pq_run_test_sign_keypair_from_seed(pq_testing_mldsa_44_keypair_nogvl, seed,
880
+ MLDSA44_PUBLICKEYBYTES, MLDSA44_SECRETKEYBYTES);
649
881
  }
650
882
 
651
- static VALUE pqcrypto_sign(VALUE self, VALUE message, VALUE secret_key) {
883
+ static VALUE pqcrypto__test_ml_dsa_87_keypair_from_seed(VALUE self, VALUE seed) {
652
884
  (void)self;
653
- pq_validate_bytes_argument(secret_key, PQ_MLDSA_SECRETKEYBYTES, "secret key");
885
+ return pq_run_test_sign_keypair_from_seed(pq_testing_mldsa_87_keypair_nogvl, seed,
886
+ MLDSA87_PUBLICKEYBYTES, MLDSA87_SECRETKEYBYTES);
887
+ }
888
+
889
+ #define PQ_RUBY_MLDSA_KEYPAIR_FROM_SEED_VARIANTS(X) \
890
+ X(ml_dsa_44, mldsa_44_keypair_from_seed, MLDSA44_PUBLICKEYBYTES, MLDSA44_SECRETKEYBYTES) \
891
+ X(ml_dsa, mldsa_keypair_from_seed, PQ_MLDSA_PUBLICKEYBYTES, PQ_MLDSA_SECRETKEYBYTES) \
892
+ X(ml_dsa_87, mldsa_87_keypair_from_seed, MLDSA87_PUBLICKEYBYTES, MLDSA87_SECRETKEYBYTES)
893
+
894
+ #define PQ_DEFINE_RUBY_MLDSA_KEYPAIR_FROM_SEED(rb_name, nogvl_suffix, pk_bytes, sk_bytes) \
895
+ static VALUE pqcrypto_##rb_name##_keypair_from_seed(VALUE self, VALUE seed) { \
896
+ (void)self; \
897
+ return pq_run_test_sign_keypair_from_seed(pq_##nogvl_suffix##_nogvl, seed, pk_bytes, \
898
+ sk_bytes); \
899
+ }
900
+
901
+ PQ_RUBY_MLDSA_KEYPAIR_FROM_SEED_VARIANTS(PQ_DEFINE_RUBY_MLDSA_KEYPAIR_FROM_SEED)
902
+
903
+ #undef PQ_DEFINE_RUBY_MLDSA_KEYPAIR_FROM_SEED
904
+
905
+ static VALUE pq_run_test_sign_from_seed(void *(*nogvl)(void *), VALUE message, VALUE secret_key,
906
+ VALUE seed, size_t secret_key_len, size_t signature_len) {
907
+ pq_validate_bytes_argument(secret_key, secret_key_len, "secret key");
908
+ StringValue(seed);
909
+
910
+ if ((size_t)RSTRING_LEN(seed) != 32) {
911
+ rb_raise(rb_eArgError, "Deterministic test seed must be 32 bytes");
912
+ }
913
+
914
+ sign_call_t call = {0};
915
+ size_t secret_key_copy_len = 0;
916
+ size_t seed_len = 0;
917
+ call.secret_key = pq_copy_ruby_string(secret_key, &secret_key_copy_len);
918
+ call.signature_len = signature_len;
919
+ call.signature = pq_alloc_buffer(signature_len);
920
+ call.message = pq_copy_ruby_string(message, &call.message_len);
921
+ call.seed = pq_copy_ruby_string(seed, &seed_len);
922
+ call.seed_len = seed_len;
923
+
924
+ rb_thread_call_without_gvl(nogvl, &call, NULL, NULL);
925
+
926
+ pq_free_buffer(call.message);
927
+ pq_wipe_and_free((uint8_t *)call.secret_key, secret_key_copy_len);
928
+ pq_wipe_and_free((uint8_t *)call.seed, call.seed_len);
929
+
930
+ if (call.result != PQ_SUCCESS) {
931
+ pq_free_buffer(call.signature);
932
+ pq_raise_general_error(call.result);
933
+ }
934
+
935
+ VALUE result = pq_string_from_buffer(call.signature, call.signature_len);
936
+ pq_free_buffer(call.signature);
937
+ return result;
938
+ }
939
+
940
+ static VALUE pqcrypto__test_ml_dsa_44_sign_from_seed(VALUE self, VALUE message, VALUE secret_key,
941
+ VALUE seed) {
942
+ (void)self;
943
+ return pq_run_test_sign_from_seed(pq_testing_mldsa_44_sign_nogvl, message, secret_key, seed,
944
+ MLDSA44_SECRETKEYBYTES, MLDSA44_BYTES);
945
+ }
946
+
947
+ static VALUE pqcrypto__test_ml_dsa_87_sign_from_seed(VALUE self, VALUE message, VALUE secret_key,
948
+ VALUE seed) {
949
+ (void)self;
950
+ return pq_run_test_sign_from_seed(pq_testing_mldsa_87_sign_nogvl, message, secret_key, seed,
951
+ MLDSA87_SECRETKEYBYTES, MLDSA87_BYTES);
952
+ }
953
+
954
+ #define PQ_DEFINE_RUBY_MLDSA_KEYPAIR(rb_name, nogvl_stem, pk_bytes, sk_bytes) \
955
+ static VALUE pqcrypto_##rb_name(VALUE self) { \
956
+ (void)self; \
957
+ return pq_run_sign_keypair(pq_##nogvl_stem##_nogvl, pk_bytes, sk_bytes); \
958
+ }
959
+
960
+ PQ_DEFINE_RUBY_MLDSA_KEYPAIR(sign_keypair, sign_keypair, PQ_MLDSA_PUBLICKEYBYTES,
961
+ PQ_MLDSA_SECRETKEYBYTES)
962
+ PQ_DEFINE_RUBY_MLDSA_KEYPAIR(ml_dsa_44_keypair, mldsa_44_sign_keypair, MLDSA44_PUBLICKEYBYTES,
963
+ MLDSA44_SECRETKEYBYTES)
964
+ PQ_DEFINE_RUBY_MLDSA_KEYPAIR(ml_dsa_87_keypair, mldsa_87_sign_keypair, MLDSA87_PUBLICKEYBYTES,
965
+ MLDSA87_SECRETKEYBYTES)
966
+
967
+ #undef PQ_DEFINE_RUBY_MLDSA_KEYPAIR
968
+
969
+ static VALUE pq_run_sign(void *(*nogvl)(void *), VALUE message, VALUE secret_key,
970
+ size_t secret_key_len_expected, size_t signature_len_expected) {
971
+ pq_validate_bytes_argument(secret_key, secret_key_len_expected, "secret key");
654
972
 
655
973
  sign_call_t call = {0};
656
974
  size_t secret_key_len = 0;
657
975
  call.secret_key = pq_copy_ruby_string(secret_key, &secret_key_len);
658
- call.signature_len = PQ_MLDSA_BYTES;
659
- call.signature = pq_alloc_buffer(PQ_MLDSA_BYTES);
976
+ call.signature_len = signature_len_expected;
977
+ call.signature = pq_alloc_buffer(signature_len_expected);
660
978
  call.message = pq_copy_ruby_string(message, &call.message_len);
661
979
 
662
- rb_nogvl(pq_sign_nogvl, &call, NULL, NULL, RB_NOGVL_OFFLOAD_SAFE);
980
+ rb_nogvl(nogvl, &call, NULL, NULL, RB_NOGVL_OFFLOAD_SAFE);
663
981
 
664
- pq_wipe_and_free(call.message, call.message_len);
982
+ pq_free_buffer(call.message);
665
983
  pq_wipe_and_free((uint8_t *)call.secret_key, secret_key_len);
666
984
 
667
985
  if (call.result != PQ_SUCCESS) {
668
- pq_wipe_and_free(call.signature, PQ_MLDSA_BYTES);
986
+ pq_free_buffer(call.signature);
669
987
  pq_raise_general_error(call.result);
670
988
  }
671
989
 
672
990
  VALUE result = pq_string_from_buffer(call.signature, call.signature_len);
673
- free(call.signature);
991
+ pq_free_buffer(call.signature);
674
992
  return result;
675
993
  }
676
994
 
677
- static VALUE pqcrypto_verify(VALUE self, VALUE message, VALUE signature, VALUE public_key) {
678
- (void)self;
995
+ #define PQ_DEFINE_RUBY_MLDSA_SIGN(rb_name, nogvl_stem, sk_bytes, sig_bytes) \
996
+ static VALUE pqcrypto_##rb_name(VALUE self, VALUE message, VALUE secret_key) { \
997
+ (void)self; \
998
+ return pq_run_sign(pq_##nogvl_stem##_nogvl, message, secret_key, sk_bytes, sig_bytes); \
999
+ }
1000
+
1001
+ PQ_DEFINE_RUBY_MLDSA_SIGN(sign, sign, PQ_MLDSA_SECRETKEYBYTES, PQ_MLDSA_BYTES)
1002
+ PQ_DEFINE_RUBY_MLDSA_SIGN(ml_dsa_44_sign, mldsa_44_sign, MLDSA44_SECRETKEYBYTES, MLDSA44_BYTES)
1003
+ PQ_DEFINE_RUBY_MLDSA_SIGN(ml_dsa_87_sign, mldsa_87_sign, MLDSA87_SECRETKEYBYTES, MLDSA87_BYTES)
1004
+
1005
+ #undef PQ_DEFINE_RUBY_MLDSA_SIGN
1006
+
1007
+ static VALUE pq_run_verify(void *(*nogvl)(void *), VALUE message, VALUE signature, VALUE public_key,
1008
+ size_t public_key_len_expected) {
679
1009
  StringValue(signature);
680
- pq_validate_bytes_argument(public_key, PQ_MLDSA_PUBLICKEYBYTES, "public key");
1010
+ pq_validate_bytes_argument(public_key, public_key_len_expected, "public key");
681
1011
 
682
1012
  verify_call_t call = {0};
683
1013
  size_t public_key_len = 0;
@@ -687,11 +1017,11 @@ static VALUE pqcrypto_verify(VALUE self, VALUE message, VALUE signature, VALUE p
687
1017
  call.signature_len = signature_len;
688
1018
  call.message = pq_copy_ruby_string(message, &call.message_len);
689
1019
 
690
- rb_nogvl(pq_verify_nogvl, &call, NULL, NULL, RB_NOGVL_OFFLOAD_SAFE);
1020
+ rb_nogvl(nogvl, &call, NULL, NULL, RB_NOGVL_OFFLOAD_SAFE);
691
1021
 
692
- pq_wipe_and_free(call.message, call.message_len);
693
- pq_wipe_and_free((uint8_t *)call.public_key, public_key_len);
694
- pq_wipe_and_free((uint8_t *)call.signature, signature_len);
1022
+ pq_free_buffer(call.message);
1023
+ pq_free_buffer((uint8_t *)call.public_key);
1024
+ pq_free_buffer((uint8_t *)call.signature);
695
1025
 
696
1026
  if (call.result == PQ_SUCCESS) {
697
1027
  return Qtrue;
@@ -702,6 +1032,19 @@ static VALUE pqcrypto_verify(VALUE self, VALUE message, VALUE signature, VALUE p
702
1032
  pq_raise_general_error(call.result);
703
1033
  }
704
1034
 
1035
+ #define PQ_DEFINE_RUBY_MLDSA_VERIFY(rb_name, nogvl_stem, pk_bytes) \
1036
+ static VALUE pqcrypto_##rb_name(VALUE self, VALUE message, VALUE signature, \
1037
+ VALUE public_key) { \
1038
+ (void)self; \
1039
+ return pq_run_verify(pq_##nogvl_stem##_nogvl, message, signature, public_key, pk_bytes); \
1040
+ }
1041
+
1042
+ PQ_DEFINE_RUBY_MLDSA_VERIFY(verify, verify, PQ_MLDSA_PUBLICKEYBYTES)
1043
+ PQ_DEFINE_RUBY_MLDSA_VERIFY(ml_dsa_44_verify, mldsa_44_verify, MLDSA44_PUBLICKEYBYTES)
1044
+ PQ_DEFINE_RUBY_MLDSA_VERIFY(ml_dsa_87_verify, mldsa_87_verify, MLDSA87_PUBLICKEYBYTES)
1045
+
1046
+ #undef PQ_DEFINE_RUBY_MLDSA_VERIFY
1047
+
705
1048
  static VALUE pqcrypto_ct_equals(VALUE self, VALUE a, VALUE b) {
706
1049
  (void)self;
707
1050
  StringValue(a);
@@ -731,19 +1074,319 @@ static VALUE pqcrypto_version(VALUE self) {
731
1074
  return rb_str_new_cstr(pq_version());
732
1075
  }
733
1076
 
1077
+ typedef struct {
1078
+ void *builder;
1079
+ } mu_builder_wrapper_t;
1080
+
1081
+ typedef struct {
1082
+ int result;
1083
+ void *builder;
1084
+ const uint8_t *chunk;
1085
+ size_t chunk_len;
1086
+ } mu_absorb_call_t;
1087
+
1088
+ typedef struct {
1089
+ int result;
1090
+ void *builder;
1091
+ uint8_t *mu_out;
1092
+ } mu_finalize_call_t;
1093
+
1094
+ typedef struct {
1095
+ int result;
1096
+ uint8_t *signature;
1097
+ size_t signature_len;
1098
+ const uint8_t *mu;
1099
+ const uint8_t *secret_key;
1100
+ } sign_mu_call_t;
1101
+
1102
+ typedef struct {
1103
+ int result;
1104
+ const uint8_t *signature;
1105
+ size_t signature_len;
1106
+ const uint8_t *mu;
1107
+ const uint8_t *public_key;
1108
+ } verify_mu_call_t;
1109
+
1110
+ static void mu_builder_wrapper_free(void *ptr) {
1111
+ mu_builder_wrapper_t *wrapper = (mu_builder_wrapper_t *)ptr;
1112
+ if (wrapper == NULL) {
1113
+ return;
1114
+ }
1115
+ if (wrapper->builder != NULL) {
1116
+ pq_mu_builder_release(wrapper->builder);
1117
+ wrapper->builder = NULL;
1118
+ }
1119
+ xfree(wrapper);
1120
+ }
1121
+
1122
+ static size_t mu_builder_wrapper_size(const void *ptr) {
1123
+ (void)ptr;
1124
+ return sizeof(mu_builder_wrapper_t);
1125
+ }
1126
+
1127
+ static const rb_data_type_t mu_builder_data_type = {
1128
+ .wrap_struct_name = "PQCrypto::MLDSA::MuBuilder",
1129
+ .function =
1130
+ {
1131
+ .dmark = NULL,
1132
+ .dfree = mu_builder_wrapper_free,
1133
+ .dsize = mu_builder_wrapper_size,
1134
+ .dcompact = NULL,
1135
+ .reserved = {NULL},
1136
+ },
1137
+ .parent = NULL,
1138
+ .data = NULL,
1139
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY,
1140
+ };
1141
+
1142
+ static mu_builder_wrapper_t *mu_builder_unwrap(VALUE obj) {
1143
+ mu_builder_wrapper_t *wrapper;
1144
+ TypedData_Get_Struct(obj, mu_builder_wrapper_t, &mu_builder_data_type, wrapper);
1145
+ if (wrapper == NULL || wrapper->builder == NULL) {
1146
+ rb_raise(ePQCryptoError, "mu builder used after release");
1147
+ }
1148
+ return wrapper;
1149
+ }
1150
+
1151
+ static VALUE pqcrypto__native_mldsa_extract_tr(VALUE self, VALUE secret_key) {
1152
+ (void)self;
1153
+ pq_validate_bytes_argument(secret_key, PQ_MLDSA_SECRETKEYBYTES, "secret key");
1154
+
1155
+ uint8_t tr[PQ_MLDSA_TRBYTES];
1156
+ int rc = pq_mldsa_extract_tr_from_secret_key(tr, (const uint8_t *)RSTRING_PTR(secret_key));
1157
+ if (rc != PQ_SUCCESS) {
1158
+ pq_secure_wipe(tr, sizeof(tr));
1159
+ pq_raise_general_error(rc);
1160
+ }
1161
+ VALUE result = pq_string_from_buffer(tr, sizeof(tr));
1162
+ pq_secure_wipe(tr, sizeof(tr));
1163
+ return result;
1164
+ }
1165
+
1166
+ static VALUE pqcrypto__native_mldsa_compute_tr(VALUE self, VALUE public_key) {
1167
+ (void)self;
1168
+ pq_validate_bytes_argument(public_key, PQ_MLDSA_PUBLICKEYBYTES, "public key");
1169
+
1170
+ uint8_t tr[PQ_MLDSA_TRBYTES];
1171
+ int rc = pq_mldsa_compute_tr_from_public_key(tr, (const uint8_t *)RSTRING_PTR(public_key));
1172
+ if (rc != PQ_SUCCESS) {
1173
+ pq_raise_general_error(rc);
1174
+ }
1175
+ return pq_string_from_buffer(tr, sizeof(tr));
1176
+ }
1177
+
1178
+ static VALUE pqcrypto__native_mldsa_mu_builder_new(VALUE self, VALUE tr, VALUE ctx) {
1179
+ (void)self;
1180
+ pq_validate_bytes_argument(tr, PQ_MLDSA_TRBYTES, "tr");
1181
+ StringValue(ctx);
1182
+
1183
+ size_t ctxlen = (size_t)RSTRING_LEN(ctx);
1184
+ if (ctxlen > 255) {
1185
+ rb_raise(rb_eArgError, "ML-DSA context length must be <= 255 bytes");
1186
+ }
1187
+
1188
+ void *builder = pq_mu_builder_new();
1189
+ if (builder == NULL) {
1190
+ rb_raise(rb_eNoMemError, "Memory allocation failed (mu builder)");
1191
+ }
1192
+
1193
+ int rc = pq_mu_builder_init(builder, (const uint8_t *)RSTRING_PTR(tr),
1194
+ (const uint8_t *)RSTRING_PTR(ctx), ctxlen);
1195
+ if (rc != PQ_SUCCESS) {
1196
+ pq_mu_builder_release(builder);
1197
+ pq_raise_general_error(rc);
1198
+ }
1199
+
1200
+ mu_builder_wrapper_t *wrapper;
1201
+ VALUE obj =
1202
+ TypedData_Make_Struct(rb_cObject, mu_builder_wrapper_t, &mu_builder_data_type, wrapper);
1203
+ wrapper->builder = builder;
1204
+ return obj;
1205
+ }
1206
+
1207
+ static void *pq_mu_absorb_nogvl(void *arg) {
1208
+ mu_absorb_call_t *call = (mu_absorb_call_t *)arg;
1209
+ call->result = pq_mu_builder_absorb(call->builder, call->chunk, call->chunk_len);
1210
+ return NULL;
1211
+ }
1212
+
1213
+ static VALUE pqcrypto__native_mldsa_mu_builder_update(VALUE self, VALUE builder_obj, VALUE chunk) {
1214
+ (void)self;
1215
+ mu_builder_wrapper_t *wrapper = mu_builder_unwrap(builder_obj);
1216
+ StringValue(chunk);
1217
+
1218
+ size_t chunk_len = (size_t)RSTRING_LEN(chunk);
1219
+ if (chunk_len == 0) {
1220
+ return Qnil;
1221
+ }
1222
+
1223
+ uint8_t *copy = pq_alloc_buffer(chunk_len);
1224
+ memcpy(copy, RSTRING_PTR(chunk), chunk_len);
1225
+
1226
+ mu_absorb_call_t call = {0};
1227
+ call.builder = wrapper->builder;
1228
+ call.chunk = copy;
1229
+ call.chunk_len = chunk_len;
1230
+
1231
+ rb_nogvl(pq_mu_absorb_nogvl, &call, NULL, NULL, RB_NOGVL_OFFLOAD_SAFE);
1232
+ free(copy);
1233
+
1234
+ if (call.result != PQ_SUCCESS) {
1235
+ pq_raise_general_error(call.result);
1236
+ }
1237
+ return Qnil;
1238
+ }
1239
+
1240
+ static void *pq_mu_finalize_nogvl(void *arg) {
1241
+ mu_finalize_call_t *call = (mu_finalize_call_t *)arg;
1242
+ call->result = pq_mu_builder_finalize(call->builder, call->mu_out);
1243
+ return NULL;
1244
+ }
1245
+
1246
+ static VALUE pqcrypto__native_mldsa_mu_builder_finalize(VALUE self, VALUE builder_obj) {
1247
+ (void)self;
1248
+ mu_builder_wrapper_t *wrapper = mu_builder_unwrap(builder_obj);
1249
+
1250
+ uint8_t mu[PQ_MLDSA_MUBYTES];
1251
+
1252
+ mu_finalize_call_t call = {0};
1253
+ call.builder = wrapper->builder;
1254
+ call.mu_out = mu;
1255
+
1256
+ rb_nogvl(pq_mu_finalize_nogvl, &call, NULL, NULL, RB_NOGVL_OFFLOAD_SAFE);
1257
+
1258
+ if (call.result != PQ_SUCCESS) {
1259
+ pq_mu_builder_release(wrapper->builder);
1260
+ }
1261
+ wrapper->builder = NULL;
1262
+
1263
+ if (call.result != PQ_SUCCESS) {
1264
+ pq_secure_wipe(mu, sizeof(mu));
1265
+ pq_raise_general_error(call.result);
1266
+ }
1267
+
1268
+ VALUE result = pq_string_from_buffer(mu, sizeof(mu));
1269
+ pq_secure_wipe(mu, sizeof(mu));
1270
+ return result;
1271
+ }
1272
+
1273
+ static VALUE pqcrypto__native_mldsa_mu_builder_release(VALUE self, VALUE builder_obj) {
1274
+ (void)self;
1275
+ mu_builder_wrapper_t *wrapper;
1276
+ TypedData_Get_Struct(builder_obj, mu_builder_wrapper_t, &mu_builder_data_type, wrapper);
1277
+ if (wrapper != NULL && wrapper->builder != NULL) {
1278
+ pq_mu_builder_release(wrapper->builder);
1279
+ wrapper->builder = NULL;
1280
+ }
1281
+ return Qnil;
1282
+ }
1283
+
1284
+ static void *pq_sign_mu_nogvl(void *arg) {
1285
+ sign_mu_call_t *call = (sign_mu_call_t *)arg;
1286
+ call->result = pq_sign_mu(call->signature, &call->signature_len, call->mu, call->secret_key);
1287
+ return NULL;
1288
+ }
1289
+
1290
+ static VALUE pqcrypto__native_mldsa_sign_mu(VALUE self, VALUE mu, VALUE secret_key) {
1291
+ (void)self;
1292
+ pq_validate_bytes_argument(mu, PQ_MLDSA_MUBYTES, "mu");
1293
+ pq_validate_bytes_argument(secret_key, PQ_MLDSA_SECRETKEYBYTES, "secret key");
1294
+
1295
+ sign_mu_call_t call = {0};
1296
+ size_t secret_key_len = 0;
1297
+ size_t mu_len = 0;
1298
+ uint8_t *mu_copy = pq_copy_ruby_string(mu, &mu_len);
1299
+ uint8_t *sk_copy = pq_copy_ruby_string(secret_key, &secret_key_len);
1300
+
1301
+ call.mu = mu_copy;
1302
+ call.secret_key = sk_copy;
1303
+ call.signature_len = PQ_MLDSA_BYTES;
1304
+ call.signature = pq_alloc_buffer(PQ_MLDSA_BYTES);
1305
+
1306
+ rb_nogvl(pq_sign_mu_nogvl, &call, NULL, NULL, RB_NOGVL_OFFLOAD_SAFE);
1307
+
1308
+ pq_wipe_and_free(mu_copy, mu_len);
1309
+ pq_wipe_and_free(sk_copy, secret_key_len);
1310
+
1311
+ if (call.result != PQ_SUCCESS) {
1312
+ pq_free_buffer(call.signature);
1313
+ pq_raise_general_error(call.result);
1314
+ }
1315
+
1316
+ VALUE result = pq_string_from_buffer(call.signature, call.signature_len);
1317
+ pq_free_buffer(call.signature);
1318
+ return result;
1319
+ }
1320
+
1321
+ static void *pq_verify_mu_nogvl(void *arg) {
1322
+ verify_mu_call_t *call = (verify_mu_call_t *)arg;
1323
+ call->result = pq_verify_mu(call->signature, call->signature_len, call->mu, call->public_key);
1324
+ return NULL;
1325
+ }
1326
+
1327
+ static VALUE pqcrypto__native_mldsa_verify_mu(VALUE self, VALUE mu, VALUE signature,
1328
+ VALUE public_key) {
1329
+ (void)self;
1330
+ StringValue(signature);
1331
+ pq_validate_bytes_argument(mu, PQ_MLDSA_MUBYTES, "mu");
1332
+ pq_validate_bytes_argument(public_key, PQ_MLDSA_PUBLICKEYBYTES, "public key");
1333
+
1334
+ verify_mu_call_t call = {0};
1335
+ size_t public_key_len = 0;
1336
+ size_t signature_len = 0;
1337
+ size_t mu_len = 0;
1338
+ uint8_t *mu_copy = pq_copy_ruby_string(mu, &mu_len);
1339
+ uint8_t *pk_copy = pq_copy_ruby_string(public_key, &public_key_len);
1340
+ uint8_t *sig_copy = pq_copy_ruby_string(signature, &signature_len);
1341
+
1342
+ call.mu = mu_copy;
1343
+ call.public_key = pk_copy;
1344
+ call.signature = sig_copy;
1345
+ call.signature_len = signature_len;
1346
+
1347
+ rb_nogvl(pq_verify_mu_nogvl, &call, NULL, NULL, RB_NOGVL_OFFLOAD_SAFE);
1348
+ pq_wipe_and_free(mu_copy, mu_len);
1349
+ pq_free_buffer(pk_copy);
1350
+ pq_free_buffer(sig_copy);
1351
+
1352
+ if (call.result == PQ_SUCCESS) {
1353
+ return Qtrue;
1354
+ }
1355
+ if (call.result == PQ_ERROR_VERIFY) {
1356
+ return Qfalse;
1357
+ }
1358
+ pq_raise_general_error(call.result);
1359
+ }
1360
+
734
1361
  static void define_constants(void) {
1362
+ rb_define_const(mPQCrypto, "ML_KEM_512_PUBLIC_KEY_BYTES", INT2NUM(MLKEM512_PUBLICKEYBYTES));
1363
+ rb_define_const(mPQCrypto, "ML_KEM_512_SECRET_KEY_BYTES", INT2NUM(MLKEM512_SECRETKEYBYTES));
1364
+ rb_define_const(mPQCrypto, "ML_KEM_512_CIPHERTEXT_BYTES", INT2NUM(MLKEM512_CIPHERTEXTBYTES));
1365
+ rb_define_const(mPQCrypto, "ML_KEM_512_SHARED_SECRET_BYTES",
1366
+ INT2NUM(MLKEM512_SHAREDSECRETBYTES));
735
1367
  rb_define_const(mPQCrypto, "ML_KEM_PUBLIC_KEY_BYTES", INT2NUM(PQ_MLKEM_PUBLICKEYBYTES));
736
1368
  rb_define_const(mPQCrypto, "ML_KEM_SECRET_KEY_BYTES", INT2NUM(PQ_MLKEM_SECRETKEYBYTES));
737
1369
  rb_define_const(mPQCrypto, "ML_KEM_CIPHERTEXT_BYTES", INT2NUM(PQ_MLKEM_CIPHERTEXTBYTES));
738
1370
  rb_define_const(mPQCrypto, "ML_KEM_SHARED_SECRET_BYTES", INT2NUM(PQ_MLKEM_SHAREDSECRETBYTES));
1371
+ rb_define_const(mPQCrypto, "ML_KEM_1024_PUBLIC_KEY_BYTES", INT2NUM(MLKEM1024_PUBLICKEYBYTES));
1372
+ rb_define_const(mPQCrypto, "ML_KEM_1024_SECRET_KEY_BYTES", INT2NUM(MLKEM1024_SECRETKEYBYTES));
1373
+ rb_define_const(mPQCrypto, "ML_KEM_1024_CIPHERTEXT_BYTES", INT2NUM(MLKEM1024_CIPHERTEXTBYTES));
1374
+ rb_define_const(mPQCrypto, "ML_KEM_1024_SHARED_SECRET_BYTES",
1375
+ INT2NUM(MLKEM1024_SHAREDSECRETBYTES));
739
1376
  rb_define_const(mPQCrypto, "HYBRID_KEM_PUBLIC_KEY_BYTES", INT2NUM(PQ_HYBRID_PUBLICKEYBYTES));
740
1377
  rb_define_const(mPQCrypto, "HYBRID_KEM_SECRET_KEY_BYTES", INT2NUM(PQ_HYBRID_SECRETKEYBYTES));
741
1378
  rb_define_const(mPQCrypto, "HYBRID_KEM_CIPHERTEXT_BYTES", INT2NUM(PQ_HYBRID_CIPHERTEXTBYTES));
742
1379
  rb_define_const(mPQCrypto, "HYBRID_KEM_SHARED_SECRET_BYTES",
743
1380
  INT2NUM(PQ_HYBRID_SHAREDSECRETBYTES));
1381
+ rb_define_const(mPQCrypto, "SIGN_44_PUBLIC_KEY_BYTES", INT2NUM(MLDSA44_PUBLICKEYBYTES));
1382
+ rb_define_const(mPQCrypto, "SIGN_44_SECRET_KEY_BYTES", INT2NUM(MLDSA44_SECRETKEYBYTES));
1383
+ rb_define_const(mPQCrypto, "SIGN_44_BYTES", INT2NUM(MLDSA44_BYTES));
744
1384
  rb_define_const(mPQCrypto, "SIGN_PUBLIC_KEY_BYTES", INT2NUM(PQ_MLDSA_PUBLICKEYBYTES));
745
1385
  rb_define_const(mPQCrypto, "SIGN_SECRET_KEY_BYTES", INT2NUM(PQ_MLDSA_SECRETKEYBYTES));
746
1386
  rb_define_const(mPQCrypto, "SIGN_BYTES", INT2NUM(PQ_MLDSA_BYTES));
1387
+ rb_define_const(mPQCrypto, "SIGN_87_PUBLIC_KEY_BYTES", INT2NUM(MLDSA87_PUBLICKEYBYTES));
1388
+ rb_define_const(mPQCrypto, "SIGN_87_SECRET_KEY_BYTES", INT2NUM(MLDSA87_SECRETKEYBYTES));
1389
+ rb_define_const(mPQCrypto, "SIGN_87_BYTES", INT2NUM(MLDSA87_BYTES));
747
1390
  }
748
1391
 
749
1392
  static VALUE pqcrypto_public_key_to_pqc_container_der(VALUE self, VALUE algorithm,
@@ -801,12 +1444,40 @@ void Init_pqcrypto_secure(void) {
801
1444
  pqcrypto__test_ml_kem_keypair_from_seed, 1);
802
1445
  rb_define_module_function(mPQCrypto, "__test_ml_kem_encapsulate_from_seed",
803
1446
  pqcrypto__test_ml_kem_encapsulate_from_seed, 2);
1447
+ rb_define_module_function(mPQCrypto, "__test_ml_kem_512_encapsulate_from_seed",
1448
+ pqcrypto__test_ml_kem_512_encapsulate_from_seed, 2);
1449
+ rb_define_module_function(mPQCrypto, "__test_ml_kem_1024_encapsulate_from_seed",
1450
+ pqcrypto__test_ml_kem_1024_encapsulate_from_seed, 2);
804
1451
  rb_define_module_function(mPQCrypto, "__test_sign_keypair_from_seed",
805
1452
  pqcrypto__test_sign_keypair_from_seed, 1);
1453
+ rb_define_module_function(mPQCrypto, "__test_ml_dsa_44_keypair_from_seed",
1454
+ pqcrypto__test_ml_dsa_44_keypair_from_seed, 1);
1455
+ rb_define_module_function(mPQCrypto, "__test_ml_dsa_87_keypair_from_seed",
1456
+ pqcrypto__test_ml_dsa_87_keypair_from_seed, 1);
806
1457
  rb_define_module_function(mPQCrypto, "__test_sign_from_seed", pqcrypto__test_sign_from_seed, 3);
1458
+ rb_define_module_function(mPQCrypto, "__test_ml_dsa_44_sign_from_seed",
1459
+ pqcrypto__test_ml_dsa_44_sign_from_seed, 3);
1460
+ rb_define_module_function(mPQCrypto, "__test_ml_dsa_87_sign_from_seed",
1461
+ pqcrypto__test_ml_dsa_87_sign_from_seed, 3);
807
1462
  rb_define_module_function(mPQCrypto, "ml_kem_keypair", pqcrypto_ml_kem_keypair, 0);
1463
+ rb_define_module_function(mPQCrypto, "ml_kem_keypair_from_seed",
1464
+ pqcrypto_ml_kem_keypair_from_seed, 1);
808
1465
  rb_define_module_function(mPQCrypto, "ml_kem_encapsulate", pqcrypto_ml_kem_encapsulate, 1);
809
1466
  rb_define_module_function(mPQCrypto, "ml_kem_decapsulate", pqcrypto_ml_kem_decapsulate, 2);
1467
+ rb_define_module_function(mPQCrypto, "ml_kem_512_keypair", pqcrypto_ml_kem_512_keypair, 0);
1468
+ rb_define_module_function(mPQCrypto, "ml_kem_512_keypair_from_seed",
1469
+ pqcrypto_ml_kem_512_keypair_from_seed, 1);
1470
+ rb_define_module_function(mPQCrypto, "ml_kem_512_encapsulate", pqcrypto_ml_kem_512_encapsulate,
1471
+ 1);
1472
+ rb_define_module_function(mPQCrypto, "ml_kem_512_decapsulate", pqcrypto_ml_kem_512_decapsulate,
1473
+ 2);
1474
+ rb_define_module_function(mPQCrypto, "ml_kem_1024_keypair", pqcrypto_ml_kem_1024_keypair, 0);
1475
+ rb_define_module_function(mPQCrypto, "ml_kem_1024_keypair_from_seed",
1476
+ pqcrypto_ml_kem_1024_keypair_from_seed, 1);
1477
+ rb_define_module_function(mPQCrypto, "ml_kem_1024_encapsulate",
1478
+ pqcrypto_ml_kem_1024_encapsulate, 1);
1479
+ rb_define_module_function(mPQCrypto, "ml_kem_1024_decapsulate",
1480
+ pqcrypto_ml_kem_1024_decapsulate, 2);
810
1481
  rb_define_module_function(mPQCrypto, "hybrid_kem_keypair", pqcrypto_hybrid_kem_keypair, 0);
811
1482
  rb_define_module_function(mPQCrypto, "hybrid_kem_encapsulate", pqcrypto_hybrid_kem_encapsulate,
812
1483
  1);
@@ -815,6 +1486,18 @@ void Init_pqcrypto_secure(void) {
815
1486
  rb_define_module_function(mPQCrypto, "sign_keypair", pqcrypto_sign_keypair, 0);
816
1487
  rb_define_module_function(mPQCrypto, "sign", pqcrypto_sign, 2);
817
1488
  rb_define_module_function(mPQCrypto, "verify", pqcrypto_verify, 3);
1489
+ rb_define_module_function(mPQCrypto, "ml_dsa_44_keypair", pqcrypto_ml_dsa_44_keypair, 0);
1490
+ rb_define_module_function(mPQCrypto, "ml_dsa_44_keypair_from_seed",
1491
+ pqcrypto_ml_dsa_44_keypair_from_seed, 1);
1492
+ rb_define_module_function(mPQCrypto, "ml_dsa_keypair_from_seed",
1493
+ pqcrypto_ml_dsa_keypair_from_seed, 1);
1494
+ rb_define_module_function(mPQCrypto, "ml_dsa_44_sign", pqcrypto_ml_dsa_44_sign, 2);
1495
+ rb_define_module_function(mPQCrypto, "ml_dsa_44_verify", pqcrypto_ml_dsa_44_verify, 3);
1496
+ rb_define_module_function(mPQCrypto, "ml_dsa_87_keypair", pqcrypto_ml_dsa_87_keypair, 0);
1497
+ rb_define_module_function(mPQCrypto, "ml_dsa_87_keypair_from_seed",
1498
+ pqcrypto_ml_dsa_87_keypair_from_seed, 1);
1499
+ rb_define_module_function(mPQCrypto, "ml_dsa_87_sign", pqcrypto_ml_dsa_87_sign, 2);
1500
+ rb_define_module_function(mPQCrypto, "ml_dsa_87_verify", pqcrypto_ml_dsa_87_verify, 3);
818
1501
  rb_define_module_function(mPQCrypto, "ct_equals", pqcrypto_ct_equals, 2);
819
1502
  rb_define_module_function(mPQCrypto, "secure_wipe", pqcrypto_secure_wipe, 1);
820
1503
  rb_define_module_function(mPQCrypto, "version", pqcrypto_version, 0);
@@ -834,6 +1517,22 @@ void Init_pqcrypto_secure(void) {
834
1517
  pqcrypto_secret_key_from_pqc_container_der, 1);
835
1518
  rb_define_module_function(mPQCrypto, "secret_key_from_pqc_container_pem",
836
1519
  pqcrypto_secret_key_from_pqc_container_pem, 1);
1520
+ rb_define_module_function(mPQCrypto, "_native_mldsa_extract_tr",
1521
+ pqcrypto__native_mldsa_extract_tr, 1);
1522
+ rb_define_module_function(mPQCrypto, "_native_mldsa_compute_tr",
1523
+ pqcrypto__native_mldsa_compute_tr, 1);
1524
+ rb_define_module_function(mPQCrypto, "_native_mldsa_mu_builder_new",
1525
+ pqcrypto__native_mldsa_mu_builder_new, 2);
1526
+ rb_define_module_function(mPQCrypto, "_native_mldsa_mu_builder_update",
1527
+ pqcrypto__native_mldsa_mu_builder_update, 2);
1528
+ rb_define_module_function(mPQCrypto, "_native_mldsa_mu_builder_finalize",
1529
+ pqcrypto__native_mldsa_mu_builder_finalize, 1);
1530
+ rb_define_module_function(mPQCrypto, "_native_mldsa_mu_builder_release",
1531
+ pqcrypto__native_mldsa_mu_builder_release, 1);
1532
+ rb_define_module_function(mPQCrypto, "_native_mldsa_sign_mu", pqcrypto__native_mldsa_sign_mu,
1533
+ 2);
1534
+ rb_define_module_function(mPQCrypto, "_native_mldsa_verify_mu",
1535
+ pqcrypto__native_mldsa_verify_mu, 3);
837
1536
 
838
1537
  define_constants();
839
1538
  }