pq_crypto 0.5.2 → 0.6.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.
@@ -1,6 +1,13 @@
1
+ #if defined(__clang__) || defined(__GNUC__)
2
+ #pragma GCC diagnostic push
3
+ #pragma GCC diagnostic ignored "-Wunused-parameter"
4
+ #endif
1
5
  #include <ruby.h>
2
6
  #include <ruby/thread.h>
3
7
  #include <ruby/encoding.h>
8
+ #if defined(__clang__) || defined(__GNUC__)
9
+ #pragma GCC diagnostic pop
10
+ #endif
4
11
  #include <stdlib.h>
5
12
  #include <string.h>
6
13
 
@@ -10,7 +17,9 @@
10
17
  #include "pqcrypto_secure.h"
11
18
 
12
19
  #ifndef RB_NOGVL_OFFLOAD_SAFE
13
- #define RB_NOGVL_OFFLOAD_SAFE 0
20
+ #define PQ_RB_NOGVL_OFFLOAD_SAFE 0
21
+ #else
22
+ #define PQ_RB_NOGVL_OFFLOAD_SAFE RB_NOGVL_OFFLOAD_SAFE
14
23
  #endif
15
24
 
16
25
  #define PQ_MU_ABSORB_NOGVL_MIN_BYTES 16384
