pq_crypto 0.1.0 → 0.2.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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +1 -1
- data/CHANGELOG.md +14 -0
- data/README.md +39 -1
- data/ext/pqcrypto/pqcrypto_ruby_secure.c +245 -310
- data/ext/pqcrypto/pqcrypto_secure.c +67 -76
- data/lib/pq_crypto/version.rb +1 -1
- metadata +4 -21
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a9f15d910879cdfa645c90795ee80932a0261322401f42e05a0623ce938cfce7
|
|
4
|
+
data.tar.gz: 126bd8e2435fedfe9aa46b046bf98215e972f9fc81d5856414015357afee6bbf
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 154020cb51f31d414e83c09cf9a7cc410fc8395500a97777fb741ff77986f4e5ae2fda7e64e932eaf17eb9d653901a63f114d5710a672aa5f954e756c2509898
|
|
7
|
+
data.tar.gz: c2ea78f4b001a32d486f876832832248261bf42d80ddbbe5821faed59240d6d55c80e67ed486c4476a1e47ae5fd1d46cbf64fc0903f2483ff8c3cf34c15a2388
|
data/.github/workflows/ci.yml
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.2.0]
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- Raised the minimum supported Ruby to the 3.4 series.
|
|
8
|
+
- Switched `PQCrypto::Signature::SecretKey#sign` and `PQCrypto::Signature::PublicKey#verify` to Ruby 3.4's scheduler-aware `rb_nogvl(..., RB_NOGVL_OFFLOAD_SAFE)` path.
|
|
9
|
+
- Left the faster KEM and key-generation operations on the existing lower-overhead no-GVL path.
|
|
10
|
+
- Removed gem-specific scheduler configuration; runtime behavior now follows the active Ruby Fiber scheduler automatically.
|
|
11
|
+
|
|
12
|
+
### Testing
|
|
13
|
+
|
|
14
|
+
- Added Async integration tests that verify sibling `task.async` work keeps making progress while `sign` and `verify` run under an Async worker-pool-enabled reactor.
|
|
15
|
+
- Updated CI to target the supported Ruby 3.4 series.
|
|
16
|
+
|
|
3
17
|
## [0.1.0]
|
|
4
18
|
|
|
5
19
|
Initial public release.
|
data/README.md
CHANGED
|
@@ -35,10 +35,48 @@ bundle exec rake compile
|
|
|
35
35
|
|
|
36
36
|
### Native dependencies
|
|
37
37
|
|
|
38
|
-
- Ruby 3.
|
|
38
|
+
- Ruby 3.4.x
|
|
39
39
|
- a C toolchain
|
|
40
40
|
- OpenSSL **3.0 or later**
|
|
41
41
|
|
|
42
|
+
## Async / Fiber scheduler support
|
|
43
|
+
|
|
44
|
+
`pq_crypto` does not require any gem-specific Async configuration. On Ruby 3.4, `sign` and `verify` use Ruby's scheduler-aware `rb_nogvl(..., RB_NOGVL_OFFLOAD_SAFE)` path automatically.
|
|
45
|
+
|
|
46
|
+
That means:
|
|
47
|
+
|
|
48
|
+
- without a Fiber scheduler, these methods fall back to the ordinary no-GVL behavior;
|
|
49
|
+
- with a scheduler that implements `blocking_operation_wait` (for example `Async` with a worker pool), the blocking native work can be moved off the event loop.
|
|
50
|
+
|
|
51
|
+
This integration is intentionally limited to `sign` and `verify`; the faster primitive operations keep the lower-overhead path.
|
|
52
|
+
|
|
53
|
+
Example with `Async`:
|
|
54
|
+
|
|
55
|
+
```ruby
|
|
56
|
+
require "async"
|
|
57
|
+
require "pq_crypto"
|
|
58
|
+
|
|
59
|
+
keypair = PQCrypto::Signature.generate(:ml_dsa_65)
|
|
60
|
+
message = "hello" * 100_000
|
|
61
|
+
|
|
62
|
+
reactor = Async::Reactor.new(worker_pool: true)
|
|
63
|
+
root = reactor.async do |task|
|
|
64
|
+
task.async do
|
|
65
|
+
signature = keypair.secret_key.sign(message)
|
|
66
|
+
keypair.public_key.verify(message, signature)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
task.async do
|
|
70
|
+
sleep 0.01
|
|
71
|
+
puts "event loop stayed responsive"
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
reactor.run
|
|
76
|
+
root.wait
|
|
77
|
+
reactor.close
|
|
78
|
+
```
|
|
79
|
+
|
|
42
80
|
## Primitive API
|
|
43
81
|
|
|
44
82
|
### ML-KEM-768
|
|
@@ -62,6 +62,9 @@ static VALUE mPQCrypto;
|
|
|
62
62
|
static VALUE ePQCryptoError;
|
|
63
63
|
static VALUE ePQCryptoVerificationError;
|
|
64
64
|
|
|
65
|
+
__attribute__((noreturn)) static void pq_raise_general_error(int err);
|
|
66
|
+
__attribute__((noreturn)) static void pq_raise_verification_error(int err);
|
|
67
|
+
|
|
65
68
|
static const char *pq_algorithm_symbol_to_cstr(VALUE algorithm) {
|
|
66
69
|
ID id;
|
|
67
70
|
if (SYMBOL_P(algorithm)) {
|
|
@@ -215,230 +218,294 @@ static void pq_wipe_and_free(uint8_t *buffer, size_t len) {
|
|
|
215
218
|
}
|
|
216
219
|
}
|
|
217
220
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
rb_raise(
|
|
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;
|
|
221
|
+
static void pq_validate_bytes_argument(VALUE value, size_t expected_len, const char *what) {
|
|
222
|
+
StringValue(value);
|
|
223
|
+
if ((size_t)RSTRING_LEN(value) != expected_len) {
|
|
224
|
+
rb_raise(rb_eArgError, "Invalid %s length", what);
|
|
253
225
|
}
|
|
254
226
|
}
|
|
255
227
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
pq_raise_general_error(err);
|
|
263
|
-
}
|
|
228
|
+
static VALUE pq_build_binary_pair(const uint8_t *first, size_t first_len, const uint8_t *second,
|
|
229
|
+
size_t second_len) {
|
|
230
|
+
VALUE result = rb_ary_new2(2);
|
|
231
|
+
rb_ary_push(result, pq_string_from_buffer(first, first_len));
|
|
232
|
+
rb_ary_push(result, pq_string_from_buffer(second, second_len));
|
|
233
|
+
return result;
|
|
264
234
|
}
|
|
265
235
|
|
|
266
|
-
static VALUE
|
|
267
|
-
|
|
236
|
+
static VALUE pq_build_algorithm_key_pair(const char *algorithm, const uint8_t *key,
|
|
237
|
+
size_t key_len) {
|
|
238
|
+
VALUE result = rb_ary_new2(2);
|
|
239
|
+
rb_ary_push(result, pq_algorithm_cstr_to_symbol(algorithm));
|
|
240
|
+
rb_ary_push(result, pq_string_from_buffer(key, key_len));
|
|
241
|
+
return result;
|
|
242
|
+
}
|
|
268
243
|
|
|
244
|
+
static VALUE pq_run_kem_keypair(void *(*nogvl)(void *), size_t public_key_len,
|
|
245
|
+
size_t secret_key_len) {
|
|
269
246
|
kem_keypair_call_t call = {0};
|
|
270
|
-
|
|
271
|
-
call.secret_key = pq_alloc_buffer(PQ_MLKEM_SECRETKEYBYTES);
|
|
247
|
+
VALUE result;
|
|
272
248
|
|
|
273
|
-
|
|
249
|
+
call.public_key = pq_alloc_buffer(public_key_len);
|
|
250
|
+
call.secret_key = pq_alloc_buffer(secret_key_len);
|
|
251
|
+
|
|
252
|
+
rb_thread_call_without_gvl(nogvl, &call, NULL, NULL);
|
|
274
253
|
|
|
275
254
|
if (call.result != PQ_SUCCESS) {
|
|
276
|
-
pq_wipe_and_free(call.secret_key,
|
|
255
|
+
pq_wipe_and_free(call.secret_key, secret_key_len);
|
|
277
256
|
free(call.public_key);
|
|
278
257
|
pq_raise_general_error(call.result);
|
|
279
258
|
}
|
|
280
259
|
|
|
281
|
-
|
|
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
|
-
|
|
260
|
+
result = pq_build_binary_pair(call.public_key, public_key_len, call.secret_key, secret_key_len);
|
|
285
261
|
free(call.public_key);
|
|
286
|
-
pq_wipe_and_free(call.secret_key,
|
|
262
|
+
pq_wipe_and_free(call.secret_key, secret_key_len);
|
|
287
263
|
return result;
|
|
288
264
|
}
|
|
289
265
|
|
|
290
|
-
static VALUE
|
|
291
|
-
|
|
292
|
-
|
|
266
|
+
static VALUE pq_run_kem_encapsulate(void *(*nogvl)(void *), VALUE public_key, size_t public_key_len,
|
|
267
|
+
size_t ciphertext_len, size_t shared_secret_len) {
|
|
268
|
+
kem_encapsulate_call_t call = {0};
|
|
269
|
+
VALUE result;
|
|
270
|
+
size_t copied_public_key_len = 0;
|
|
293
271
|
|
|
294
|
-
|
|
295
|
-
rb_raise(rb_eArgError, "Invalid public key length");
|
|
296
|
-
}
|
|
272
|
+
pq_validate_bytes_argument(public_key, public_key_len, "public key");
|
|
297
273
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
call.
|
|
301
|
-
call.ciphertext = pq_alloc_buffer(PQ_MLKEM_CIPHERTEXTBYTES);
|
|
302
|
-
call.shared_secret = pq_alloc_buffer(PQ_MLKEM_SHAREDSECRETBYTES);
|
|
274
|
+
call.public_key = pq_copy_ruby_string(public_key, &copied_public_key_len);
|
|
275
|
+
call.ciphertext = pq_alloc_buffer(ciphertext_len);
|
|
276
|
+
call.shared_secret = pq_alloc_buffer(shared_secret_len);
|
|
303
277
|
|
|
304
|
-
rb_thread_call_without_gvl(
|
|
305
|
-
pq_wipe_and_free((uint8_t *)call.public_key,
|
|
278
|
+
rb_thread_call_without_gvl(nogvl, &call, NULL, NULL);
|
|
279
|
+
pq_wipe_and_free((uint8_t *)call.public_key, copied_public_key_len);
|
|
306
280
|
|
|
307
281
|
if (call.result != PQ_SUCCESS) {
|
|
308
|
-
pq_wipe_and_free(call.shared_secret,
|
|
282
|
+
pq_wipe_and_free(call.shared_secret, shared_secret_len);
|
|
309
283
|
free(call.ciphertext);
|
|
310
284
|
pq_raise_general_error(call.result);
|
|
311
285
|
}
|
|
312
286
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
rb_ary_push(result, pq_string_from_buffer(call.shared_secret, PQ_MLKEM_SHAREDSECRETBYTES));
|
|
316
|
-
|
|
287
|
+
result = pq_build_binary_pair(call.ciphertext, ciphertext_len, call.shared_secret,
|
|
288
|
+
shared_secret_len);
|
|
317
289
|
free(call.ciphertext);
|
|
318
|
-
pq_wipe_and_free(call.shared_secret,
|
|
290
|
+
pq_wipe_and_free(call.shared_secret, shared_secret_len);
|
|
319
291
|
return result;
|
|
320
292
|
}
|
|
321
293
|
|
|
322
|
-
static VALUE
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
294
|
+
static VALUE pq_run_kem_decapsulate(void *(*nogvl)(void *), VALUE ciphertext, size_t ciphertext_len,
|
|
295
|
+
VALUE secret_key, size_t secret_key_len,
|
|
296
|
+
size_t shared_secret_len) {
|
|
297
|
+
kem_decapsulate_call_t call = {0};
|
|
298
|
+
VALUE result;
|
|
299
|
+
size_t copied_ciphertext_len = 0;
|
|
300
|
+
size_t copied_secret_key_len = 0;
|
|
326
301
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
}
|
|
330
|
-
if ((size_t)RSTRING_LEN(secret_key) != PQ_MLKEM_SECRETKEYBYTES) {
|
|
331
|
-
rb_raise(rb_eArgError, "Invalid secret key length");
|
|
332
|
-
}
|
|
302
|
+
pq_validate_bytes_argument(ciphertext, ciphertext_len, "ciphertext");
|
|
303
|
+
pq_validate_bytes_argument(secret_key, secret_key_len, "secret key");
|
|
333
304
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
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);
|
|
305
|
+
call.ciphertext = pq_copy_ruby_string(ciphertext, &copied_ciphertext_len);
|
|
306
|
+
call.secret_key = pq_copy_ruby_string(secret_key, &copied_secret_key_len);
|
|
307
|
+
call.shared_secret = pq_alloc_buffer(shared_secret_len);
|
|
340
308
|
|
|
341
|
-
rb_thread_call_without_gvl(
|
|
342
|
-
pq_wipe_and_free((uint8_t *)call.ciphertext,
|
|
343
|
-
pq_wipe_and_free((uint8_t *)call.secret_key,
|
|
309
|
+
rb_thread_call_without_gvl(nogvl, &call, NULL, NULL);
|
|
310
|
+
pq_wipe_and_free((uint8_t *)call.ciphertext, copied_ciphertext_len);
|
|
311
|
+
pq_wipe_and_free((uint8_t *)call.secret_key, copied_secret_key_len);
|
|
344
312
|
|
|
345
313
|
if (call.result != PQ_SUCCESS) {
|
|
346
|
-
pq_wipe_and_free(call.shared_secret,
|
|
314
|
+
pq_wipe_and_free(call.shared_secret, shared_secret_len);
|
|
347
315
|
pq_raise_general_error(call.result);
|
|
348
316
|
}
|
|
349
317
|
|
|
350
|
-
|
|
351
|
-
pq_wipe_and_free(call.shared_secret,
|
|
318
|
+
result = pq_string_from_buffer(call.shared_secret, shared_secret_len);
|
|
319
|
+
pq_wipe_and_free(call.shared_secret, shared_secret_len);
|
|
352
320
|
return result;
|
|
353
321
|
}
|
|
354
322
|
|
|
355
|
-
static VALUE
|
|
356
|
-
|
|
323
|
+
static VALUE pq_run_sign_keypair(void *(*nogvl)(void *), size_t public_key_len,
|
|
324
|
+
size_t secret_key_len) {
|
|
325
|
+
sign_keypair_call_t call = {0};
|
|
326
|
+
VALUE result;
|
|
357
327
|
|
|
358
|
-
|
|
359
|
-
call.
|
|
360
|
-
call.secret_key = pq_alloc_buffer(PQ_HYBRID_SECRETKEYBYTES);
|
|
328
|
+
call.public_key = pq_alloc_buffer(public_key_len);
|
|
329
|
+
call.secret_key = pq_alloc_buffer(secret_key_len);
|
|
361
330
|
|
|
362
|
-
rb_thread_call_without_gvl(
|
|
331
|
+
rb_thread_call_without_gvl(nogvl, &call, NULL, NULL);
|
|
363
332
|
|
|
364
333
|
if (call.result != PQ_SUCCESS) {
|
|
365
|
-
pq_wipe_and_free(call.secret_key,
|
|
334
|
+
pq_wipe_and_free(call.secret_key, secret_key_len);
|
|
366
335
|
free(call.public_key);
|
|
367
336
|
pq_raise_general_error(call.result);
|
|
368
337
|
}
|
|
369
338
|
|
|
370
|
-
|
|
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
|
-
|
|
339
|
+
result = pq_build_binary_pair(call.public_key, public_key_len, call.secret_key, secret_key_len);
|
|
374
340
|
free(call.public_key);
|
|
375
|
-
pq_wipe_and_free(call.secret_key,
|
|
341
|
+
pq_wipe_and_free(call.secret_key, secret_key_len);
|
|
376
342
|
return result;
|
|
377
343
|
}
|
|
378
344
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
345
|
+
typedef int (*pq_export_der_fn)(uint8_t **, size_t *, const uint8_t *, size_t, const char *);
|
|
346
|
+
typedef int (*pq_export_pem_fn)(char **, size_t *, const uint8_t *, size_t, const char *);
|
|
347
|
+
typedef int (*pq_import_der_fn)(char **, uint8_t **, size_t *, const uint8_t *, size_t);
|
|
348
|
+
typedef int (*pq_import_pem_fn)(char **, uint8_t **, size_t *, const char *, size_t);
|
|
382
349
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
350
|
+
static VALUE pq_export_container_der(VALUE algorithm, VALUE key_bytes, pq_export_der_fn fn) {
|
|
351
|
+
uint8_t *out = NULL;
|
|
352
|
+
size_t out_len = 0;
|
|
353
|
+
VALUE result;
|
|
354
|
+
int ret;
|
|
386
355
|
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
356
|
+
StringValue(key_bytes);
|
|
357
|
+
ret = fn(&out, &out_len, (const uint8_t *)RSTRING_PTR(key_bytes),
|
|
358
|
+
(size_t)RSTRING_LEN(key_bytes), pq_algorithm_symbol_to_cstr(algorithm));
|
|
359
|
+
if (ret != PQ_SUCCESS)
|
|
360
|
+
pq_raise_general_error(ret);
|
|
392
361
|
|
|
393
|
-
|
|
394
|
-
|
|
362
|
+
result = pq_string_from_buffer(out, out_len);
|
|
363
|
+
pq_secure_wipe(out, out_len);
|
|
364
|
+
free(out);
|
|
365
|
+
return result;
|
|
366
|
+
}
|
|
395
367
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
368
|
+
static VALUE pq_export_container_pem(VALUE algorithm, VALUE key_bytes, pq_export_pem_fn fn) {
|
|
369
|
+
char *out = NULL;
|
|
370
|
+
size_t out_len = 0;
|
|
371
|
+
VALUE result;
|
|
372
|
+
int ret;
|
|
401
373
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
374
|
+
StringValue(key_bytes);
|
|
375
|
+
ret = fn(&out, &out_len, (const uint8_t *)RSTRING_PTR(key_bytes),
|
|
376
|
+
(size_t)RSTRING_LEN(key_bytes), pq_algorithm_symbol_to_cstr(algorithm));
|
|
377
|
+
if (ret != PQ_SUCCESS)
|
|
378
|
+
pq_raise_general_error(ret);
|
|
405
379
|
|
|
406
|
-
|
|
407
|
-
|
|
380
|
+
result = rb_utf8_str_new(out, (long)out_len);
|
|
381
|
+
pq_secure_wipe(out, out_len);
|
|
382
|
+
free(out);
|
|
408
383
|
return result;
|
|
409
384
|
}
|
|
410
385
|
|
|
411
|
-
static VALUE
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
386
|
+
static VALUE pq_import_container_der(VALUE der, pq_import_der_fn fn) {
|
|
387
|
+
char *algorithm = NULL;
|
|
388
|
+
uint8_t *key = NULL;
|
|
389
|
+
size_t key_len = 0;
|
|
390
|
+
VALUE result;
|
|
391
|
+
int ret;
|
|
392
|
+
|
|
393
|
+
StringValue(der);
|
|
394
|
+
ret =
|
|
395
|
+
fn(&algorithm, &key, &key_len, (const uint8_t *)RSTRING_PTR(der), (size_t)RSTRING_LEN(der));
|
|
396
|
+
if (ret != PQ_SUCCESS)
|
|
397
|
+
pq_raise_general_error(ret);
|
|
415
398
|
|
|
416
|
-
|
|
417
|
-
|
|
399
|
+
result = pq_build_algorithm_key_pair(algorithm, key, key_len);
|
|
400
|
+
free(algorithm);
|
|
401
|
+
pq_secure_wipe(key, key_len);
|
|
402
|
+
free(key);
|
|
403
|
+
return result;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
static VALUE pq_import_container_pem(VALUE pem, pq_import_pem_fn fn) {
|
|
407
|
+
char *algorithm = NULL;
|
|
408
|
+
uint8_t *key = NULL;
|
|
409
|
+
size_t key_len = 0;
|
|
410
|
+
VALUE result;
|
|
411
|
+
int ret;
|
|
412
|
+
|
|
413
|
+
StringValue(pem);
|
|
414
|
+
ret = fn(&algorithm, &key, &key_len, RSTRING_PTR(pem), (size_t)RSTRING_LEN(pem));
|
|
415
|
+
if (ret != PQ_SUCCESS)
|
|
416
|
+
pq_raise_general_error(ret);
|
|
417
|
+
|
|
418
|
+
result = pq_build_algorithm_key_pair(algorithm, key, key_len);
|
|
419
|
+
free(algorithm);
|
|
420
|
+
pq_secure_wipe(key, key_len);
|
|
421
|
+
free(key);
|
|
422
|
+
return result;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
__attribute__((noreturn)) static void pq_raise_general_error(int err) {
|
|
426
|
+
switch (err) {
|
|
427
|
+
case PQ_ERROR_KEYPAIR:
|
|
428
|
+
rb_raise(ePQCryptoError, "Keypair generation failed");
|
|
429
|
+
break;
|
|
430
|
+
case PQ_ERROR_ENCAPSULATE:
|
|
431
|
+
rb_raise(ePQCryptoError, "Encapsulation failed");
|
|
432
|
+
break;
|
|
433
|
+
case PQ_ERROR_DECAPSULATE:
|
|
434
|
+
rb_raise(ePQCryptoError, "Decapsulation failed");
|
|
435
|
+
break;
|
|
436
|
+
case PQ_ERROR_SIGN:
|
|
437
|
+
rb_raise(ePQCryptoError, "Signing failed");
|
|
438
|
+
break;
|
|
439
|
+
case PQ_ERROR_VERIFY:
|
|
440
|
+
rb_raise(ePQCryptoError, "Verification failed");
|
|
441
|
+
break;
|
|
442
|
+
case PQ_ERROR_RANDOM:
|
|
443
|
+
rb_raise(ePQCryptoError, "Random number generation failed");
|
|
444
|
+
break;
|
|
445
|
+
case PQ_ERROR_KDF:
|
|
446
|
+
rb_raise(ePQCryptoError, "Key derivation failed");
|
|
447
|
+
break;
|
|
448
|
+
case PQ_ERROR_BUFFER:
|
|
449
|
+
rb_raise(ePQCryptoError, "Buffer error");
|
|
450
|
+
break;
|
|
451
|
+
case PQ_ERROR_NOMEM:
|
|
452
|
+
rb_raise(rb_eNoMemError, "Memory allocation failed");
|
|
453
|
+
break;
|
|
454
|
+
case PQ_ERROR_OPENSSL:
|
|
455
|
+
rb_raise(ePQCryptoError, "OpenSSL error");
|
|
456
|
+
break;
|
|
457
|
+
default:
|
|
458
|
+
rb_raise(ePQCryptoError, "Unknown error: %d", err);
|
|
459
|
+
break;
|
|
418
460
|
}
|
|
419
|
-
|
|
420
|
-
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
__attribute__((noreturn)) static void pq_raise_verification_error(int err) {
|
|
464
|
+
switch (err) {
|
|
465
|
+
case PQ_ERROR_VERIFY:
|
|
466
|
+
rb_raise(ePQCryptoVerificationError, "Verification failed");
|
|
467
|
+
break;
|
|
468
|
+
default:
|
|
469
|
+
pq_raise_general_error(err);
|
|
421
470
|
}
|
|
471
|
+
}
|
|
422
472
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
call.shared_secret = pq_alloc_buffer(PQ_HYBRID_SHAREDSECRETBYTES);
|
|
473
|
+
static VALUE pqcrypto_ml_kem_keypair(VALUE self) {
|
|
474
|
+
(void)self;
|
|
475
|
+
return pq_run_kem_keypair(pq_ml_kem_keypair_nogvl, PQ_MLKEM_PUBLICKEYBYTES,
|
|
476
|
+
PQ_MLKEM_SECRETKEYBYTES);
|
|
477
|
+
}
|
|
429
478
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
479
|
+
static VALUE pqcrypto_ml_kem_encapsulate(VALUE self, VALUE public_key) {
|
|
480
|
+
(void)self;
|
|
481
|
+
return pq_run_kem_encapsulate(pq_ml_kem_encapsulate_nogvl, public_key, PQ_MLKEM_PUBLICKEYBYTES,
|
|
482
|
+
PQ_MLKEM_CIPHERTEXTBYTES, PQ_MLKEM_SHAREDSECRETBYTES);
|
|
483
|
+
}
|
|
433
484
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
485
|
+
static VALUE pqcrypto_ml_kem_decapsulate(VALUE self, VALUE ciphertext, VALUE secret_key) {
|
|
486
|
+
(void)self;
|
|
487
|
+
return pq_run_kem_decapsulate(pq_ml_kem_decapsulate_nogvl, ciphertext, PQ_MLKEM_CIPHERTEXTBYTES,
|
|
488
|
+
secret_key, PQ_MLKEM_SECRETKEYBYTES, PQ_MLKEM_SHAREDSECRETBYTES);
|
|
489
|
+
}
|
|
438
490
|
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
return
|
|
491
|
+
static VALUE pqcrypto_hybrid_kem_keypair(VALUE self) {
|
|
492
|
+
(void)self;
|
|
493
|
+
return pq_run_kem_keypair(pq_hybrid_kem_keypair_nogvl, PQ_HYBRID_PUBLICKEYBYTES,
|
|
494
|
+
PQ_HYBRID_SECRETKEYBYTES);
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
static VALUE pqcrypto_hybrid_kem_encapsulate(VALUE self, VALUE public_key) {
|
|
498
|
+
(void)self;
|
|
499
|
+
return pq_run_kem_encapsulate(pq_hybrid_kem_encapsulate_nogvl, public_key,
|
|
500
|
+
PQ_HYBRID_PUBLICKEYBYTES, PQ_HYBRID_CIPHERTEXTBYTES,
|
|
501
|
+
PQ_HYBRID_SHAREDSECRETBYTES);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
static VALUE pqcrypto_hybrid_kem_decapsulate(VALUE self, VALUE ciphertext, VALUE secret_key) {
|
|
505
|
+
(void)self;
|
|
506
|
+
return pq_run_kem_decapsulate(pq_hybrid_kem_decapsulate_nogvl, ciphertext,
|
|
507
|
+
PQ_HYBRID_CIPHERTEXTBYTES, secret_key, PQ_HYBRID_SECRETKEYBYTES,
|
|
508
|
+
PQ_HYBRID_SHAREDSECRETBYTES);
|
|
442
509
|
}
|
|
443
510
|
|
|
444
511
|
static VALUE pqcrypto__test_ml_kem_keypair_from_seed(VALUE self, VALUE seed) {
|
|
@@ -476,12 +543,9 @@ static VALUE pqcrypto__test_ml_kem_keypair_from_seed(VALUE self, VALUE seed) {
|
|
|
476
543
|
|
|
477
544
|
static VALUE pqcrypto__test_ml_kem_encapsulate_from_seed(VALUE self, VALUE public_key, VALUE seed) {
|
|
478
545
|
(void)self;
|
|
479
|
-
|
|
546
|
+
pq_validate_bytes_argument(public_key, PQ_MLKEM_PUBLICKEYBYTES, "public key");
|
|
480
547
|
StringValue(seed);
|
|
481
548
|
|
|
482
|
-
if ((size_t)RSTRING_LEN(public_key) != PQ_MLKEM_PUBLICKEYBYTES) {
|
|
483
|
-
rb_raise(rb_eArgError, "Invalid public key length");
|
|
484
|
-
}
|
|
485
549
|
if ((size_t)RSTRING_LEN(seed) != 32) {
|
|
486
550
|
rb_raise(rb_eArgError, "Deterministic test seed must be 32 bytes");
|
|
487
551
|
}
|
|
@@ -550,12 +614,9 @@ static VALUE pqcrypto__test_sign_keypair_from_seed(VALUE self, VALUE seed) {
|
|
|
550
614
|
static VALUE pqcrypto__test_sign_from_seed(VALUE self, VALUE message, VALUE secret_key,
|
|
551
615
|
VALUE seed) {
|
|
552
616
|
(void)self;
|
|
553
|
-
|
|
617
|
+
pq_validate_bytes_argument(secret_key, PQ_MLDSA_SECRETKEYBYTES, "secret key");
|
|
554
618
|
StringValue(seed);
|
|
555
619
|
|
|
556
|
-
if ((size_t)RSTRING_LEN(secret_key) != PQ_MLDSA_SECRETKEYBYTES) {
|
|
557
|
-
rb_raise(rb_eArgError, "Invalid secret key length");
|
|
558
|
-
}
|
|
559
620
|
if ((size_t)RSTRING_LEN(seed) != 32) {
|
|
560
621
|
rb_raise(rb_eArgError, "Deterministic test seed must be 32 bytes");
|
|
561
622
|
}
|
|
@@ -588,35 +649,13 @@ static VALUE pqcrypto__test_sign_from_seed(VALUE self, VALUE message, VALUE secr
|
|
|
588
649
|
|
|
589
650
|
static VALUE pqcrypto_sign_keypair(VALUE self) {
|
|
590
651
|
(void)self;
|
|
591
|
-
|
|
592
|
-
|
|
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;
|
|
652
|
+
return pq_run_sign_keypair(pq_sign_keypair_nogvl, PQ_MLDSA_PUBLICKEYBYTES,
|
|
653
|
+
PQ_MLDSA_SECRETKEYBYTES);
|
|
611
654
|
}
|
|
612
655
|
|
|
613
656
|
static VALUE pqcrypto_sign(VALUE self, VALUE message, VALUE secret_key) {
|
|
614
657
|
(void)self;
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
if ((size_t)RSTRING_LEN(secret_key) != PQ_MLDSA_SECRETKEYBYTES) {
|
|
618
|
-
rb_raise(rb_eArgError, "Invalid secret key length");
|
|
619
|
-
}
|
|
658
|
+
pq_validate_bytes_argument(secret_key, PQ_MLDSA_SECRETKEYBYTES, "secret key");
|
|
620
659
|
|
|
621
660
|
sign_call_t call = {0};
|
|
622
661
|
size_t secret_key_len = 0;
|
|
@@ -625,7 +664,7 @@ static VALUE pqcrypto_sign(VALUE self, VALUE message, VALUE secret_key) {
|
|
|
625
664
|
call.signature = pq_alloc_buffer(PQ_MLDSA_BYTES);
|
|
626
665
|
call.message = pq_copy_ruby_string(message, &call.message_len);
|
|
627
666
|
|
|
628
|
-
|
|
667
|
+
rb_nogvl(pq_sign_nogvl, &call, NULL, NULL, RB_NOGVL_OFFLOAD_SAFE);
|
|
629
668
|
|
|
630
669
|
pq_wipe_and_free(call.message, call.message_len);
|
|
631
670
|
pq_wipe_and_free((uint8_t *)call.secret_key, secret_key_len);
|
|
@@ -643,11 +682,7 @@ static VALUE pqcrypto_sign(VALUE self, VALUE message, VALUE secret_key) {
|
|
|
643
682
|
static VALUE pqcrypto_verify(VALUE self, VALUE message, VALUE signature, VALUE public_key) {
|
|
644
683
|
(void)self;
|
|
645
684
|
StringValue(signature);
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
if ((size_t)RSTRING_LEN(public_key) != PQ_MLDSA_PUBLICKEYBYTES) {
|
|
649
|
-
rb_raise(rb_eArgError, "Invalid public key length");
|
|
650
|
-
}
|
|
685
|
+
pq_validate_bytes_argument(public_key, PQ_MLDSA_PUBLICKEYBYTES, "public key");
|
|
651
686
|
|
|
652
687
|
verify_call_t call = {0};
|
|
653
688
|
size_t public_key_len = 0;
|
|
@@ -657,7 +692,7 @@ static VALUE pqcrypto_verify(VALUE self, VALUE message, VALUE signature, VALUE p
|
|
|
657
692
|
call.signature_len = signature_len;
|
|
658
693
|
call.message = pq_copy_ruby_string(message, &call.message_len);
|
|
659
694
|
|
|
660
|
-
|
|
695
|
+
rb_nogvl(pq_verify_nogvl, &call, NULL, NULL, RB_NOGVL_OFFLOAD_SAFE);
|
|
661
696
|
|
|
662
697
|
pq_wipe_and_free(call.message, call.message_len);
|
|
663
698
|
pq_wipe_and_free((uint8_t *)call.public_key, public_key_len);
|
|
@@ -700,146 +735,46 @@ static void define_constants(void) {
|
|
|
700
735
|
|
|
701
736
|
static VALUE pqcrypto_public_key_to_pqc_container_der(VALUE self, VALUE algorithm,
|
|
702
737
|
VALUE key_bytes) {
|
|
703
|
-
|
|
704
|
-
|
|
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;
|
|
738
|
+
(void)self;
|
|
739
|
+
return pq_export_container_der(algorithm, key_bytes, pq_public_key_to_pqc_container_der);
|
|
716
740
|
}
|
|
717
741
|
|
|
718
742
|
static VALUE pqcrypto_public_key_to_pqc_container_pem(VALUE self, VALUE algorithm,
|
|
719
743
|
VALUE key_bytes) {
|
|
720
|
-
|
|
721
|
-
|
|
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;
|
|
744
|
+
(void)self;
|
|
745
|
+
return pq_export_container_pem(algorithm, key_bytes, pq_public_key_to_pqc_container_pem);
|
|
733
746
|
}
|
|
734
747
|
|
|
735
748
|
static VALUE pqcrypto_secret_key_to_pqc_container_der(VALUE self, VALUE algorithm,
|
|
736
749
|
VALUE key_bytes) {
|
|
737
|
-
|
|
738
|
-
|
|
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
|
+
(void)self;
|
|
751
|
+
return pq_export_container_der(algorithm, key_bytes, pq_secret_key_to_pqc_container_der);
|
|
750
752
|
}
|
|
751
753
|
|
|
752
754
|
static VALUE pqcrypto_secret_key_to_pqc_container_pem(VALUE self, VALUE algorithm,
|
|
753
755
|
VALUE key_bytes) {
|
|
754
|
-
|
|
755
|
-
|
|
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;
|
|
756
|
+
(void)self;
|
|
757
|
+
return pq_export_container_pem(algorithm, key_bytes, pq_secret_key_to_pqc_container_pem);
|
|
767
758
|
}
|
|
768
759
|
|
|
769
760
|
static VALUE pqcrypto_public_key_from_pqc_container_der(VALUE self, VALUE der) {
|
|
770
|
-
|
|
771
|
-
|
|
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;
|
|
761
|
+
(void)self;
|
|
762
|
+
return pq_import_container_der(der, pq_public_key_from_pqc_container_der);
|
|
786
763
|
}
|
|
787
764
|
|
|
788
765
|
static VALUE pqcrypto_public_key_from_pqc_container_pem(VALUE self, VALUE pem) {
|
|
789
|
-
|
|
790
|
-
|
|
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;
|
|
766
|
+
(void)self;
|
|
767
|
+
return pq_import_container_pem(pem, pq_public_key_from_pqc_container_pem);
|
|
805
768
|
}
|
|
806
769
|
|
|
807
770
|
static VALUE pqcrypto_secret_key_from_pqc_container_der(VALUE self, VALUE der) {
|
|
808
|
-
|
|
809
|
-
|
|
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;
|
|
771
|
+
(void)self;
|
|
772
|
+
return pq_import_container_der(der, pq_secret_key_from_pqc_container_der);
|
|
824
773
|
}
|
|
825
774
|
|
|
826
775
|
static VALUE pqcrypto_secret_key_from_pqc_container_pem(VALUE self, VALUE pem) {
|
|
827
|
-
|
|
828
|
-
|
|
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;
|
|
776
|
+
(void)self;
|
|
777
|
+
return pq_import_container_pem(pem, pq_secret_key_from_pqc_container_pem);
|
|
843
778
|
}
|
|
844
779
|
|
|
845
780
|
void Init_pqcrypto_secure(void) {
|
|
@@ -705,69 +705,60 @@ static const char PQC_OID_ML_KEM_768[] = "2.25.186599352125448088867056807454444
|
|
|
705
705
|
static const char PQC_OID_ML_KEM_768_X25519_HKDF_SHA256[] =
|
|
706
706
|
"2.25.260242945110721168101139140490528778800";
|
|
707
707
|
static const char PQC_OID_ML_DSA_65[] = "2.25.305232938483772195555080795650659207792";
|
|
708
|
-
|
|
709
|
-
static
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
708
|
+
static const char PQC_PUBLIC_KEY_PEM_LABEL[] = "PQC PUBLIC KEY CONTAINER";
|
|
709
|
+
static const char PQC_PRIVATE_KEY_PEM_LABEL[] = "PQC PRIVATE KEY CONTAINER";
|
|
710
|
+
|
|
711
|
+
typedef struct {
|
|
712
|
+
const char *algorithm;
|
|
713
|
+
const char *oid;
|
|
714
|
+
size_t public_key_len;
|
|
715
|
+
size_t secret_key_len;
|
|
716
|
+
} pq_serialization_algorithm_t;
|
|
717
|
+
|
|
718
|
+
static const pq_serialization_algorithm_t PQC_SERIALIZATION_ALGORITHMS[] = {
|
|
719
|
+
{"ml_kem_768", PQC_OID_ML_KEM_768, PQ_MLKEM_PUBLICKEYBYTES, PQ_MLKEM_SECRETKEYBYTES},
|
|
720
|
+
{"ml_kem_768_x25519_hkdf_sha256", PQC_OID_ML_KEM_768_X25519_HKDF_SHA256,
|
|
721
|
+
PQ_HYBRID_PUBLICKEYBYTES, PQ_HYBRID_SECRETKEYBYTES},
|
|
722
|
+
{"ml_dsa_65", PQC_OID_ML_DSA_65, MLDSA_PUBLICKEYBYTES, MLDSA_SECRETKEYBYTES},
|
|
723
|
+
};
|
|
724
|
+
|
|
725
|
+
static const pq_serialization_algorithm_t *pq_find_serialization_algorithm(const char *algorithm) {
|
|
726
|
+
size_t i;
|
|
727
|
+
|
|
728
|
+
if (!algorithm)
|
|
729
|
+
return NULL;
|
|
730
|
+
|
|
731
|
+
for (i = 0; i < sizeof(PQC_SERIALIZATION_ALGORITHMS) / sizeof(PQC_SERIALIZATION_ALGORITHMS[0]);
|
|
732
|
+
++i) {
|
|
733
|
+
if (strcmp(algorithm, PQC_SERIALIZATION_ALGORITHMS[i].algorithm) == 0)
|
|
734
|
+
return &PQC_SERIALIZATION_ALGORITHMS[i];
|
|
720
735
|
}
|
|
721
|
-
if (strcmp(algorithm, "ml_dsa_65") == 0) {
|
|
722
|
-
*expected_len = is_public ? MLDSA_PUBLICKEYBYTES : MLDSA_SECRETKEYBYTES;
|
|
723
|
-
return PQ_SUCCESS;
|
|
724
|
-
}
|
|
725
|
-
return PQ_ERROR_BUFFER;
|
|
726
|
-
}
|
|
727
736
|
|
|
728
|
-
|
|
729
|
-
if (!algorithm || !oid_out)
|
|
730
|
-
return PQ_ERROR_BUFFER;
|
|
731
|
-
if (strcmp(algorithm, "ml_kem_768") == 0) {
|
|
732
|
-
*oid_out = PQC_OID_ML_KEM_768;
|
|
733
|
-
return PQ_SUCCESS;
|
|
734
|
-
}
|
|
735
|
-
if (strcmp(algorithm, "ml_kem_768_x25519_hkdf_sha256") == 0) {
|
|
736
|
-
*oid_out = PQC_OID_ML_KEM_768_X25519_HKDF_SHA256;
|
|
737
|
-
return PQ_SUCCESS;
|
|
738
|
-
}
|
|
739
|
-
if (strcmp(algorithm, "ml_dsa_65") == 0) {
|
|
740
|
-
*oid_out = PQC_OID_ML_DSA_65;
|
|
741
|
-
return PQ_SUCCESS;
|
|
742
|
-
}
|
|
743
|
-
return PQ_ERROR_BUFFER;
|
|
737
|
+
return NULL;
|
|
744
738
|
}
|
|
745
739
|
|
|
746
|
-
static
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
if (
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
}
|
|
759
|
-
if (oid_len == strlen(PQC_OID_ML_DSA_65) && memcmp(oid, PQC_OID_ML_DSA_65, oid_len) == 0) {
|
|
760
|
-
*algorithm_out = "ml_dsa_65";
|
|
761
|
-
return PQ_SUCCESS;
|
|
740
|
+
static const pq_serialization_algorithm_t *pq_find_serialization_algorithm_by_oid(const char *oid,
|
|
741
|
+
size_t oid_len) {
|
|
742
|
+
size_t i;
|
|
743
|
+
|
|
744
|
+
if (!oid)
|
|
745
|
+
return NULL;
|
|
746
|
+
|
|
747
|
+
for (i = 0; i < sizeof(PQC_SERIALIZATION_ALGORITHMS) / sizeof(PQC_SERIALIZATION_ALGORITHMS[0]);
|
|
748
|
+
++i) {
|
|
749
|
+
const pq_serialization_algorithm_t *entry = &PQC_SERIALIZATION_ALGORITHMS[i];
|
|
750
|
+
if (oid_len == strlen(entry->oid) && memcmp(oid, entry->oid, oid_len) == 0)
|
|
751
|
+
return entry;
|
|
762
752
|
}
|
|
763
|
-
|
|
753
|
+
|
|
754
|
+
return NULL;
|
|
764
755
|
}
|
|
765
756
|
|
|
766
757
|
static int pq_encode_serialized_key(uint8_t **output, size_t *output_len, uint8_t type,
|
|
767
758
|
const uint8_t *key_bytes, size_t key_len,
|
|
768
759
|
const char *algorithm) {
|
|
769
|
-
const
|
|
770
|
-
size_t expected_len
|
|
760
|
+
const pq_serialization_algorithm_t *entry;
|
|
761
|
+
size_t expected_len;
|
|
771
762
|
size_t oid_len;
|
|
772
763
|
size_t total_len = 0;
|
|
773
764
|
uint8_t *buf;
|
|
@@ -779,15 +770,16 @@ static int pq_encode_serialized_key(uint8_t **output, size_t *output_len, uint8_
|
|
|
779
770
|
*output = NULL;
|
|
780
771
|
*output_len = 0;
|
|
781
772
|
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
if (ret != PQ_SUCCESS || key_len != expected_len)
|
|
773
|
+
entry = pq_find_serialization_algorithm(algorithm);
|
|
774
|
+
if (!entry)
|
|
785
775
|
return PQ_ERROR_BUFFER;
|
|
786
|
-
ret = pq_serialization_oid_for_algorithm(algorithm, &oid);
|
|
787
|
-
if (ret != PQ_SUCCESS)
|
|
788
|
-
return ret;
|
|
789
776
|
|
|
790
|
-
|
|
777
|
+
expected_len =
|
|
778
|
+
(type == PQC_SERIALIZATION_TYPE_PUBLIC) ? entry->public_key_len : entry->secret_key_len;
|
|
779
|
+
if (key_len != expected_len)
|
|
780
|
+
return PQ_ERROR_BUFFER;
|
|
781
|
+
|
|
782
|
+
oid_len = strlen(entry->oid);
|
|
791
783
|
if (oid_len == 0 || oid_len > UINT16_MAX)
|
|
792
784
|
return PQ_ERROR_BUFFER;
|
|
793
785
|
if (key_len > UINT32_MAX)
|
|
@@ -821,7 +813,7 @@ static int pq_encode_serialized_key(uint8_t **output, size_t *output_len, uint8_
|
|
|
821
813
|
buf[5] = type;
|
|
822
814
|
buf[6] = (uint8_t)((oid_len >> 8) & 0xFF);
|
|
823
815
|
buf[7] = (uint8_t)(oid_len & 0xFF);
|
|
824
|
-
memcpy(buf + 8, oid, oid_len);
|
|
816
|
+
memcpy(buf + 8, entry->oid, oid_len);
|
|
825
817
|
buf[8 + oid_len + 0] = (uint8_t)((key_len >> 24) & 0xFF);
|
|
826
818
|
buf[8 + oid_len + 1] = (uint8_t)((key_len >> 16) & 0xFF);
|
|
827
819
|
buf[8 + oid_len + 2] = (uint8_t)((key_len >> 8) & 0xFF);
|
|
@@ -837,12 +829,11 @@ static int pq_decode_serialized_key(const uint8_t *input, size_t input_len, uint
|
|
|
837
829
|
char **algorithm_out, uint8_t **key_out, size_t *key_len_out) {
|
|
838
830
|
uint16_t oid_len;
|
|
839
831
|
uint32_t key_len;
|
|
840
|
-
const
|
|
832
|
+
const pq_serialization_algorithm_t *entry;
|
|
841
833
|
size_t offset;
|
|
842
834
|
size_t expected_len = 0;
|
|
843
835
|
uint8_t *key_copy = NULL;
|
|
844
836
|
char *algorithm_copy = NULL;
|
|
845
|
-
int ret;
|
|
846
837
|
|
|
847
838
|
if (!input || !algorithm_out || !key_out || !key_len_out)
|
|
848
839
|
return PQ_ERROR_BUFFER;
|
|
@@ -866,18 +857,18 @@ static int pq_decode_serialized_key(const uint8_t *input, size_t input_len, uint
|
|
|
866
857
|
offset = 8;
|
|
867
858
|
if (input_len < offset || input_len - offset < (size_t)oid_len + 4)
|
|
868
859
|
return PQ_ERROR_BUFFER;
|
|
869
|
-
|
|
870
|
-
if (
|
|
871
|
-
return
|
|
860
|
+
entry = pq_find_serialization_algorithm_by_oid((const char *)(input + offset), oid_len);
|
|
861
|
+
if (!entry)
|
|
862
|
+
return PQ_ERROR_BUFFER;
|
|
872
863
|
offset += oid_len;
|
|
873
864
|
key_len = ((uint32_t)input[offset + 0] << 24) | ((uint32_t)input[offset + 1] << 16) |
|
|
874
865
|
((uint32_t)input[offset + 2] << 8) | (uint32_t)input[offset + 3];
|
|
875
866
|
offset += 4;
|
|
876
867
|
if (input_len < offset || input_len - offset != (size_t)key_len)
|
|
877
868
|
return PQ_ERROR_BUFFER;
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
if (
|
|
869
|
+
expected_len = (expected_type == PQC_SERIALIZATION_TYPE_PUBLIC) ? entry->public_key_len
|
|
870
|
+
: entry->secret_key_len;
|
|
871
|
+
if ((size_t)key_len != expected_len)
|
|
881
872
|
return PQ_ERROR_BUFFER;
|
|
882
873
|
|
|
883
874
|
key_copy = malloc((size_t)key_len);
|
|
@@ -886,14 +877,14 @@ static int pq_decode_serialized_key(const uint8_t *input, size_t input_len, uint
|
|
|
886
877
|
memcpy(key_copy, input + offset, (size_t)key_len);
|
|
887
878
|
|
|
888
879
|
{
|
|
889
|
-
size_t algorithm_len = strlen(algorithm);
|
|
880
|
+
size_t algorithm_len = strlen(entry->algorithm);
|
|
890
881
|
algorithm_copy = malloc(algorithm_len + 1);
|
|
891
882
|
if (!algorithm_copy) {
|
|
892
883
|
pq_secure_wipe(key_copy, (size_t)key_len);
|
|
893
884
|
free(key_copy);
|
|
894
885
|
return PQ_ERROR_NOMEM;
|
|
895
886
|
}
|
|
896
|
-
memcpy(algorithm_copy, algorithm, algorithm_len + 1);
|
|
887
|
+
memcpy(algorithm_copy, entry->algorithm, algorithm_len + 1);
|
|
897
888
|
}
|
|
898
889
|
|
|
899
890
|
*algorithm_out = algorithm_copy;
|
|
@@ -1104,7 +1095,7 @@ int pq_public_key_to_pqc_container_pem(char **output, size_t *output_len, const
|
|
|
1104
1095
|
ret = pq_public_key_to_pqc_container_der(&der, &der_len, public_key, public_key_len, algorithm);
|
|
1105
1096
|
if (ret != PQ_SUCCESS)
|
|
1106
1097
|
return ret;
|
|
1107
|
-
ret = pq_der_to_pem(
|
|
1098
|
+
ret = pq_der_to_pem(PQC_PUBLIC_KEY_PEM_LABEL, der, der_len, output, output_len);
|
|
1108
1099
|
pq_secure_wipe(der, der_len);
|
|
1109
1100
|
free(der);
|
|
1110
1101
|
return ret;
|
|
@@ -1125,7 +1116,7 @@ int pq_secret_key_to_pqc_container_pem(char **output, size_t *output_len, const
|
|
|
1125
1116
|
ret = pq_secret_key_to_pqc_container_der(&der, &der_len, secret_key, secret_key_len, algorithm);
|
|
1126
1117
|
if (ret != PQ_SUCCESS)
|
|
1127
1118
|
return ret;
|
|
1128
|
-
ret = pq_der_to_pem(
|
|
1119
|
+
ret = pq_der_to_pem(PQC_PRIVATE_KEY_PEM_LABEL, der, der_len, output, output_len);
|
|
1129
1120
|
pq_secure_wipe(der, der_len);
|
|
1130
1121
|
free(der);
|
|
1131
1122
|
return ret;
|
|
@@ -1143,7 +1134,7 @@ int pq_public_key_from_pqc_container_pem(char **algorithm_out, uint8_t **key_out
|
|
|
1143
1134
|
uint8_t *der = NULL;
|
|
1144
1135
|
size_t der_len = 0;
|
|
1145
1136
|
int ret;
|
|
1146
|
-
ret = pq_pem_to_der(
|
|
1137
|
+
ret = pq_pem_to_der(PQC_PUBLIC_KEY_PEM_LABEL, input, input_len, &der, &der_len);
|
|
1147
1138
|
if (ret != PQ_SUCCESS)
|
|
1148
1139
|
return ret;
|
|
1149
1140
|
ret = pq_public_key_from_pqc_container_der(algorithm_out, key_out, key_len_out, der, der_len);
|
|
@@ -1164,7 +1155,7 @@ int pq_secret_key_from_pqc_container_pem(char **algorithm_out, uint8_t **key_out
|
|
|
1164
1155
|
uint8_t *der = NULL;
|
|
1165
1156
|
size_t der_len = 0;
|
|
1166
1157
|
int ret;
|
|
1167
|
-
ret = pq_pem_to_der(
|
|
1158
|
+
ret = pq_pem_to_der(PQC_PRIVATE_KEY_PEM_LABEL, input, input_len, &der, &der_len);
|
|
1168
1159
|
if (ret != PQ_SUCCESS)
|
|
1169
1160
|
return ret;
|
|
1170
1161
|
ret = pq_secret_key_from_pqc_container_der(algorithm_out, key_out, key_len_out, der, der_len);
|
|
@@ -1174,5 +1165,5 @@ int pq_secret_key_from_pqc_container_pem(char **algorithm_out, uint8_t **key_out
|
|
|
1174
1165
|
}
|
|
1175
1166
|
|
|
1176
1167
|
const char *pq_version(void) {
|
|
1177
|
-
return "0.
|
|
1168
|
+
return "0.2.0";
|
|
1178
1169
|
}
|
data/lib/pq_crypto/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,29 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: pq_crypto
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Roman Haydarov
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: exe
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date: 2026-04-
|
|
10
|
+
date: 2026-04-22 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
|
-
- !ruby/object:Gem::Dependency
|
|
14
|
-
name: bundler
|
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
|
16
|
-
requirements:
|
|
17
|
-
- - "~>"
|
|
18
|
-
- !ruby/object:Gem::Version
|
|
19
|
-
version: '2.0'
|
|
20
|
-
type: :development
|
|
21
|
-
prerelease: false
|
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
-
requirements:
|
|
24
|
-
- - "~>"
|
|
25
|
-
- !ruby/object:Gem::Version
|
|
26
|
-
version: '2.0'
|
|
27
12
|
- !ruby/object:Gem::Dependency
|
|
28
13
|
name: rake
|
|
29
14
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -173,7 +158,6 @@ metadata:
|
|
|
173
158
|
homepage_uri: https://github.com/roman-haidarov/pq_crypto
|
|
174
159
|
source_code_uri: https://github.com/roman-haidarov/pq_crypto
|
|
175
160
|
changelog_uri: https://github.com/roman-haidarov/pq_crypto/blob/main/CHANGELOG.md
|
|
176
|
-
post_install_message:
|
|
177
161
|
rdoc_options: []
|
|
178
162
|
require_paths:
|
|
179
163
|
- lib
|
|
@@ -181,15 +165,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
181
165
|
requirements:
|
|
182
166
|
- - ">="
|
|
183
167
|
- !ruby/object:Gem::Version
|
|
184
|
-
version: 3.
|
|
168
|
+
version: 3.4.0.a
|
|
185
169
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
186
170
|
requirements:
|
|
187
171
|
- - ">="
|
|
188
172
|
- !ruby/object:Gem::Version
|
|
189
173
|
version: '0'
|
|
190
174
|
requirements: []
|
|
191
|
-
rubygems_version: 3.
|
|
192
|
-
signing_key:
|
|
175
|
+
rubygems_version: 3.6.2
|
|
193
176
|
specification_version: 4
|
|
194
177
|
summary: Primitive-first post-quantum cryptography for Ruby
|
|
195
178
|
test_files: []
|