@@ -72,6 +81,8 @@ typedef struct {
72
81
  size_t signature_len;
73
82
  uint8_t *message;
74
83
  size_t message_len;
84
+ uint8_t *context;
85
+ size_t context_len;
75
86
  const uint8_t *secret_key;
76
87
  const uint8_t *seed;
77
88
  size_t seed_len;
@@ -83,6 +94,8 @@ typedef struct {
83
94
  size_t signature_len;
84
95
  uint8_t *message;
85
96
  size_t message_len;
97
+ uint8_t *context;
98
+ size_t context_len;
86
99
  const uint8_t *public_key;
87
100
  } verify_call_t;
88
101
 
@@ -144,6 +157,52 @@ static const char *const PQC_CONTAINER_ALGORITHMS[] = {
144
157
  static ID pqc_container_algorithm_ids[sizeof(PQC_CONTAINER_ALGORITHMS) /
145
158
  sizeof(PQC_CONTAINER_ALGORITHMS[0])];
146
159
 
160
+ typedef int (*pq_mldsa_pk_from_sk_fn)(uint8_t *, const uint8_t *);
161
+ typedef int (*pq_mldsa_signature_extmu_fn)(uint8_t *, size_t *, const uint8_t *, const uint8_t *);
162
+ typedef int (*pq_mldsa_verify_extmu_fn)(const uint8_t *, size_t, const uint8_t *, const uint8_t *);
163
+
164
+ typedef struct {
165
+ const char *name;
166
+ size_t public_key_len;
167
+ size_t secret_key_len;
168
+ size_t signature_len;
169
+ pq_mldsa_pk_from_sk_fn pk_from_sk;
170
+ pq_mldsa_signature_extmu_fn signature_extmu;
171
+ pq_mldsa_verify_extmu_fn verify_extmu;
172
+ } pq_mldsa_profile_t;
173
+
174
+ static const pq_mldsa_profile_t MLDSA_PROFILES[] = {
175
+ {"ml_dsa_44", MLDSA44_PUBLICKEYBYTES, MLDSA44_SECRETKEYBYTES, MLDSA44_BYTES,
176
+ pqcr_mldsa44_pk_from_sk, pqcr_mldsa44_signature_extmu, pqcr_mldsa44_verify_extmu},
177
+ {"ml_dsa_65", MLDSA65_PUBLICKEYBYTES, MLDSA65_SECRETKEYBYTES, MLDSA65_BYTES,
178
+ pqcr_mldsa65_pk_from_sk, pqcr_mldsa65_signature_extmu, pqcr_mldsa65_verify_extmu},
179
+ {"ml_dsa_87", MLDSA87_PUBLICKEYBYTES, MLDSA87_SECRETKEYBYTES, MLDSA87_BYTES,
180
+ pqcr_mldsa87_pk_from_sk, pqcr_mldsa87_signature_extmu, pqcr_mldsa87_verify_extmu},
181
+ };
182
+
183
+ static const pq_mldsa_profile_t *pq_mldsa_profile_from_value(VALUE algorithm) {
184
+ ID id;
185
+ const char *name;
186
+
187
+ if (!SYMBOL_P(algorithm)) {
188
+ rb_raise(rb_eArgError, "ML-DSA algorithm must be a Symbol");
189
+ }
190
+
191
+ id = SYM2ID(algorithm);
192
+ name = rb_id2name(id);
193
+ if (!name) {
194
+ rb_raise(rb_eArgError, "Invalid ML-DSA algorithm symbol");
195
+ }
196
+
197
+ for (size_t i = 0; i < sizeof(MLDSA_PROFILES) / sizeof(MLDSA_PROFILES[0]); ++i) {
198
+ if (strcmp(name, MLDSA_PROFILES[i].name) == 0) {
199
+ return &MLDSA_PROFILES[i];
200
+ }
201
+ }
202
+
203
+ rb_raise(ePQCryptoError, "Unsupported ML-DSA algorithm: %s", name);
204
+ }
205
+
147
206
  static void pq_init_algorithm_ids(void) {
148
207
  for (size_t i = 0; i < sizeof(PQC_CONTAINER_ALGORITHMS) / sizeof(PQC_CONTAINER_ALGORITHMS[0]);
149
208
  ++i) {
@@ -154,8 +213,8 @@ static void pq_init_algorithm_ids(void) {
154
213
  static const char *pq_algorithm_symbol_to_cstr(VALUE algorithm) {
155
214
  if (SYMBOL_P(algorithm)) {
156
215
  ID id = SYM2ID(algorithm);
157
- for (size_t i = 0; i < sizeof(PQC_CONTAINER_ALGORITHMS) / sizeof(PQC_CONTAINER_ALGORITHMS[0]);
158
- ++i) {
216
+ for (size_t i = 0;
217
+ i < sizeof(PQC_CONTAINER_ALGORITHMS) / sizeof(PQC_CONTAINER_ALGORITHMS[0]); ++i) {
159
218
  if (id == pqc_container_algorithm_ids[i]) {
160
219
  return PQC_CONTAINER_ALGORITHMS[i];
161
220
  }
@@ -164,8 +223,8 @@ static const char *pq_algorithm_symbol_to_cstr(VALUE algorithm) {
164
223
  VALUE str = StringValue(algorithm);
165
224
  const char *ptr = RSTRING_PTR(str);
166
225
  size_t len = (size_t)RSTRING_LEN(str);
167
- for (size_t i = 0; i < sizeof(PQC_CONTAINER_ALGORITHMS) / sizeof(PQC_CONTAINER_ALGORITHMS[0]);
168
- ++i) {
226
+ for (size_t i = 0;
227
+ i < sizeof(PQC_CONTAINER_ALGORITHMS) / sizeof(PQC_CONTAINER_ALGORITHMS[0]); ++i) {
169
228
  size_t algorithm_len = strlen(PQC_CONTAINER_ALGORITHMS[i]);
170
229
  if (len == algorithm_len && memcmp(ptr, PQC_CONTAINER_ALGORITHMS[i], len) == 0) {
171
230
  return PQC_CONTAINER_ALGORITHMS[i];
@@ -272,16 +331,16 @@ static void *pq_hybrid_kem_decapsulate_nogvl(void *arg) {
272
331
 
273
332
  static void *pq_hybrid_kem_decapsulate_expanded_nogvl(void *arg) {
274
333
  kem_decapsulate_call_t *call = (kem_decapsulate_call_t *)arg;
275
- call->result = pq_hybrid_kem_decapsulate_expanded(call->shared_secret, call->ciphertext,
276
- call->secret_key);
334
+ call->result =
335
+ pq_hybrid_kem_decapsulate_expanded(call->shared_secret, call->ciphertext, call->secret_key);
277
336
  return NULL;
278
337
  }
279
338
 
280
339
  static void *pq_hybrid_kem_decapsulate_expanded_pkey_nogvl(void *arg) {
281
- hybrid_decapsulate_expanded_pkey_call_t *call =
282
- (hybrid_decapsulate_expanded_pkey_call_t *)arg;
283
- call->result = pq_hybrid_kem_decapsulate_expanded_pkey(
284
- call->shared_secret, call->ciphertext, call->expanded_secret_key, call->x25519_private_pkey);
340
+ hybrid_decapsulate_expanded_pkey_call_t *call = (hybrid_decapsulate_expanded_pkey_call_t *)arg;
341
+ call->result = pq_hybrid_kem_decapsulate_expanded_pkey(call->shared_secret, call->ciphertext,
342
+ call->expanded_secret_key,
343
+ call->x25519_private_pkey);
285
344
  return NULL;
286
345
  }
287
346
 
@@ -311,12 +370,13 @@ PQ_DEFINE_SIGN_KEYPAIR_FROM_SEED_NOGVL(mldsa_87_keypair_from_seed, pq_mldsa87_ke
311
370
 
312
371
  #undef PQ_DEFINE_SIGN_KEYPAIR_FROM_SEED_NOGVL
313
372
 
314
- #define PQ_DEFINE_SIGN_NOGVL(rb_name, c_call) \
315
- static void *pq_##rb_name##_nogvl(void *arg) { \
316
- sign_call_t *call = (sign_call_t *)arg; \
317
- call->result = c_call(call->signature, &call->signature_len, call->message, \
318
- call->message_len, call->secret_key); \
319
- return NULL; \
373
+ #define PQ_DEFINE_SIGN_NOGVL(rb_name, c_call) \
374
+ static void *pq_##rb_name##_nogvl(void *arg) { \
375
+ sign_call_t *call = (sign_call_t *)arg; \
376
+ call->result = \
377
+ c_call(call->signature, &call->signature_len, call->message, call->message_len, \
378
+ call->context, call->context_len, call->secret_key); \
379
+ return NULL; \
320
380
  }
321
381
 
322
382
  PQ_DEFINE_SIGN_NOGVL(sign, pq_sign)
@@ -364,12 +424,13 @@ PQ_DEFINE_TESTING_SIGN_FROM_SEED(mldsa_87, pq_testing_mldsa87_sign_from_seed)
364
424
  #undef PQ_DEFINE_TESTING_KEYPAIR_FROM_SEED
365
425
  #undef PQ_DEFINE_TESTING_SIGN_FROM_SEED
366
426
 
367
- #define PQ_DEFINE_VERIFY_NOGVL(rb_name, c_call) \
368
- static void *pq_##rb_name##_nogvl(void *arg) { \
369
- verify_call_t *call = (verify_call_t *)arg; \
370
- call->result = c_call(call->signature, call->signature_len, call->message, \
371
- call->message_len, call->public_key); \
372
- return NULL; \
427
+ #define PQ_DEFINE_VERIFY_NOGVL(rb_name, c_call) \
428
+ static void *pq_##rb_name##_nogvl(void *arg) { \
429
+ verify_call_t *call = (verify_call_t *)arg; \
430
+ call->result = \
431
+ c_call(call->signature, call->signature_len, call->message, call->message_len, \
432
+ call->context, call->context_len, call->public_key); \
433
+ return NULL; \
373
434
  }
374
435
 
375
436
  PQ_DEFINE_VERIFY_NOGVL(verify, pq_verify)
@@ -803,6 +864,26 @@ static VALUE pqcrypto_hybrid_kem_expand_secret_key_object(VALUE self, VALUE secr
803
864
  return obj;
804
865
  }
805
866
 
867
+ static VALUE pqcrypto_hybrid_kem_expanded_secret_key_wipe(VALUE self,
868
+ VALUE expanded_secret_key_obj) {
869
+ (void)self;
870
+ hybrid_expanded_key_wrapper_t *wrapper;
871
+
872
+ TypedData_Get_Struct(expanded_secret_key_obj, hybrid_expanded_key_wrapper_t,
873
+ &hybrid_expanded_key_data_type, wrapper);
874
+ if (!wrapper) {
875
+ return Qnil;
876
+ }
877
+
878
+ pq_secure_wipe(wrapper->expanded_secret_key, sizeof(wrapper->expanded_secret_key));
879
+ if (wrapper->x25519_private_pkey) {
880
+ EVP_PKEY_free(wrapper->x25519_private_pkey);
881
+ wrapper->x25519_private_pkey = NULL;
882
+ }
883
+
884
+ return Qnil;
885
+ }
886
+
806
887
  static VALUE pqcrypto_hybrid_kem_decapsulate(VALUE self, VALUE ciphertext, VALUE secret_key) {
807
888
  (void)self;
808
889
  return pq_run_kem_decapsulate(pq_hybrid_kem_decapsulate_nogvl, ciphertext,
@@ -815,12 +896,11 @@ static VALUE pqcrypto_hybrid_kem_decapsulate_expanded(VALUE self, VALUE cipherte
815
896
  (void)self;
816
897
  return pq_run_kem_decapsulate(pq_hybrid_kem_decapsulate_expanded_nogvl, ciphertext,
817
898
  PQ_HYBRID_CIPHERTEXTBYTES, expanded_secret_key,
818
- PQ_HYBRID_EXPANDED_SECRETKEYBYTES,
819
- PQ_HYBRID_SHAREDSECRETBYTES);
899
+ PQ_HYBRID_EXPANDED_SECRETKEYBYTES, PQ_HYBRID_SHAREDSECRETBYTES);
820
900
  }
821
901
 
822
902
  static VALUE pqcrypto_hybrid_kem_decapsulate_expanded_object(VALUE self, VALUE ciphertext,
823
- VALUE expanded_secret_key_obj) {
903
+ VALUE expanded_secret_key_obj) {
824
904
  (void)self;
825
905
  hybrid_expanded_key_wrapper_t *wrapper = hybrid_expanded_key_unwrap(expanded_secret_key_obj);
826
906
  hybrid_decapsulate_expanded_pkey_call_t call = {0};
@@ -1173,20 +1253,28 @@ PQ_DEFINE_RUBY_MLDSA_KEYPAIR(ml_dsa_87_keypair, mldsa_87_sign_keypair, MLDSA87_P
1173
1253
 
1174
1254
  #undef PQ_DEFINE_RUBY_MLDSA_KEYPAIR
1175
1255
 
1176
- static VALUE pq_run_sign(void *(*nogvl)(void *), VALUE message, VALUE secret_key,
1256
+ static VALUE pq_run_sign(void *(*nogvl)(void *), VALUE message, VALUE secret_key, VALUE context,
1177
1257
  size_t secret_key_len_expected, size_t signature_len_expected) {
1178
1258
  pq_validate_bytes_argument(secret_key, secret_key_len_expected, "secret key");
1259
+ StringValue(context);
1260
+ if (RSTRING_LEN(context) > 255) {
1261
+ rb_raise(rb_eArgError, "ML-DSA context length must be <= 255 bytes");
1262
+ }
1179
1263
 
1180
1264
  sign_call_t call = {0};
1181
1265
  size_t secret_key_len = 0;
1266
+ size_t context_len = 0;
1182
1267
  call.secret_key = pq_copy_ruby_string(secret_key, &secret_key_len);
1183
1268
  call.signature_len = signature_len_expected;
1184
1269
  call.signature = pq_alloc_buffer(signature_len_expected);
1185
1270
  call.message = pq_copy_ruby_string(message, &call.message_len);
1271
+ call.context = pq_copy_ruby_string(context, &context_len);
1272
+ call.context_len = context_len;
1186
1273
 
1187
- rb_nogvl(nogvl, &call, NULL, NULL, RB_NOGVL_OFFLOAD_SAFE);
1274
+ rb_nogvl(nogvl, &call, NULL, NULL, PQ_RB_NOGVL_OFFLOAD_SAFE);
1188
1275
 
1189
1276
  pq_free_buffer(call.message);
1277
+ pq_free_buffer(call.context);
1190
1278
  pq_wipe_and_free((uint8_t *)call.secret_key, secret_key_len);
1191
1279
 
1192
1280
  if (call.result != PQ_SUCCESS) {
@@ -1199,10 +1287,16 @@ static VALUE pq_run_sign(void *(*nogvl)(void *), VALUE message, VALUE secret_key
1199
1287
  return result;
1200
1288
  }
1201
1289
 
1202
- #define PQ_DEFINE_RUBY_MLDSA_SIGN(rb_name, nogvl_stem, sk_bytes, sig_bytes) \
1203
- static VALUE pqcrypto_##rb_name(VALUE self, VALUE message, VALUE secret_key) { \
1204
- (void)self; \
1205
- return pq_run_sign(pq_##nogvl_stem##_nogvl, message, secret_key, sk_bytes, sig_bytes); \
1290
+ #define PQ_DEFINE_RUBY_MLDSA_SIGN(rb_name, nogvl_stem, sk_bytes, sig_bytes) \
1291
+ static VALUE pqcrypto_##rb_name(int argc, VALUE *argv, VALUE self) { \
1292
+ (void)self; \
1293
+ VALUE message, secret_key, context; \
1294
+ rb_scan_args(argc, argv, "21", &message, &secret_key, &context); \
1295
+ if (NIL_P(context)) { \
1296
+ context = rb_str_new("", 0); \
1297
+ } \
1298
+ return pq_run_sign(pq_##nogvl_stem##_nogvl, message, secret_key, context, sk_bytes, \
1299
+ sig_bytes); \
1206
1300
  }
1207
1301
 
1208
1302
  PQ_DEFINE_RUBY_MLDSA_SIGN(sign, sign, PQ_MLDSA_SECRETKEYBYTES, PQ_MLDSA_BYTES)
@@ -1212,21 +1306,29 @@ PQ_DEFINE_RUBY_MLDSA_SIGN(ml_dsa_87_sign, mldsa_87_sign, MLDSA87_SECRETKEYBYTES,
1212
1306
  #undef PQ_DEFINE_RUBY_MLDSA_SIGN
1213
1307
 
1214
1308
  static VALUE pq_run_verify(void *(*nogvl)(void *), VALUE message, VALUE signature, VALUE public_key,
1215
- size_t public_key_len_expected) {
1309
+ VALUE context, size_t public_key_len_expected) {
1216
1310
  StringValue(signature);
1217
1311
  pq_validate_bytes_argument(public_key, public_key_len_expected, "public key");
1312
+ StringValue(context);
1313
+ if (RSTRING_LEN(context) > 255) {
1314
+ rb_raise(rb_eArgError, "ML-DSA context length must be <= 255 bytes");
1315
+ }
1218
1316
 
1219
1317
  verify_call_t call = {0};
1220
1318
  size_t public_key_len = 0;
1221
1319
  size_t signature_len = 0;
1320
+ size_t context_len = 0;
1222
1321
  call.public_key = pq_copy_ruby_string(public_key, &public_key_len);
1223
1322
  call.signature = pq_copy_ruby_string(signature, &signature_len);
1224
1323
  call.signature_len = signature_len;
1225
1324
  call.message = pq_copy_ruby_string(message, &call.message_len);
1325
+ call.context = pq_copy_ruby_string(context, &context_len);
1326
+ call.context_len = context_len;
1226
1327
 
1227
- rb_nogvl(nogvl, &call, NULL, NULL, RB_NOGVL_OFFLOAD_SAFE);
1328
+ rb_nogvl(nogvl, &call, NULL, NULL, PQ_RB_NOGVL_OFFLOAD_SAFE);
1228
1329
 
1229
1330
  pq_free_buffer(call.message);
1331
+ pq_free_buffer(call.context);
1230
1332
  pq_free_buffer((uint8_t *)call.public_key);
1231
1333
  pq_free_buffer((uint8_t *)call.signature);
1232
1334
 
@@ -1239,11 +1341,16 @@ static VALUE pq_run_verify(void *(*nogvl)(void *), VALUE message, VALUE signatur
1239
1341
  pq_raise_general_error(call.result);
1240
1342
  }
1241
1343
 
1242
- #define PQ_DEFINE_RUBY_MLDSA_VERIFY(rb_name, nogvl_stem, pk_bytes) \
1243
- static VALUE pqcrypto_##rb_name(VALUE self, VALUE message, VALUE signature, \
1244
- VALUE public_key) { \
1245
- (void)self; \
1246
- return pq_run_verify(pq_##nogvl_stem##_nogvl, message, signature, public_key, pk_bytes); \
1344
+ #define PQ_DEFINE_RUBY_MLDSA_VERIFY(rb_name, nogvl_stem, pk_bytes) \
1345
+ static VALUE pqcrypto_##rb_name(int argc, VALUE *argv, VALUE self) { \
1346
+ (void)self; \
1347
+ VALUE message, signature, public_key, context; \
1348
+ rb_scan_args(argc, argv, "31", &message, &signature, &public_key, &context); \
1349
+ if (NIL_P(context)) { \
1350
+ context = rb_str_new("", 0); \
1351
+ } \
1352
+ return pq_run_verify(pq_##nogvl_stem##_nogvl, message, signature, public_key, context, \
1353
+ pk_bytes); \
1247
1354
  }
1248
1355
 
1249
1356
  PQ_DEFINE_RUBY_MLDSA_VERIFY(verify, verify, PQ_MLDSA_PUBLICKEYBYTES)
@@ -1304,6 +1411,7 @@ typedef struct {
1304
1411
  size_t signature_len;
1305
1412
  const uint8_t *mu;
1306
1413
  const uint8_t *secret_key;
1414
+ pq_mldsa_signature_extmu_fn signature_extmu;
1307
1415
  } sign_mu_call_t;
1308
1416
 
1309
1417
  typedef struct {
@@ -1312,6 +1420,8 @@ typedef struct {
1312
1420
  size_t signature_len;
1313
1421
  const uint8_t *mu;
1314
1422
  const uint8_t *public_key;
1423
+ size_t expected_signature_len;
1424
+ pq_mldsa_verify_extmu_fn verify_extmu;
1315
1425
  } verify_mu_call_t;
1316
1426
 
1317
1427
  static void mu_builder_wrapper_free(void *ptr) {
@@ -1355,12 +1465,25 @@ static mu_builder_wrapper_t *mu_builder_unwrap(VALUE obj) {
1355
1465
  return wrapper;
1356
1466
  }
1357
1467
 
1358
- static VALUE pqcrypto__native_mldsa_extract_tr(VALUE self, VALUE secret_key) {
1468
+ static VALUE pqcrypto__native_mldsa_extract_tr(int argc, VALUE *argv, VALUE self) {
1359
1469
  (void)self;
1360
- pq_validate_bytes_argument(secret_key, PQ_MLDSA_SECRETKEYBYTES, "secret key");
1470
+ VALUE algorithm = ID2SYM(rb_intern("ml_dsa_65"));
1471
+ VALUE secret_key;
1472
+ if (argc == 1) {
1473
+ secret_key = argv[0];
1474
+ } else if (argc == 2) {
1475
+ algorithm = argv[0];
1476
+ secret_key = argv[1];
1477
+ } else {
1478
+ rb_raise(rb_eArgError, "wrong number of arguments (given %d, expected 1..2)", argc);
1479
+ }
1480
+
1481
+ const pq_mldsa_profile_t *profile = pq_mldsa_profile_from_value(algorithm);
1482
+ pq_validate_bytes_argument(secret_key, profile->secret_key_len, "secret key");
1361
1483
 
1362
1484
  uint8_t tr[PQ_MLDSA_TRBYTES];
1363
- int rc = pq_mldsa_extract_tr_from_secret_key(tr, (const uint8_t *)RSTRING_PTR(secret_key));
1485
+ int rc = pq_mldsa_extract_tr_from_secret_key(tr, (const uint8_t *)RSTRING_PTR(secret_key),
1486
+ profile->public_key_len, profile->pk_from_sk);
1364
1487
  if (rc != PQ_SUCCESS) {
1365
1488
  pq_secure_wipe(tr, sizeof(tr));
1366
1489
  pq_raise_general_error(rc);
@@ -1370,12 +1493,25 @@ static VALUE pqcrypto__native_mldsa_extract_tr(VALUE self, VALUE secret_key) {
1370
1493
  return result;
1371
1494
  }
1372
1495
 
1373
- static VALUE pqcrypto__native_mldsa_compute_tr(VALUE self, VALUE public_key) {
1496
+ static VALUE pqcrypto__native_mldsa_compute_tr(int argc, VALUE *argv, VALUE self) {
1374
1497
  (void)self;
1375
- pq_validate_bytes_argument(public_key, PQ_MLDSA_PUBLICKEYBYTES, "public key");
1498
+ VALUE algorithm = ID2SYM(rb_intern("ml_dsa_65"));
1499
+ VALUE public_key;
1500
+ if (argc == 1) {
1501
+ public_key = argv[0];
1502
+ } else if (argc == 2) {
1503
+ algorithm = argv[0];
1504
+ public_key = argv[1];
1505
+ } else {
1506
+ rb_raise(rb_eArgError, "wrong number of arguments (given %d, expected 1..2)", argc);
1507
+ }
1508
+
1509
+ const pq_mldsa_profile_t *profile = pq_mldsa_profile_from_value(algorithm);
1510
+ pq_validate_bytes_argument(public_key, profile->public_key_len, "public key");
1376
1511
 
1377
1512
  uint8_t tr[PQ_MLDSA_TRBYTES];
1378
- int rc = pq_mldsa_compute_tr_from_public_key(tr, (const uint8_t *)RSTRING_PTR(public_key));
1513
+ int rc = pq_mldsa_compute_tr_from_public_key(tr, (const uint8_t *)RSTRING_PTR(public_key),
1514
+ profile->public_key_len);
1379
1515
  if (rc != PQ_SUCCESS) {
1380
1516
  pq_raise_general_error(rc);
1381
1517
  }
@@ -1428,8 +1564,8 @@ static VALUE pqcrypto__native_mldsa_mu_builder_update(VALUE self, VALUE builder_
1428
1564
  }
1429
1565
 
1430
1566
  if (chunk_len < PQ_MU_ABSORB_NOGVL_MIN_BYTES) {
1431
- int rc = pq_mu_builder_absorb(wrapper->builder, (const uint8_t *)RSTRING_PTR(chunk),
1432
- chunk_len);
1567
+ int rc =
1568
+ pq_mu_builder_absorb(wrapper->builder, (const uint8_t *)RSTRING_PTR(chunk), chunk_len);
1433
1569
  if (rc != PQ_SUCCESS) {
1434
1570
  pq_raise_general_error(rc);
1435
1571
  }
@@ -1444,7 +1580,7 @@ static VALUE pqcrypto__native_mldsa_mu_builder_update(VALUE self, VALUE builder_
1444
1580
  call.chunk = copy;
1445
1581
  call.chunk_len = chunk_len;
1446
1582
 
1447
- rb_nogvl(pq_mu_absorb_nogvl, &call, NULL, NULL, RB_NOGVL_OFFLOAD_SAFE);
1583
+ rb_nogvl(pq_mu_absorb_nogvl, &call, NULL, NULL, PQ_RB_NOGVL_OFFLOAD_SAFE);
1448
1584
  free(copy);
1449
1585
 
1450
1586
  if (call.result != PQ_SUCCESS) {
@@ -1469,7 +1605,7 @@ static VALUE pqcrypto__native_mldsa_mu_builder_finalize(VALUE self, VALUE builde
1469
1605
  call.builder = wrapper->builder;
1470
1606
  call.mu_out = mu;
1471
1607
 
1472
- rb_nogvl(pq_mu_finalize_nogvl, &call, NULL, NULL, RB_NOGVL_OFFLOAD_SAFE);
1608
+ rb_nogvl(pq_mu_finalize_nogvl, &call, NULL, NULL, PQ_RB_NOGVL_OFFLOAD_SAFE);
1473
1609
 
1474
1610
  wrapper->builder = NULL;
1475
1611
 
@@ -1496,14 +1632,29 @@ static VALUE pqcrypto__native_mldsa_mu_builder_release(VALUE self, VALUE builder
1496
1632
 
1497
1633
  static void *pq_sign_mu_nogvl(void *arg) {
1498
1634
  sign_mu_call_t *call = (sign_mu_call_t *)arg;
1499
- call->result = pq_sign_mu(call->signature, &call->signature_len, call->mu, call->secret_key);
1635
+ call->result = pq_sign_mu(call->signature, &call->signature_len, call->mu, call->secret_key,
1636
+ call->signature_extmu);
1500
1637
  return NULL;
1501
1638
  }
1502
1639
 
1503
- static VALUE pqcrypto__native_mldsa_sign_mu(VALUE self, VALUE mu, VALUE secret_key) {
1640
+ static VALUE pqcrypto__native_mldsa_sign_mu(int argc, VALUE *argv, VALUE self) {
1504
1641
  (void)self;
1642
+ VALUE algorithm = ID2SYM(rb_intern("ml_dsa_65"));
1643
+ VALUE mu, secret_key;
1644
+ if (argc == 2) {
1645
+ mu = argv[0];
1646
+ secret_key = argv[1];
1647
+ } else if (argc == 3) {
1648
+ algorithm = argv[0];
1649
+ mu = argv[1];
1650
+ secret_key = argv[2];
1651
+ } else {
1652
+ rb_raise(rb_eArgError, "wrong number of arguments (given %d, expected 2..3)", argc);
1653
+ }
1654
+
1655
+ const pq_mldsa_profile_t *profile = pq_mldsa_profile_from_value(algorithm);
1505
1656
  pq_validate_bytes_argument(mu, PQ_MLDSA_MUBYTES, "mu");
1506
- pq_validate_bytes_argument(secret_key, PQ_MLDSA_SECRETKEYBYTES, "secret key");
1657
+ pq_validate_bytes_argument(secret_key, profile->secret_key_len, "secret key");
1507
1658
 
1508
1659
  sign_mu_call_t call = {0};
1509
1660
  size_t secret_key_len = 0;
@@ -1513,10 +1664,11 @@ static VALUE pqcrypto__native_mldsa_sign_mu(VALUE self, VALUE mu, VALUE secret_k
1513
1664
 
1514
1665
  call.mu = mu_copy;
1515
1666
  call.secret_key = sk_copy;
1516
- call.signature_len = PQ_MLDSA_BYTES;
1517
- call.signature = pq_alloc_buffer(PQ_MLDSA_BYTES);
1667
+ call.signature_extmu = profile->signature_extmu;
1668
+ call.signature_len = profile->signature_len;
1669
+ call.signature = pq_alloc_buffer(profile->signature_len);
1518
1670
 
1519
- rb_nogvl(pq_sign_mu_nogvl, &call, NULL, NULL, RB_NOGVL_OFFLOAD_SAFE);
1671
+ rb_nogvl(pq_sign_mu_nogvl, &call, NULL, NULL, PQ_RB_NOGVL_OFFLOAD_SAFE);
1520
1672
 
1521
1673
  pq_wipe_and_free(mu_copy, mu_len);
1522
1674
  pq_wipe_and_free(sk_copy, secret_key_len);
@@ -1533,16 +1685,32 @@ static VALUE pqcrypto__native_mldsa_sign_mu(VALUE self, VALUE mu, VALUE secret_k
1533
1685
 
1534
1686
  static void *pq_verify_mu_nogvl(void *arg) {
1535
1687
  verify_mu_call_t *call = (verify_mu_call_t *)arg;
1536
- call->result = pq_verify_mu(call->signature, call->signature_len, call->mu, call->public_key);
1688
+ call->result = pq_verify_mu(call->signature, call->signature_len, call->mu, call->public_key,
1689
+ call->expected_signature_len, call->verify_extmu);
1537
1690
  return NULL;
1538
1691
  }
1539
1692
 
1540
- static VALUE pqcrypto__native_mldsa_verify_mu(VALUE self, VALUE mu, VALUE signature,
1541
- VALUE public_key) {
1693
+ static VALUE pqcrypto__native_mldsa_verify_mu(int argc, VALUE *argv, VALUE self) {
1542
1694
  (void)self;
1695
+ VALUE algorithm = ID2SYM(rb_intern("ml_dsa_65"));
1696
+ VALUE mu, signature, public_key;
1697
+ if (argc == 3) {
1698
+ mu = argv[0];
1699
+ signature = argv[1];
1700
+ public_key = argv[2];
1701
+ } else if (argc == 4) {
1702
+ algorithm = argv[0];
1703
+ mu = argv[1];
1704
+ signature = argv[2];
1705
+ public_key = argv[3];
1706
+ } else {
1707
+ rb_raise(rb_eArgError, "wrong number of arguments (given %d, expected 3..4)", argc);
1708
+ }
1709
+
1710
+ const pq_mldsa_profile_t *profile = pq_mldsa_profile_from_value(algorithm);
1543
1711
  StringValue(signature);
1544
1712
  pq_validate_bytes_argument(mu, PQ_MLDSA_MUBYTES, "mu");
1545
- pq_validate_bytes_argument(public_key, PQ_MLDSA_PUBLICKEYBYTES, "public key");
1713
+ pq_validate_bytes_argument(public_key, profile->public_key_len, "public key");
1546
1714
 
1547
1715
  verify_mu_call_t call = {0};
1548
1716
  size_t public_key_len = 0;
@@ -1556,8 +1724,10 @@ static VALUE pqcrypto__native_mldsa_verify_mu(VALUE self, VALUE mu, VALUE signat
1556
1724
  call.public_key = pk_copy;
1557
1725
  call.signature = sig_copy;
1558
1726
  call.signature_len = signature_len;
1727
+ call.expected_signature_len = profile->signature_len;
1728
+ call.verify_extmu = profile->verify_extmu;
1559
1729
 
1560
- rb_nogvl(pq_verify_mu_nogvl, &call, NULL, NULL, RB_NOGVL_OFFLOAD_SAFE);
1730
+ rb_nogvl(pq_verify_mu_nogvl, &call, NULL, NULL, PQ_RB_NOGVL_OFFLOAD_SAFE);
1561
1731
  pq_wipe_and_free(mu_copy, mu_len);
1562
1732
  pq_free_buffer(pk_copy);
1563
1733
  pq_free_buffer(sig_copy);
@@ -1699,6 +1869,8 @@ void Init_pqcrypto_secure(void) {
1699
1869
  pqcrypto_hybrid_kem_expand_secret_key, 1);
1700
1870
  rb_define_module_function(mPQCrypto, "hybrid_kem_expand_secret_key_object",
1701
1871
  pqcrypto_hybrid_kem_expand_secret_key_object, 1);
1872
+ rb_define_module_function(mPQCrypto, "hybrid_kem_expanded_secret_key_wipe",
1873
+ pqcrypto_hybrid_kem_expanded_secret_key_wipe, 1);
1702
1874
  rb_define_module_function(mPQCrypto, "hybrid_kem_decapsulate", pqcrypto_hybrid_kem_decapsulate,
1703
1875
  2);
1704
1876
  rb_define_module_function(mPQCrypto, "hybrid_kem_decapsulate_expanded",
@@ -1706,20 +1878,20 @@ void Init_pqcrypto_secure(void) {
1706
1878
  rb_define_module_function(mPQCrypto, "hybrid_kem_decapsulate_expanded_object",
1707
1879
  pqcrypto_hybrid_kem_decapsulate_expanded_object, 2);
1708
1880
  rb_define_module_function(mPQCrypto, "sign_keypair", pqcrypto_sign_keypair, 0);
1709
- rb_define_module_function(mPQCrypto, "sign", pqcrypto_sign, 2);
1710
- rb_define_module_function(mPQCrypto, "verify", pqcrypto_verify, 3);
1881
+ rb_define_module_function(mPQCrypto, "sign", pqcrypto_sign, -1);
1882
+ rb_define_module_function(mPQCrypto, "verify", pqcrypto_verify, -1);
1711
1883
  rb_define_module_function(mPQCrypto, "ml_dsa_44_keypair", pqcrypto_ml_dsa_44_keypair, 0);
1712
1884
  rb_define_module_function(mPQCrypto, "ml_dsa_44_keypair_from_seed",
1713
1885
  pqcrypto_ml_dsa_44_keypair_from_seed, 1);
1714
1886
  rb_define_module_function(mPQCrypto, "ml_dsa_keypair_from_seed",
1715
1887
  pqcrypto_ml_dsa_keypair_from_seed, 1);
1716
- rb_define_module_function(mPQCrypto, "ml_dsa_44_sign", pqcrypto_ml_dsa_44_sign, 2);
1717
- rb_define_module_function(mPQCrypto, "ml_dsa_44_verify", pqcrypto_ml_dsa_44_verify, 3);
1888
+ rb_define_module_function(mPQCrypto, "ml_dsa_44_sign", pqcrypto_ml_dsa_44_sign, -1);
1889
+ rb_define_module_function(mPQCrypto, "ml_dsa_44_verify", pqcrypto_ml_dsa_44_verify, -1);
1718
1890
  rb_define_module_function(mPQCrypto, "ml_dsa_87_keypair", pqcrypto_ml_dsa_87_keypair, 0);
1719
1891
  rb_define_module_function(mPQCrypto, "ml_dsa_87_keypair_from_seed",
1720
1892
  pqcrypto_ml_dsa_87_keypair_from_seed, 1);
1721
- rb_define_module_function(mPQCrypto, "ml_dsa_87_sign", pqcrypto_ml_dsa_87_sign, 2);
1722
- rb_define_module_function(mPQCrypto, "ml_dsa_87_verify", pqcrypto_ml_dsa_87_verify, 3);
1893
+ rb_define_module_function(mPQCrypto, "ml_dsa_87_sign", pqcrypto_ml_dsa_87_sign, -1);
1894
+ rb_define_module_function(mPQCrypto, "ml_dsa_87_verify", pqcrypto_ml_dsa_87_verify, -1);
1723
1895
  rb_define_module_function(mPQCrypto, "ct_equals", pqcrypto_ct_equals, 2);
1724
1896
  rb_define_module_function(mPQCrypto, "secure_wipe", pqcrypto_secure_wipe, 1);
1725
1897
  rb_define_module_function(mPQCrypto, "version", pqcrypto_version, 0);
@@ -1740,9 +1912,9 @@ void Init_pqcrypto_secure(void) {
1740
1912
  rb_define_module_function(mPQCrypto, "secret_key_from_pqc_container_pem",
1741
1913
  pqcrypto_secret_key_from_pqc_container_pem, 1);
1742
1914
  rb_define_module_function(mPQCrypto, "_native_mldsa_extract_tr",
1743
- pqcrypto__native_mldsa_extract_tr, 1);
1915
+ pqcrypto__native_mldsa_extract_tr, -1);
1744
1916
  rb_define_module_function(mPQCrypto, "_native_mldsa_compute_tr",
1745
- pqcrypto__native_mldsa_compute_tr, 1);
1917
+ pqcrypto__native_mldsa_compute_tr, -1);
1746
1918
  rb_define_module_function(mPQCrypto, "_native_mldsa_mu_builder_new",
1747
1919
  pqcrypto__native_mldsa_mu_builder_new, 2);
1748
1920
  rb_define_module_function(mPQCrypto, "_native_mldsa_mu_builder_update",
@@ -1752,9 +1924,9 @@ void Init_pqcrypto_secure(void) {
1752
1924
  rb_define_module_function(mPQCrypto, "_native_mldsa_mu_builder_release",
1753
1925
  pqcrypto__native_mldsa_mu_builder_release, 1);
1754
1926
  rb_define_module_function(mPQCrypto, "_native_mldsa_sign_mu", pqcrypto__native_mldsa_sign_mu,
1755
- 2);
1927
+ -1);
1756
1928
  rb_define_module_function(mPQCrypto, "_native_mldsa_verify_mu",
1757
- pqcrypto__native_mldsa_verify_mu, 3);
1929
+ pqcrypto__native_mldsa_verify_mu, -1);
1758
1930
 
1759
1931
  define_constants();
1760
1932
  }