pq_crypto 0.3.1 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +13 -0
- data/GET_STARTED.md +23 -0
- data/README.md +47 -0
- data/ext/pqcrypto/extconf.rb +46 -3
- data/ext/pqcrypto/pq_externalmu.c +297 -0
- data/ext/pqcrypto/pqcrypto_ruby_secure.c +305 -9
- data/ext/pqcrypto/pqcrypto_secure.c +1 -1
- data/ext/pqcrypto/pqcrypto_secure.h +16 -0
- data/ext/pqcrypto/vendor/pqclean/common/keccak4x/Makefile +8 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/Makefile +19 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-65/clean/Makefile +19 -0
- data/lib/pq_crypto/signature.rb +112 -0
- data/lib/pq_crypto/version.rb +1 -1
- data/lib/pq_crypto.rb +12 -0
- metadata +5 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8cd88ab6ebe3042111e60711895a9563a1510057a321ac9dab510496634656b8
|
|
4
|
+
data.tar.gz: 684f469f8be7912780e00d368989418499aae00bb530d7fc32f8b6c6d5576593
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 54c1f3b0b8a2f7141d3ec4c8649c003793a3469f212fed94b0dc7ef0e2b0ff3ddba510383a0b62813d9ee98700bf266547be1900693c9ad465fb36deec91ab7b
|
|
7
|
+
data.tar.gz: 41c0bdea2d91bbd2a2e8884ee2b2a328c4c06d410fba8d92c9c1a9470b9d5597d0b8f5233b0ba87225535a80c9055033518ef1dd82e90101a9f5ffcd606b81a6
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.3.2] — 2026-04-25
|
|
4
|
+
|
|
5
|
+
### Added — streaming ML-DSA for large inputs
|
|
6
|
+
|
|
7
|
+
- Added `PQCrypto::Signature::SecretKey#sign_io(io, chunk_size: 1 << 20, context: "".b)`.
|
|
8
|
+
- Added `PQCrypto::Signature::PublicKey#verify_io(io, signature, chunk_size: 1 << 20, context: "".b)` and `verify_io!`.
|
|
9
|
+
- Implemented streaming pure ML-DSA through an internal FIPS 204 ExternalMu path. Existing one-shot `sign` / `verify` semantics are unchanged; no public `sign_mu` / `verify_mu` API is exposed.
|
|
10
|
+
|
|
11
|
+
### Notes
|
|
12
|
+
|
|
13
|
+
- Streaming is primarily for large IO inputs and lower peak memory pressure. It is not a HashML-DSA/prehash speed mode; CPU cost is still dominated by SHAKE/Keccak.
|
|
14
|
+
- Default empty-context streaming signatures interoperate with the existing one-shot `verify(message, signature)` API. Non-empty `context:` must be supplied again during `verify_io`.
|
|
15
|
+
|
|
3
16
|
## [0.3.1] — 2026-04-24
|
|
4
17
|
|
|
5
18
|
### Fixed — X-Wing draft-10 compatibility
|
data/GET_STARTED.md
CHANGED
|
@@ -35,6 +35,29 @@ sig.public_key.verify("message", signature)
|
|
|
35
35
|
sig.public_key.verify!("message", signature)
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
+
For large files, use streaming ML-DSA:
|
|
39
|
+
|
|
40
|
+
```ruby
|
|
41
|
+
signature = File.open("document.bin", "rb") do |io|
|
|
42
|
+
sig.secret_key.sign_io(io, chunk_size: 1 << 20)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
ok = File.open("document.bin", "rb") do |io|
|
|
46
|
+
sig.public_key.verify_io(io, signature, chunk_size: 1 << 20)
|
|
47
|
+
end
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
With an optional context:
|
|
51
|
+
|
|
52
|
+
```ruby
|
|
53
|
+
ctx = "document-v1".b
|
|
54
|
+
signature = File.open("document.bin", "rb") { |io| sig.secret_key.sign_io(io, context: ctx) }
|
|
55
|
+
ok = File.open("document.bin", "rb") { |io| sig.public_key.verify_io(io, signature, context: ctx) }
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
`sign_io` / `verify_io` are pure ML-DSA streaming helpers, not prehash
|
|
59
|
+
shortcuts. `verify_io!` raises on mismatch.
|
|
60
|
+
|
|
38
61
|
## 6. Hybrid KEM (X-Wing)
|
|
39
62
|
|
|
40
63
|
```ruby
|
data/README.md
CHANGED
|
@@ -20,6 +20,7 @@ roll-your-own where a library primitive exists.
|
|
|
20
20
|
|
|
21
21
|
- primitive-first API only
|
|
22
22
|
- no protocol/session helpers in the public surface
|
|
23
|
+
- streaming ML-DSA signing/verification is available for large IO inputs
|
|
23
24
|
- serialization uses pq_crypto-specific `pqc_container_*` wrappers
|
|
24
25
|
- not audited
|
|
25
26
|
- not yet positioned as production-ready
|
|
@@ -44,6 +45,20 @@ bundle exec rake compile
|
|
|
44
45
|
- a C toolchain with C11 support (for `_Static_assert` / `_Thread_local`)
|
|
45
46
|
- OpenSSL **3.0 or later** with SHA3-256 and SHAKE256 available (default provider)
|
|
46
47
|
|
|
48
|
+
### Build-time Keccak backend
|
|
49
|
+
|
|
50
|
+
The default build uses PQClean's scalar `common/fips202.c` backend:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
PQCRYPTO_KECCAK_BACKEND=clean bundle exec rake compile
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
`PQCRYPTO_KECCAK_BACKEND=xkcp` is reserved for a separately vendored,
|
|
57
|
+
reviewed, `fips202.h`-compatible XKCP adapter. If requested without that
|
|
58
|
+
adapter, the build aborts instead of silently falling back to `clean`.
|
|
59
|
+
This avoids mixing OpenSSL EVP SHAKE state with PQClean SHAKE state and
|
|
60
|
+
keeps output-byte compatibility explicit.
|
|
61
|
+
|
|
47
62
|
## Async / Fiber scheduler support
|
|
48
63
|
|
|
49
64
|
`pq_crypto` does not require any gem-specific Async configuration. On
|
|
@@ -100,6 +115,8 @@ shared_secret = keypair.secret_key.decapsulate(result.ciphertext)
|
|
|
100
115
|
|
|
101
116
|
### ML-DSA-65
|
|
102
117
|
|
|
118
|
+
One-shot signing keeps the existing API:
|
|
119
|
+
|
|
103
120
|
```ruby
|
|
104
121
|
keypair = PQCrypto::Signature.generate(:ml_dsa_65)
|
|
105
122
|
signature = keypair.secret_key.sign("hello")
|
|
@@ -108,6 +125,36 @@ keypair.public_key.verify("hello", signature) # => true / false
|
|
|
108
125
|
keypair.public_key.verify!("hello", signature) # raises on mismatch
|
|
109
126
|
```
|
|
110
127
|
|
|
128
|
+
For large inputs, use streaming IO so the message does not need to be
|
|
129
|
+
materialized as one Ruby string:
|
|
130
|
+
|
|
131
|
+
```ruby
|
|
132
|
+
signature = File.open("document.bin", "rb") do |io|
|
|
133
|
+
keypair.secret_key.sign_io(io, chunk_size: 1 << 20)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
ok = File.open("document.bin", "rb") do |io|
|
|
137
|
+
keypair.public_key.verify_io(io, signature, chunk_size: 1 << 20)
|
|
138
|
+
end
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
`sign_io` / `verify_io` use pure ML-DSA with an internal FIPS 204
|
|
142
|
+
ExternalMu flow. They are not HashML-DSA/prehash shortcuts and do not
|
|
143
|
+
expose public `sign_mu` / `verify_mu` APIs. With the default empty
|
|
144
|
+
context, streaming signatures verify with `verify(message, signature)`
|
|
145
|
+
and one-shot signatures verify with `verify_io(io, signature)`.
|
|
146
|
+
|
|
147
|
+
Optional context is supported and must match on verify:
|
|
148
|
+
|
|
149
|
+
```ruby
|
|
150
|
+
ctx = "invoice-v1".b
|
|
151
|
+
signature = File.open("document.bin", "rb") { |io| keypair.secret_key.sign_io(io, context: ctx) }
|
|
152
|
+
ok = File.open("document.bin", "rb") { |io| keypair.public_key.verify_io(io, signature, context: ctx) }
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
`chunk_size` must be positive. `context` is limited to 255 bytes by
|
|
156
|
+
FIPS 204. `verify_io!` raises `PQCrypto::VerificationError` on mismatch.
|
|
157
|
+
|
|
111
158
|
Note: `verify` returns a plain boolean for normal outcomes. `verify!`
|
|
112
159
|
raises `PQCrypto::VerificationError` when the signature does not
|
|
113
160
|
match.
|
data/ext/pqcrypto/extconf.rb
CHANGED
|
@@ -11,6 +11,9 @@ $LDFLAGS << " -Wl,-no_warn_duplicate_libraries" if RbConfig::CONFIG["host_os"] =
|
|
|
11
11
|
|
|
12
12
|
USE_SYSTEM = arg_config("--use-system-libraries") || ENV["PQCRYPTO_USE_SYSTEM_LIBRARIES"]
|
|
13
13
|
|
|
14
|
+
KECCAK_BACKEND = (ENV["PQCRYPTO_KECCAK_BACKEND"] || "clean").strip.downcase
|
|
15
|
+
SUPPORTED_KECCAK_BACKENDS = %w[clean xkcp].freeze
|
|
16
|
+
|
|
14
17
|
SANITIZE = ENV["PQCRYPTO_SANITIZE"]
|
|
15
18
|
|
|
16
19
|
if SANITIZE && !SANITIZE.strip.empty?
|
|
@@ -85,6 +88,41 @@ def configure_openssl!
|
|
|
85
88
|
$CFLAGS << " -DHAVE_OPENSSL_EVP_H -DHAVE_OPENSSL_RAND_H"
|
|
86
89
|
end
|
|
87
90
|
|
|
91
|
+
def configure_keccak_backend(vendor_dir, common_dir)
|
|
92
|
+
abort "Unsupported PQCRYPTO_KECCAK_BACKEND=#{KECCAK_BACKEND.inspect}. Supported: #{SUPPORTED_KECCAK_BACKENDS.join(", ")}" unless SUPPORTED_KECCAK_BACKENDS.include?(KECCAK_BACKEND)
|
|
93
|
+
|
|
94
|
+
case KECCAK_BACKEND
|
|
95
|
+
when "clean"
|
|
96
|
+
{
|
|
97
|
+
name: "clean",
|
|
98
|
+
include_dirs: [],
|
|
99
|
+
source_group: ["pqclean_common", [File.join(common_dir, "fips202.c")]]
|
|
100
|
+
}
|
|
101
|
+
when "xkcp"
|
|
102
|
+
# The optimized backend must provide the same fips202.h-compatible API as
|
|
103
|
+
# PQClean's common/fips202.c. Do not substitute OpenSSL EVP SHAKE here: the
|
|
104
|
+
# PQClean SHAKE state layout is part of the ML-KEM/ML-DSA call graph.
|
|
105
|
+
xkcp_dir = File.join(vendor_dir, "xkcp")
|
|
106
|
+
adapter_source = File.join(xkcp_dir, "pqclean_fips202_xkcp.c")
|
|
107
|
+
|
|
108
|
+
abort <<~MSG unless File.exist?(adapter_source)
|
|
109
|
+
PQCRYPTO_KECCAK_BACKEND=xkcp was requested, but no reviewed XKCP adapter was found.
|
|
110
|
+
|
|
111
|
+
Expected:
|
|
112
|
+
#{adapter_source}
|
|
113
|
+
|
|
114
|
+
Refusing to fall back silently to the clean backend. Vendor a fips202.h-compatible
|
|
115
|
+
XKCP adapter first, then run the full SHAKE-dependent KAT/regression test matrix.
|
|
116
|
+
MSG
|
|
117
|
+
|
|
118
|
+
{
|
|
119
|
+
name: "xkcp",
|
|
120
|
+
include_dirs: [xkcp_dir],
|
|
121
|
+
source_group: ["xkcp_keccak", [adapter_source]]
|
|
122
|
+
}
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
88
126
|
def configure_pqclean(vendor_dir)
|
|
89
127
|
return nil unless vendor_dir
|
|
90
128
|
|
|
@@ -95,17 +133,20 @@ def configure_pqclean(vendor_dir)
|
|
|
95
133
|
mldsa_dir = File.join(pqclean_dir, "crypto_sign", "ml-dsa-65", "clean")
|
|
96
134
|
common_dir = File.join(pqclean_dir, "common")
|
|
97
135
|
|
|
98
|
-
|
|
136
|
+
keccak_config = configure_keccak_backend(vendor_dir, common_dir)
|
|
137
|
+
|
|
138
|
+
include_dirs = [mlkem_dir, mldsa_dir, common_dir, *keccak_config[:include_dirs]]
|
|
99
139
|
return nil unless include_dirs.all? { |dir| Dir.exist?(dir) }
|
|
100
140
|
|
|
101
141
|
mlkem_sources = Dir.glob(File.join(mlkem_dir, "*.c")).sort
|
|
102
142
|
mldsa_sources = Dir.glob(File.join(mldsa_dir, "*.c")).sort
|
|
103
|
-
common_sources = %w[
|
|
143
|
+
common_sources = %w[sha2.c sp800-185.c].map { |name| File.join(common_dir, name) }
|
|
104
144
|
|
|
105
145
|
source_groups = [
|
|
106
146
|
["pqclean_mlkem", mlkem_sources],
|
|
107
147
|
["pqclean_mldsa", mldsa_sources],
|
|
108
|
-
["pqclean_common", common_sources]
|
|
148
|
+
["pqclean_common", common_sources],
|
|
149
|
+
keccak_config[:source_group]
|
|
109
150
|
]
|
|
110
151
|
|
|
111
152
|
return nil unless source_groups.all? { |_, sources| sources.all? { |path| File.exist?(path) } }
|
|
@@ -115,6 +156,7 @@ def configure_pqclean(vendor_dir)
|
|
|
115
156
|
|
|
116
157
|
{
|
|
117
158
|
include_dirs: include_dirs,
|
|
159
|
+
keccak_backend: keccak_config[:name],
|
|
118
160
|
source_groups: source_groups
|
|
119
161
|
}
|
|
120
162
|
end
|
|
@@ -165,6 +207,7 @@ pqclean_config = configure_pqclean(vendor_dir)
|
|
|
165
207
|
puts "OpenSSL: system"
|
|
166
208
|
abort "PQClean vendored sources are required. Run: bundle exec rake vendor" unless pqclean_config
|
|
167
209
|
puts "PQClean: vendored (randombytes overridden by pq_randombytes.c)"
|
|
210
|
+
puts "Keccak backend: #{pqclean_config[:keccak_backend]}"
|
|
168
211
|
puts "Output: pqcrypto/pqcrypto_secure"
|
|
169
212
|
puts "===================================="
|
|
170
213
|
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
#include "pqcrypto_secure.h"
|
|
2
|
+
|
|
3
|
+
#include <stdint.h>
|
|
4
|
+
#include <stddef.h>
|
|
5
|
+
#include <string.h>
|
|
6
|
+
|
|
7
|
+
#include "vendor/pqclean/crypto_sign/ml-dsa-65/clean/params.h"
|
|
8
|
+
#include "vendor/pqclean/crypto_sign/ml-dsa-65/clean/packing.h"
|
|
9
|
+
#include "vendor/pqclean/crypto_sign/ml-dsa-65/clean/polyvec.h"
|
|
10
|
+
#include "vendor/pqclean/crypto_sign/ml-dsa-65/clean/poly.h"
|
|
11
|
+
#include "vendor/pqclean/crypto_sign/ml-dsa-65/clean/symmetric.h"
|
|
12
|
+
#include "fips202.h"
|
|
13
|
+
#include "randombytes.h"
|
|
14
|
+
|
|
15
|
+
#if CRHBYTES != PQ_MLDSA_MUBYTES
|
|
16
|
+
#error "PQ_MLDSA_MUBYTES must match PQClean's CRHBYTES"
|
|
17
|
+
#endif
|
|
18
|
+
#if TRBYTES != PQ_MLDSA_TRBYTES
|
|
19
|
+
#error "PQ_MLDSA_TRBYTES must match PQClean's TRBYTES"
|
|
20
|
+
#endif
|
|
21
|
+
|
|
22
|
+
int pq_mldsa_extract_tr_from_secret_key(uint8_t *tr_out, const uint8_t *secret_key) {
|
|
23
|
+
if (tr_out == NULL || secret_key == NULL) {
|
|
24
|
+
return PQ_ERROR_BUFFER;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
uint8_t rho[SEEDBYTES];
|
|
28
|
+
uint8_t key[SEEDBYTES];
|
|
29
|
+
polyveck t0;
|
|
30
|
+
polyvecl s1;
|
|
31
|
+
polyveck s2;
|
|
32
|
+
|
|
33
|
+
PQCLEAN_MLDSA65_CLEAN_unpack_sk(rho, tr_out, key, &t0, &s1, &s2, secret_key);
|
|
34
|
+
|
|
35
|
+
pq_secure_wipe(rho, sizeof(rho));
|
|
36
|
+
pq_secure_wipe(key, sizeof(key));
|
|
37
|
+
pq_secure_wipe(&t0, sizeof(t0));
|
|
38
|
+
pq_secure_wipe(&s1, sizeof(s1));
|
|
39
|
+
pq_secure_wipe(&s2, sizeof(s2));
|
|
40
|
+
|
|
41
|
+
return PQ_SUCCESS;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
int pq_mldsa_compute_tr_from_public_key(uint8_t *tr_out, const uint8_t *public_key) {
|
|
45
|
+
if (tr_out == NULL || public_key == NULL) {
|
|
46
|
+
return PQ_ERROR_BUFFER;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
shake256(tr_out, TRBYTES, public_key, PQCLEAN_MLDSA65_CLEAN_CRYPTO_PUBLICKEYBYTES);
|
|
50
|
+
return PQ_SUCCESS;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
int pq_sign_mu(uint8_t *signature, size_t *signature_len, const uint8_t *mu,
|
|
54
|
+
const uint8_t *secret_key) {
|
|
55
|
+
if (signature == NULL || signature_len == NULL || mu == NULL || secret_key == NULL) {
|
|
56
|
+
return PQ_ERROR_BUFFER;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
unsigned int n;
|
|
60
|
+
uint8_t rho[SEEDBYTES];
|
|
61
|
+
uint8_t tr_unused[TRBYTES];
|
|
62
|
+
uint8_t key[SEEDBYTES];
|
|
63
|
+
uint8_t rnd[RNDBYTES];
|
|
64
|
+
uint8_t mu_local[CRHBYTES];
|
|
65
|
+
uint8_t rhoprime[CRHBYTES];
|
|
66
|
+
uint16_t nonce = 0;
|
|
67
|
+
polyvecl mat[K], s1, y, z;
|
|
68
|
+
polyveck t0, s2, w1, w0, h;
|
|
69
|
+
poly cp;
|
|
70
|
+
shake256incctx state;
|
|
71
|
+
|
|
72
|
+
PQCLEAN_MLDSA65_CLEAN_unpack_sk(rho, tr_unused, key, &t0, &s1, &s2, secret_key);
|
|
73
|
+
pq_secure_wipe(tr_unused, sizeof(tr_unused));
|
|
74
|
+
|
|
75
|
+
memcpy(mu_local, mu, CRHBYTES);
|
|
76
|
+
|
|
77
|
+
randombytes(rnd, RNDBYTES);
|
|
78
|
+
|
|
79
|
+
{
|
|
80
|
+
uint8_t kr[SEEDBYTES + RNDBYTES + CRHBYTES];
|
|
81
|
+
memcpy(kr, key, SEEDBYTES);
|
|
82
|
+
memcpy(kr + SEEDBYTES, rnd, RNDBYTES);
|
|
83
|
+
memcpy(kr + SEEDBYTES + RNDBYTES, mu_local, CRHBYTES);
|
|
84
|
+
shake256(rhoprime, CRHBYTES, kr, sizeof(kr));
|
|
85
|
+
pq_secure_wipe(kr, sizeof(kr));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
PQCLEAN_MLDSA65_CLEAN_polyvec_matrix_expand(mat, rho);
|
|
89
|
+
PQCLEAN_MLDSA65_CLEAN_polyvecl_ntt(&s1);
|
|
90
|
+
PQCLEAN_MLDSA65_CLEAN_polyveck_ntt(&s2);
|
|
91
|
+
PQCLEAN_MLDSA65_CLEAN_polyveck_ntt(&t0);
|
|
92
|
+
|
|
93
|
+
rej:
|
|
94
|
+
PQCLEAN_MLDSA65_CLEAN_polyvecl_uniform_gamma1(&y, rhoprime, nonce++);
|
|
95
|
+
|
|
96
|
+
z = y;
|
|
97
|
+
PQCLEAN_MLDSA65_CLEAN_polyvecl_ntt(&z);
|
|
98
|
+
PQCLEAN_MLDSA65_CLEAN_polyvec_matrix_pointwise_montgomery(&w1, mat, &z);
|
|
99
|
+
PQCLEAN_MLDSA65_CLEAN_polyveck_reduce(&w1);
|
|
100
|
+
PQCLEAN_MLDSA65_CLEAN_polyveck_invntt_tomont(&w1);
|
|
101
|
+
|
|
102
|
+
PQCLEAN_MLDSA65_CLEAN_polyveck_caddq(&w1);
|
|
103
|
+
PQCLEAN_MLDSA65_CLEAN_polyveck_decompose(&w1, &w0, &w1);
|
|
104
|
+
PQCLEAN_MLDSA65_CLEAN_polyveck_pack_w1(signature, &w1);
|
|
105
|
+
|
|
106
|
+
shake256_inc_init(&state);
|
|
107
|
+
shake256_inc_absorb(&state, mu_local, CRHBYTES);
|
|
108
|
+
shake256_inc_absorb(&state, signature, K * POLYW1_PACKEDBYTES);
|
|
109
|
+
shake256_inc_finalize(&state);
|
|
110
|
+
shake256_inc_squeeze(signature, CTILDEBYTES, &state);
|
|
111
|
+
shake256_inc_ctx_release(&state);
|
|
112
|
+
|
|
113
|
+
PQCLEAN_MLDSA65_CLEAN_poly_challenge(&cp, signature);
|
|
114
|
+
PQCLEAN_MLDSA65_CLEAN_poly_ntt(&cp);
|
|
115
|
+
|
|
116
|
+
PQCLEAN_MLDSA65_CLEAN_polyvecl_pointwise_poly_montgomery(&z, &cp, &s1);
|
|
117
|
+
PQCLEAN_MLDSA65_CLEAN_polyvecl_invntt_tomont(&z);
|
|
118
|
+
PQCLEAN_MLDSA65_CLEAN_polyvecl_add(&z, &z, &y);
|
|
119
|
+
PQCLEAN_MLDSA65_CLEAN_polyvecl_reduce(&z);
|
|
120
|
+
if (PQCLEAN_MLDSA65_CLEAN_polyvecl_chknorm(&z, GAMMA1 - BETA)) {
|
|
121
|
+
goto rej;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
PQCLEAN_MLDSA65_CLEAN_polyveck_pointwise_poly_montgomery(&h, &cp, &s2);
|
|
125
|
+
PQCLEAN_MLDSA65_CLEAN_polyveck_invntt_tomont(&h);
|
|
126
|
+
PQCLEAN_MLDSA65_CLEAN_polyveck_sub(&w0, &w0, &h);
|
|
127
|
+
PQCLEAN_MLDSA65_CLEAN_polyveck_reduce(&w0);
|
|
128
|
+
if (PQCLEAN_MLDSA65_CLEAN_polyveck_chknorm(&w0, GAMMA2 - BETA)) {
|
|
129
|
+
goto rej;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
PQCLEAN_MLDSA65_CLEAN_polyveck_pointwise_poly_montgomery(&h, &cp, &t0);
|
|
133
|
+
PQCLEAN_MLDSA65_CLEAN_polyveck_invntt_tomont(&h);
|
|
134
|
+
PQCLEAN_MLDSA65_CLEAN_polyveck_reduce(&h);
|
|
135
|
+
if (PQCLEAN_MLDSA65_CLEAN_polyveck_chknorm(&h, GAMMA2)) {
|
|
136
|
+
goto rej;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
PQCLEAN_MLDSA65_CLEAN_polyveck_add(&w0, &w0, &h);
|
|
140
|
+
n = PQCLEAN_MLDSA65_CLEAN_polyveck_make_hint(&h, &w0, &w1);
|
|
141
|
+
if (n > OMEGA) {
|
|
142
|
+
goto rej;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
PQCLEAN_MLDSA65_CLEAN_pack_sig(signature, signature, &z, &h);
|
|
146
|
+
*signature_len = PQCLEAN_MLDSA65_CLEAN_CRYPTO_BYTES;
|
|
147
|
+
|
|
148
|
+
pq_secure_wipe(rho, sizeof(rho));
|
|
149
|
+
pq_secure_wipe(key, sizeof(key));
|
|
150
|
+
pq_secure_wipe(rnd, sizeof(rnd));
|
|
151
|
+
pq_secure_wipe(mu_local, sizeof(mu_local));
|
|
152
|
+
pq_secure_wipe(rhoprime, sizeof(rhoprime));
|
|
153
|
+
pq_secure_wipe(&s1, sizeof(s1));
|
|
154
|
+
pq_secure_wipe(&s2, sizeof(s2));
|
|
155
|
+
pq_secure_wipe(&t0, sizeof(t0));
|
|
156
|
+
pq_secure_wipe(&y, sizeof(y));
|
|
157
|
+
pq_secure_wipe(&z, sizeof(z));
|
|
158
|
+
pq_secure_wipe(&w0, sizeof(w0));
|
|
159
|
+
pq_secure_wipe(&cp, sizeof(cp));
|
|
160
|
+
|
|
161
|
+
return PQ_SUCCESS;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
int pq_verify_mu(const uint8_t *signature, size_t signature_len, const uint8_t *mu,
|
|
165
|
+
const uint8_t *public_key) {
|
|
166
|
+
if (signature == NULL || mu == NULL || public_key == NULL) {
|
|
167
|
+
return PQ_ERROR_BUFFER;
|
|
168
|
+
}
|
|
169
|
+
if (signature_len != PQCLEAN_MLDSA65_CLEAN_CRYPTO_BYTES) {
|
|
170
|
+
return PQ_ERROR_VERIFY;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
unsigned int i;
|
|
174
|
+
uint8_t buf[K * POLYW1_PACKEDBYTES];
|
|
175
|
+
uint8_t rho[SEEDBYTES];
|
|
176
|
+
uint8_t c[CTILDEBYTES];
|
|
177
|
+
uint8_t c2[CTILDEBYTES];
|
|
178
|
+
poly cp;
|
|
179
|
+
polyvecl mat[K], z;
|
|
180
|
+
polyveck t1, w1, h;
|
|
181
|
+
shake256incctx state;
|
|
182
|
+
|
|
183
|
+
PQCLEAN_MLDSA65_CLEAN_unpack_pk(rho, &t1, public_key);
|
|
184
|
+
if (PQCLEAN_MLDSA65_CLEAN_unpack_sig(c, &z, &h, signature)) {
|
|
185
|
+
return PQ_ERROR_VERIFY;
|
|
186
|
+
}
|
|
187
|
+
if (PQCLEAN_MLDSA65_CLEAN_polyvecl_chknorm(&z, GAMMA1 - BETA)) {
|
|
188
|
+
return PQ_ERROR_VERIFY;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
PQCLEAN_MLDSA65_CLEAN_poly_challenge(&cp, c);
|
|
192
|
+
PQCLEAN_MLDSA65_CLEAN_polyvec_matrix_expand(mat, rho);
|
|
193
|
+
|
|
194
|
+
PQCLEAN_MLDSA65_CLEAN_polyvecl_ntt(&z);
|
|
195
|
+
PQCLEAN_MLDSA65_CLEAN_polyvec_matrix_pointwise_montgomery(&w1, mat, &z);
|
|
196
|
+
|
|
197
|
+
PQCLEAN_MLDSA65_CLEAN_poly_ntt(&cp);
|
|
198
|
+
PQCLEAN_MLDSA65_CLEAN_polyveck_shiftl(&t1);
|
|
199
|
+
PQCLEAN_MLDSA65_CLEAN_polyveck_ntt(&t1);
|
|
200
|
+
PQCLEAN_MLDSA65_CLEAN_polyveck_pointwise_poly_montgomery(&t1, &cp, &t1);
|
|
201
|
+
|
|
202
|
+
PQCLEAN_MLDSA65_CLEAN_polyveck_sub(&w1, &w1, &t1);
|
|
203
|
+
PQCLEAN_MLDSA65_CLEAN_polyveck_reduce(&w1);
|
|
204
|
+
PQCLEAN_MLDSA65_CLEAN_polyveck_invntt_tomont(&w1);
|
|
205
|
+
|
|
206
|
+
PQCLEAN_MLDSA65_CLEAN_polyveck_caddq(&w1);
|
|
207
|
+
PQCLEAN_MLDSA65_CLEAN_polyveck_use_hint(&w1, &w1, &h);
|
|
208
|
+
PQCLEAN_MLDSA65_CLEAN_polyveck_pack_w1(buf, &w1);
|
|
209
|
+
|
|
210
|
+
shake256_inc_init(&state);
|
|
211
|
+
shake256_inc_absorb(&state, mu, CRHBYTES);
|
|
212
|
+
shake256_inc_absorb(&state, buf, K * POLYW1_PACKEDBYTES);
|
|
213
|
+
shake256_inc_finalize(&state);
|
|
214
|
+
shake256_inc_squeeze(c2, CTILDEBYTES, &state);
|
|
215
|
+
shake256_inc_ctx_release(&state);
|
|
216
|
+
|
|
217
|
+
for (i = 0; i < CTILDEBYTES; ++i) {
|
|
218
|
+
if (c[i] != c2[i]) {
|
|
219
|
+
return PQ_ERROR_VERIFY;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return PQ_SUCCESS;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
void *pq_mu_builder_new(void) {
|
|
227
|
+
shake256incctx *state = (shake256incctx *)malloc(sizeof(shake256incctx));
|
|
228
|
+
if (state == NULL) {
|
|
229
|
+
return NULL;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
shake256_inc_init(state);
|
|
233
|
+
return state;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
int pq_mu_builder_init(void *state_ptr, const uint8_t *tr, const uint8_t *ctx, size_t ctxlen) {
|
|
237
|
+
if (state_ptr == NULL || tr == NULL) {
|
|
238
|
+
return PQ_ERROR_BUFFER;
|
|
239
|
+
}
|
|
240
|
+
if (ctxlen > 255) {
|
|
241
|
+
return PQ_ERROR_BUFFER;
|
|
242
|
+
}
|
|
243
|
+
if (ctxlen > 0 && ctx == NULL) {
|
|
244
|
+
return PQ_ERROR_BUFFER;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
shake256incctx *state = (shake256incctx *)state_ptr;
|
|
248
|
+
|
|
249
|
+
uint8_t prefix[2];
|
|
250
|
+
prefix[0] = 0x00;
|
|
251
|
+
prefix[1] = (uint8_t)ctxlen;
|
|
252
|
+
|
|
253
|
+
shake256_inc_absorb(state, tr, TRBYTES);
|
|
254
|
+
shake256_inc_absorb(state, prefix, sizeof(prefix));
|
|
255
|
+
if (ctxlen > 0) {
|
|
256
|
+
shake256_inc_absorb(state, ctx, ctxlen);
|
|
257
|
+
}
|
|
258
|
+
return PQ_SUCCESS;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
int pq_mu_builder_absorb(void *state_ptr, const uint8_t *chunk, size_t chunk_len) {
|
|
262
|
+
if (state_ptr == NULL) {
|
|
263
|
+
return PQ_ERROR_BUFFER;
|
|
264
|
+
}
|
|
265
|
+
if (chunk_len == 0) {
|
|
266
|
+
return PQ_SUCCESS;
|
|
267
|
+
}
|
|
268
|
+
if (chunk == NULL) {
|
|
269
|
+
return PQ_ERROR_BUFFER;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
shake256incctx *state = (shake256incctx *)state_ptr;
|
|
273
|
+
shake256_inc_absorb(state, chunk, chunk_len);
|
|
274
|
+
return PQ_SUCCESS;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
int pq_mu_builder_finalize(void *state_ptr, uint8_t *mu_out) {
|
|
278
|
+
if (state_ptr == NULL || mu_out == NULL) {
|
|
279
|
+
return PQ_ERROR_BUFFER;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
shake256incctx *state = (shake256incctx *)state_ptr;
|
|
283
|
+
shake256_inc_finalize(state);
|
|
284
|
+
shake256_inc_squeeze(mu_out, CRHBYTES, state);
|
|
285
|
+
shake256_inc_ctx_release(state);
|
|
286
|
+
free(state);
|
|
287
|
+
return PQ_SUCCESS;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
void pq_mu_builder_release(void *state_ptr) {
|
|
291
|
+
if (state_ptr == NULL) {
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
shake256incctx *state = (shake256incctx *)state_ptr;
|
|
295
|
+
shake256_inc_ctx_release(state);
|
|
296
|
+
free(state);
|
|
297
|
+
}
|
|
@@ -223,6 +223,10 @@ static void pq_wipe_and_free(uint8_t *buffer, size_t len) {
|
|
|
223
223
|
}
|
|
224
224
|
}
|
|
225
225
|
|
|
226
|
+
static void pq_free_buffer(uint8_t *buffer) {
|
|
227
|
+
free(buffer);
|
|
228
|
+
}
|
|
229
|
+
|
|
226
230
|
static void pq_validate_bytes_argument(VALUE value, size_t expected_len, const char *what) {
|
|
227
231
|
StringValue(value);
|
|
228
232
|
if ((size_t)RSTRING_LEN(value) != expected_len) {
|
|
@@ -628,17 +632,17 @@ static VALUE pqcrypto__test_sign_from_seed(VALUE self, VALUE message, VALUE secr
|
|
|
628
632
|
|
|
629
633
|
rb_thread_call_without_gvl(pq_testing_sign_nogvl, &call, NULL, NULL);
|
|
630
634
|
|
|
631
|
-
|
|
635
|
+
pq_free_buffer(call.message);
|
|
632
636
|
pq_wipe_and_free((uint8_t *)call.secret_key, secret_key_len);
|
|
633
637
|
pq_wipe_and_free((uint8_t *)call.seed, call.seed_len);
|
|
634
638
|
|
|
635
639
|
if (call.result != PQ_SUCCESS) {
|
|
636
|
-
|
|
640
|
+
pq_free_buffer(call.signature);
|
|
637
641
|
pq_raise_general_error(call.result);
|
|
638
642
|
}
|
|
639
643
|
|
|
640
644
|
VALUE result = pq_string_from_buffer(call.signature, call.signature_len);
|
|
641
|
-
|
|
645
|
+
pq_free_buffer(call.signature);
|
|
642
646
|
return result;
|
|
643
647
|
}
|
|
644
648
|
|
|
@@ -661,16 +665,16 @@ static VALUE pqcrypto_sign(VALUE self, VALUE message, VALUE secret_key) {
|
|
|
661
665
|
|
|
662
666
|
rb_nogvl(pq_sign_nogvl, &call, NULL, NULL, RB_NOGVL_OFFLOAD_SAFE);
|
|
663
667
|
|
|
664
|
-
|
|
668
|
+
pq_free_buffer(call.message);
|
|
665
669
|
pq_wipe_and_free((uint8_t *)call.secret_key, secret_key_len);
|
|
666
670
|
|
|
667
671
|
if (call.result != PQ_SUCCESS) {
|
|
668
|
-
|
|
672
|
+
pq_free_buffer(call.signature);
|
|
669
673
|
pq_raise_general_error(call.result);
|
|
670
674
|
}
|
|
671
675
|
|
|
672
676
|
VALUE result = pq_string_from_buffer(call.signature, call.signature_len);
|
|
673
|
-
|
|
677
|
+
pq_free_buffer(call.signature);
|
|
674
678
|
return result;
|
|
675
679
|
}
|
|
676
680
|
|
|
@@ -689,9 +693,9 @@ static VALUE pqcrypto_verify(VALUE self, VALUE message, VALUE signature, VALUE p
|
|
|
689
693
|
|
|
690
694
|
rb_nogvl(pq_verify_nogvl, &call, NULL, NULL, RB_NOGVL_OFFLOAD_SAFE);
|
|
691
695
|
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
696
|
+
pq_free_buffer(call.message);
|
|
697
|
+
pq_free_buffer((uint8_t *)call.public_key);
|
|
698
|
+
pq_free_buffer((uint8_t *)call.signature);
|
|
695
699
|
|
|
696
700
|
if (call.result == PQ_SUCCESS) {
|
|
697
701
|
return Qtrue;
|
|
@@ -731,6 +735,282 @@ static VALUE pqcrypto_version(VALUE self) {
|
|
|
731
735
|
return rb_str_new_cstr(pq_version());
|
|
732
736
|
}
|
|
733
737
|
|
|
738
|
+
typedef struct {
|
|
739
|
+
void *builder;
|
|
740
|
+
} mu_builder_wrapper_t;
|
|
741
|
+
|
|
742
|
+
typedef struct {
|
|
743
|
+
int result;
|
|
744
|
+
void *builder;
|
|
745
|
+
const uint8_t *chunk;
|
|
746
|
+
size_t chunk_len;
|
|
747
|
+
} mu_absorb_call_t;
|
|
748
|
+
|
|
749
|
+
typedef struct {
|
|
750
|
+
int result;
|
|
751
|
+
void *builder;
|
|
752
|
+
uint8_t *mu_out;
|
|
753
|
+
} mu_finalize_call_t;
|
|
754
|
+
|
|
755
|
+
typedef struct {
|
|
756
|
+
int result;
|
|
757
|
+
uint8_t *signature;
|
|
758
|
+
size_t signature_len;
|
|
759
|
+
const uint8_t *mu;
|
|
760
|
+
const uint8_t *secret_key;
|
|
761
|
+
} sign_mu_call_t;
|
|
762
|
+
|
|
763
|
+
typedef struct {
|
|
764
|
+
int result;
|
|
765
|
+
const uint8_t *signature;
|
|
766
|
+
size_t signature_len;
|
|
767
|
+
const uint8_t *mu;
|
|
768
|
+
const uint8_t *public_key;
|
|
769
|
+
} verify_mu_call_t;
|
|
770
|
+
|
|
771
|
+
static void mu_builder_wrapper_free(void *ptr) {
|
|
772
|
+
mu_builder_wrapper_t *wrapper = (mu_builder_wrapper_t *)ptr;
|
|
773
|
+
if (wrapper == NULL) {
|
|
774
|
+
return;
|
|
775
|
+
}
|
|
776
|
+
if (wrapper->builder != NULL) {
|
|
777
|
+
pq_mu_builder_release(wrapper->builder);
|
|
778
|
+
wrapper->builder = NULL;
|
|
779
|
+
}
|
|
780
|
+
xfree(wrapper);
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
static size_t mu_builder_wrapper_size(const void *ptr) {
|
|
784
|
+
(void)ptr;
|
|
785
|
+
return sizeof(mu_builder_wrapper_t);
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
static const rb_data_type_t mu_builder_data_type = {
|
|
789
|
+
"PQCrypto::MLDSA::MuBuilder",
|
|
790
|
+
{NULL, mu_builder_wrapper_free, mu_builder_wrapper_size},
|
|
791
|
+
NULL,
|
|
792
|
+
NULL,
|
|
793
|
+
RUBY_TYPED_FREE_IMMEDIATELY};
|
|
794
|
+
|
|
795
|
+
static mu_builder_wrapper_t *mu_builder_unwrap(VALUE obj) {
|
|
796
|
+
mu_builder_wrapper_t *wrapper;
|
|
797
|
+
TypedData_Get_Struct(obj, mu_builder_wrapper_t, &mu_builder_data_type, wrapper);
|
|
798
|
+
if (wrapper == NULL || wrapper->builder == NULL) {
|
|
799
|
+
rb_raise(ePQCryptoError, "mu builder used after release");
|
|
800
|
+
}
|
|
801
|
+
return wrapper;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
static VALUE pqcrypto__native_mldsa_extract_tr(VALUE self, VALUE secret_key) {
|
|
805
|
+
(void)self;
|
|
806
|
+
pq_validate_bytes_argument(secret_key, PQ_MLDSA_SECRETKEYBYTES, "secret key");
|
|
807
|
+
|
|
808
|
+
uint8_t tr[PQ_MLDSA_TRBYTES];
|
|
809
|
+
int rc = pq_mldsa_extract_tr_from_secret_key(tr, (const uint8_t *)RSTRING_PTR(secret_key));
|
|
810
|
+
if (rc != PQ_SUCCESS) {
|
|
811
|
+
pq_secure_wipe(tr, sizeof(tr));
|
|
812
|
+
pq_raise_general_error(rc);
|
|
813
|
+
}
|
|
814
|
+
VALUE result = pq_string_from_buffer(tr, sizeof(tr));
|
|
815
|
+
pq_secure_wipe(tr, sizeof(tr));
|
|
816
|
+
return result;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
static VALUE pqcrypto__native_mldsa_compute_tr(VALUE self, VALUE public_key) {
|
|
820
|
+
(void)self;
|
|
821
|
+
pq_validate_bytes_argument(public_key, PQ_MLDSA_PUBLICKEYBYTES, "public key");
|
|
822
|
+
|
|
823
|
+
uint8_t tr[PQ_MLDSA_TRBYTES];
|
|
824
|
+
int rc = pq_mldsa_compute_tr_from_public_key(tr, (const uint8_t *)RSTRING_PTR(public_key));
|
|
825
|
+
if (rc != PQ_SUCCESS) {
|
|
826
|
+
pq_raise_general_error(rc);
|
|
827
|
+
}
|
|
828
|
+
return pq_string_from_buffer(tr, sizeof(tr));
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
static VALUE pqcrypto__native_mldsa_mu_builder_new(VALUE self, VALUE tr, VALUE ctx) {
|
|
832
|
+
(void)self;
|
|
833
|
+
pq_validate_bytes_argument(tr, PQ_MLDSA_TRBYTES, "tr");
|
|
834
|
+
StringValue(ctx);
|
|
835
|
+
|
|
836
|
+
size_t ctxlen = (size_t)RSTRING_LEN(ctx);
|
|
837
|
+
if (ctxlen > 255) {
|
|
838
|
+
rb_raise(rb_eArgError, "ML-DSA context length must be <= 255 bytes");
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
void *builder = pq_mu_builder_new();
|
|
842
|
+
if (builder == NULL) {
|
|
843
|
+
rb_raise(rb_eNoMemError, "Memory allocation failed (mu builder)");
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
int rc = pq_mu_builder_init(builder, (const uint8_t *)RSTRING_PTR(tr),
|
|
847
|
+
(const uint8_t *)RSTRING_PTR(ctx), ctxlen);
|
|
848
|
+
if (rc != PQ_SUCCESS) {
|
|
849
|
+
pq_mu_builder_release(builder);
|
|
850
|
+
pq_raise_general_error(rc);
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
mu_builder_wrapper_t *wrapper;
|
|
854
|
+
VALUE obj =
|
|
855
|
+
TypedData_Make_Struct(rb_cObject, mu_builder_wrapper_t, &mu_builder_data_type, wrapper);
|
|
856
|
+
wrapper->builder = builder;
|
|
857
|
+
return obj;
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
static void *pq_mu_absorb_nogvl(void *arg) {
|
|
861
|
+
mu_absorb_call_t *call = (mu_absorb_call_t *)arg;
|
|
862
|
+
call->result = pq_mu_builder_absorb(call->builder, call->chunk, call->chunk_len);
|
|
863
|
+
return NULL;
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
static VALUE pqcrypto__native_mldsa_mu_builder_update(VALUE self, VALUE builder_obj, VALUE chunk) {
|
|
867
|
+
(void)self;
|
|
868
|
+
mu_builder_wrapper_t *wrapper = mu_builder_unwrap(builder_obj);
|
|
869
|
+
StringValue(chunk);
|
|
870
|
+
|
|
871
|
+
size_t chunk_len = (size_t)RSTRING_LEN(chunk);
|
|
872
|
+
if (chunk_len == 0) {
|
|
873
|
+
return Qnil;
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
uint8_t *copy = pq_alloc_buffer(chunk_len);
|
|
877
|
+
memcpy(copy, RSTRING_PTR(chunk), chunk_len);
|
|
878
|
+
|
|
879
|
+
mu_absorb_call_t call = {0};
|
|
880
|
+
call.builder = wrapper->builder;
|
|
881
|
+
call.chunk = copy;
|
|
882
|
+
call.chunk_len = chunk_len;
|
|
883
|
+
|
|
884
|
+
rb_nogvl(pq_mu_absorb_nogvl, &call, NULL, NULL, RB_NOGVL_OFFLOAD_SAFE);
|
|
885
|
+
free(copy);
|
|
886
|
+
|
|
887
|
+
if (call.result != PQ_SUCCESS) {
|
|
888
|
+
pq_raise_general_error(call.result);
|
|
889
|
+
}
|
|
890
|
+
return Qnil;
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
static void *pq_mu_finalize_nogvl(void *arg) {
|
|
894
|
+
mu_finalize_call_t *call = (mu_finalize_call_t *)arg;
|
|
895
|
+
call->result = pq_mu_builder_finalize(call->builder, call->mu_out);
|
|
896
|
+
return NULL;
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
static VALUE pqcrypto__native_mldsa_mu_builder_finalize(VALUE self, VALUE builder_obj) {
|
|
900
|
+
(void)self;
|
|
901
|
+
mu_builder_wrapper_t *wrapper = mu_builder_unwrap(builder_obj);
|
|
902
|
+
|
|
903
|
+
uint8_t mu[PQ_MLDSA_MUBYTES];
|
|
904
|
+
|
|
905
|
+
mu_finalize_call_t call = {0};
|
|
906
|
+
call.builder = wrapper->builder;
|
|
907
|
+
call.mu_out = mu;
|
|
908
|
+
|
|
909
|
+
rb_nogvl(pq_mu_finalize_nogvl, &call, NULL, NULL, RB_NOGVL_OFFLOAD_SAFE);
|
|
910
|
+
|
|
911
|
+
if (call.result != PQ_SUCCESS) {
|
|
912
|
+
pq_mu_builder_release(wrapper->builder);
|
|
913
|
+
}
|
|
914
|
+
wrapper->builder = NULL;
|
|
915
|
+
|
|
916
|
+
if (call.result != PQ_SUCCESS) {
|
|
917
|
+
pq_secure_wipe(mu, sizeof(mu));
|
|
918
|
+
pq_raise_general_error(call.result);
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
VALUE result = pq_string_from_buffer(mu, sizeof(mu));
|
|
922
|
+
pq_secure_wipe(mu, sizeof(mu));
|
|
923
|
+
return result;
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
static VALUE pqcrypto__native_mldsa_mu_builder_release(VALUE self, VALUE builder_obj) {
|
|
927
|
+
(void)self;
|
|
928
|
+
mu_builder_wrapper_t *wrapper;
|
|
929
|
+
TypedData_Get_Struct(builder_obj, mu_builder_wrapper_t, &mu_builder_data_type, wrapper);
|
|
930
|
+
if (wrapper != NULL && wrapper->builder != NULL) {
|
|
931
|
+
pq_mu_builder_release(wrapper->builder);
|
|
932
|
+
wrapper->builder = NULL;
|
|
933
|
+
}
|
|
934
|
+
return Qnil;
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
static void *pq_sign_mu_nogvl(void *arg) {
|
|
938
|
+
sign_mu_call_t *call = (sign_mu_call_t *)arg;
|
|
939
|
+
call->result = pq_sign_mu(call->signature, &call->signature_len, call->mu, call->secret_key);
|
|
940
|
+
return NULL;
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
static VALUE pqcrypto__native_mldsa_sign_mu(VALUE self, VALUE mu, VALUE secret_key) {
|
|
944
|
+
(void)self;
|
|
945
|
+
pq_validate_bytes_argument(mu, PQ_MLDSA_MUBYTES, "mu");
|
|
946
|
+
pq_validate_bytes_argument(secret_key, PQ_MLDSA_SECRETKEYBYTES, "secret key");
|
|
947
|
+
|
|
948
|
+
sign_mu_call_t call = {0};
|
|
949
|
+
size_t secret_key_len = 0;
|
|
950
|
+
size_t mu_len = 0;
|
|
951
|
+
uint8_t *mu_copy = pq_copy_ruby_string(mu, &mu_len);
|
|
952
|
+
uint8_t *sk_copy = pq_copy_ruby_string(secret_key, &secret_key_len);
|
|
953
|
+
|
|
954
|
+
call.mu = mu_copy;
|
|
955
|
+
call.secret_key = sk_copy;
|
|
956
|
+
call.signature_len = PQ_MLDSA_BYTES;
|
|
957
|
+
call.signature = pq_alloc_buffer(PQ_MLDSA_BYTES);
|
|
958
|
+
|
|
959
|
+
rb_nogvl(pq_sign_mu_nogvl, &call, NULL, NULL, RB_NOGVL_OFFLOAD_SAFE);
|
|
960
|
+
|
|
961
|
+
pq_wipe_and_free(mu_copy, mu_len);
|
|
962
|
+
pq_wipe_and_free(sk_copy, secret_key_len);
|
|
963
|
+
|
|
964
|
+
if (call.result != PQ_SUCCESS) {
|
|
965
|
+
pq_free_buffer(call.signature);
|
|
966
|
+
pq_raise_general_error(call.result);
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
VALUE result = pq_string_from_buffer(call.signature, call.signature_len);
|
|
970
|
+
pq_free_buffer(call.signature);
|
|
971
|
+
return result;
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
static void *pq_verify_mu_nogvl(void *arg) {
|
|
975
|
+
verify_mu_call_t *call = (verify_mu_call_t *)arg;
|
|
976
|
+
call->result = pq_verify_mu(call->signature, call->signature_len, call->mu, call->public_key);
|
|
977
|
+
return NULL;
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
static VALUE pqcrypto__native_mldsa_verify_mu(VALUE self, VALUE mu, VALUE signature,
|
|
981
|
+
VALUE public_key) {
|
|
982
|
+
(void)self;
|
|
983
|
+
StringValue(signature);
|
|
984
|
+
pq_validate_bytes_argument(mu, PQ_MLDSA_MUBYTES, "mu");
|
|
985
|
+
pq_validate_bytes_argument(public_key, PQ_MLDSA_PUBLICKEYBYTES, "public key");
|
|
986
|
+
|
|
987
|
+
verify_mu_call_t call = {0};
|
|
988
|
+
size_t public_key_len = 0;
|
|
989
|
+
size_t signature_len = 0;
|
|
990
|
+
size_t mu_len = 0;
|
|
991
|
+
uint8_t *mu_copy = pq_copy_ruby_string(mu, &mu_len);
|
|
992
|
+
uint8_t *pk_copy = pq_copy_ruby_string(public_key, &public_key_len);
|
|
993
|
+
uint8_t *sig_copy = pq_copy_ruby_string(signature, &signature_len);
|
|
994
|
+
|
|
995
|
+
call.mu = mu_copy;
|
|
996
|
+
call.public_key = pk_copy;
|
|
997
|
+
call.signature = sig_copy;
|
|
998
|
+
call.signature_len = signature_len;
|
|
999
|
+
|
|
1000
|
+
rb_nogvl(pq_verify_mu_nogvl, &call, NULL, NULL, RB_NOGVL_OFFLOAD_SAFE);
|
|
1001
|
+
pq_wipe_and_free(mu_copy, mu_len);
|
|
1002
|
+
pq_free_buffer(pk_copy);
|
|
1003
|
+
pq_free_buffer(sig_copy);
|
|
1004
|
+
|
|
1005
|
+
if (call.result == PQ_SUCCESS) {
|
|
1006
|
+
return Qtrue;
|
|
1007
|
+
}
|
|
1008
|
+
if (call.result == PQ_ERROR_VERIFY) {
|
|
1009
|
+
return Qfalse;
|
|
1010
|
+
}
|
|
1011
|
+
pq_raise_general_error(call.result);
|
|
1012
|
+
}
|
|
1013
|
+
|
|
734
1014
|
static void define_constants(void) {
|
|
735
1015
|
rb_define_const(mPQCrypto, "ML_KEM_PUBLIC_KEY_BYTES", INT2NUM(PQ_MLKEM_PUBLICKEYBYTES));
|
|
736
1016
|
rb_define_const(mPQCrypto, "ML_KEM_SECRET_KEY_BYTES", INT2NUM(PQ_MLKEM_SECRETKEYBYTES));
|
|
@@ -834,6 +1114,22 @@ void Init_pqcrypto_secure(void) {
|
|
|
834
1114
|
pqcrypto_secret_key_from_pqc_container_der, 1);
|
|
835
1115
|
rb_define_module_function(mPQCrypto, "secret_key_from_pqc_container_pem",
|
|
836
1116
|
pqcrypto_secret_key_from_pqc_container_pem, 1);
|
|
1117
|
+
rb_define_module_function(mPQCrypto, "_native_mldsa_extract_tr",
|
|
1118
|
+
pqcrypto__native_mldsa_extract_tr, 1);
|
|
1119
|
+
rb_define_module_function(mPQCrypto, "_native_mldsa_compute_tr",
|
|
1120
|
+
pqcrypto__native_mldsa_compute_tr, 1);
|
|
1121
|
+
rb_define_module_function(mPQCrypto, "_native_mldsa_mu_builder_new",
|
|
1122
|
+
pqcrypto__native_mldsa_mu_builder_new, 2);
|
|
1123
|
+
rb_define_module_function(mPQCrypto, "_native_mldsa_mu_builder_update",
|
|
1124
|
+
pqcrypto__native_mldsa_mu_builder_update, 2);
|
|
1125
|
+
rb_define_module_function(mPQCrypto, "_native_mldsa_mu_builder_finalize",
|
|
1126
|
+
pqcrypto__native_mldsa_mu_builder_finalize, 1);
|
|
1127
|
+
rb_define_module_function(mPQCrypto, "_native_mldsa_mu_builder_release",
|
|
1128
|
+
pqcrypto__native_mldsa_mu_builder_release, 1);
|
|
1129
|
+
rb_define_module_function(mPQCrypto, "_native_mldsa_sign_mu", pqcrypto__native_mldsa_sign_mu,
|
|
1130
|
+
2);
|
|
1131
|
+
rb_define_module_function(mPQCrypto, "_native_mldsa_verify_mu",
|
|
1132
|
+
pqcrypto__native_mldsa_verify_mu, 3);
|
|
837
1133
|
|
|
838
1134
|
define_constants();
|
|
839
1135
|
}
|
|
@@ -144,6 +144,22 @@ const char *pq_version(void);
|
|
|
144
144
|
#define PQ_MLDSA_PUBLICKEYBYTES MLDSA_PUBLICKEYBYTES
|
|
145
145
|
#define PQ_MLDSA_SECRETKEYBYTES MLDSA_SECRETKEYBYTES
|
|
146
146
|
#define PQ_MLDSA_BYTES MLDSA_BYTES
|
|
147
|
+
#define PQ_MLDSA_MUBYTES 64
|
|
148
|
+
#define PQ_MLDSA_TRBYTES 64
|
|
149
|
+
|
|
150
|
+
int pq_mldsa_extract_tr_from_secret_key(uint8_t *tr_out, const uint8_t *secret_key);
|
|
151
|
+
int pq_mldsa_compute_tr_from_public_key(uint8_t *tr_out, const uint8_t *public_key);
|
|
152
|
+
|
|
153
|
+
int pq_sign_mu(uint8_t *signature, size_t *signature_len,
|
|
154
|
+
const uint8_t *mu, const uint8_t *secret_key);
|
|
155
|
+
int pq_verify_mu(const uint8_t *signature, size_t signature_len,
|
|
156
|
+
const uint8_t *mu, const uint8_t *public_key);
|
|
157
|
+
void *pq_mu_builder_new(void);
|
|
158
|
+
int pq_mu_builder_init(void *state, const uint8_t *tr,
|
|
159
|
+
const uint8_t *ctx, size_t ctxlen);
|
|
160
|
+
int pq_mu_builder_absorb(void *state, const uint8_t *chunk, size_t chunk_len);
|
|
161
|
+
int pq_mu_builder_finalize(void *state, uint8_t *mu_out);
|
|
162
|
+
void pq_mu_builder_release(void *state);
|
|
147
163
|
|
|
148
164
|
int pq_hybrid_kem_keypair(uint8_t *public_key, uint8_t *secret_key);
|
|
149
165
|
int pq_hybrid_kem_encapsulate(uint8_t *ciphertext, uint8_t *shared_secret,
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# This Makefile can be used with GNU Make or BSD Make
|
|
2
|
+
|
|
3
|
+
LIB=libml-kem-768_clean.a
|
|
4
|
+
HEADERS=api.h cbd.h indcpa.h kem.h ntt.h params.h poly.h polyvec.h reduce.h symmetric.h verify.h
|
|
5
|
+
OBJECTS=cbd.o indcpa.o kem.o ntt.o poly.o polyvec.o reduce.o symmetric-shake.o verify.o
|
|
6
|
+
|
|
7
|
+
CFLAGS=-O3 -Wall -Wextra -Wpedantic -Werror -Wmissing-prototypes -Wredundant-decls -std=c99 -I../../../common $(EXTRAFLAGS)
|
|
8
|
+
|
|
9
|
+
all: $(LIB)
|
|
10
|
+
|
|
11
|
+
%.o: %.c $(HEADERS)
|
|
12
|
+
$(CC) $(CFLAGS) -c -o $@ $<
|
|
13
|
+
|
|
14
|
+
$(LIB): $(OBJECTS)
|
|
15
|
+
$(AR) -r $@ $(OBJECTS)
|
|
16
|
+
|
|
17
|
+
clean:
|
|
18
|
+
$(RM) $(OBJECTS)
|
|
19
|
+
$(RM) $(LIB)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# This Makefile can be used with GNU Make or BSD Make
|
|
2
|
+
|
|
3
|
+
LIB=libml-dsa-65_clean.a
|
|
4
|
+
HEADERS=api.h ntt.h packing.h params.h poly.h polyvec.h reduce.h rounding.h sign.h symmetric.h
|
|
5
|
+
OBJECTS=ntt.o packing.o poly.o polyvec.o reduce.o rounding.o sign.o symmetric-shake.o
|
|
6
|
+
|
|
7
|
+
CFLAGS=-O3 -Wall -Wextra -Wpedantic -Werror -Wmissing-prototypes -Wredundant-decls -std=c99 -I../../../common $(EXTRAFLAGS)
|
|
8
|
+
|
|
9
|
+
all: $(LIB)
|
|
10
|
+
|
|
11
|
+
%.o: %.c $(HEADERS)
|
|
12
|
+
$(CC) $(CFLAGS) -c -o $@ $<
|
|
13
|
+
|
|
14
|
+
$(LIB): $(OBJECTS)
|
|
15
|
+
$(AR) -r $@ $(OBJECTS)
|
|
16
|
+
|
|
17
|
+
clean:
|
|
18
|
+
$(RM) $(OBJECTS)
|
|
19
|
+
$(RM) $(LIB)
|
data/lib/pq_crypto/signature.rb
CHANGED
|
@@ -74,6 +74,95 @@ module PQCrypto
|
|
|
74
74
|
|
|
75
75
|
raise UnsupportedAlgorithmError, "Unsupported signature algorithm: #{algorithm.inspect}"
|
|
76
76
|
end
|
|
77
|
+
|
|
78
|
+
def _streaming_sign(secret_key, io, chunk_size, context)
|
|
79
|
+
validate_chunk_size!(chunk_size)
|
|
80
|
+
validate_context!(context)
|
|
81
|
+
validate_io!(io)
|
|
82
|
+
|
|
83
|
+
sk_bytes = secret_key.__send__(:bytes_for_native)
|
|
84
|
+
begin
|
|
85
|
+
tr = PQCrypto.__send__(:_native_mldsa_extract_tr, sk_bytes)
|
|
86
|
+
rescue ArgumentError => e
|
|
87
|
+
raise InvalidKeyError, e.message
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
builder = PQCrypto.__send__(:_native_mldsa_mu_builder_new, tr, context.b)
|
|
91
|
+
builder_consumed = false
|
|
92
|
+
mu = nil
|
|
93
|
+
begin
|
|
94
|
+
_drain_io_into_builder(io, builder, chunk_size)
|
|
95
|
+
mu = PQCrypto.__send__(:_native_mldsa_mu_builder_finalize, builder)
|
|
96
|
+
builder_consumed = true
|
|
97
|
+
PQCrypto.__send__(:_native_mldsa_sign_mu, mu, sk_bytes)
|
|
98
|
+
ensure
|
|
99
|
+
PQCrypto.__send__(:_native_mldsa_mu_builder_release, builder) unless builder_consumed
|
|
100
|
+
PQCrypto.secure_wipe(tr) if tr && !tr.frozen?
|
|
101
|
+
PQCrypto.secure_wipe(mu) if mu && !mu.frozen?
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def _streaming_verify(public_key, io, signature, chunk_size, context)
|
|
106
|
+
validate_chunk_size!(chunk_size)
|
|
107
|
+
validate_context!(context)
|
|
108
|
+
validate_io!(io)
|
|
109
|
+
|
|
110
|
+
pk_bytes = public_key.__send__(:bytes_for_native)
|
|
111
|
+
begin
|
|
112
|
+
tr = PQCrypto.__send__(:_native_mldsa_compute_tr, pk_bytes)
|
|
113
|
+
rescue ArgumentError => e
|
|
114
|
+
raise InvalidKeyError, e.message
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
builder = PQCrypto.__send__(:_native_mldsa_mu_builder_new, tr, context.b)
|
|
118
|
+
builder_consumed = false
|
|
119
|
+
mu = nil
|
|
120
|
+
sig_bytes = String(signature).b
|
|
121
|
+
begin
|
|
122
|
+
_drain_io_into_builder(io, builder, chunk_size)
|
|
123
|
+
mu = PQCrypto.__send__(:_native_mldsa_mu_builder_finalize, builder)
|
|
124
|
+
builder_consumed = true
|
|
125
|
+
PQCrypto.__send__(:_native_mldsa_verify_mu, mu, sig_bytes, pk_bytes)
|
|
126
|
+
ensure
|
|
127
|
+
PQCrypto.__send__(:_native_mldsa_mu_builder_release, builder) unless builder_consumed
|
|
128
|
+
|
|
129
|
+
PQCrypto.secure_wipe(tr) if tr && !tr.frozen?
|
|
130
|
+
PQCrypto.secure_wipe(mu) if mu && !mu.frozen?
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def _drain_io_into_builder(io, builder, chunk_size)
|
|
135
|
+
buffer = String.new(capacity: chunk_size).b
|
|
136
|
+
loop do
|
|
137
|
+
result = io.read(chunk_size, buffer)
|
|
138
|
+
break if result.nil?
|
|
139
|
+
|
|
140
|
+
chunk = result.equal?(buffer) ? buffer : result
|
|
141
|
+
chunk_bytes = chunk.encoding == Encoding::BINARY ? chunk : chunk.b
|
|
142
|
+
break if chunk_bytes.bytesize.zero?
|
|
143
|
+
|
|
144
|
+
PQCrypto.__send__(:_native_mldsa_mu_builder_update, builder, chunk_bytes)
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def validate_io!(io)
|
|
149
|
+
unless io.respond_to?(:read)
|
|
150
|
+
raise ArgumentError, "io must respond to #read"
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def validate_chunk_size!(chunk_size)
|
|
155
|
+
unless chunk_size.is_a?(Integer) && chunk_size > 0
|
|
156
|
+
raise ArgumentError, "chunk_size must be a positive Integer"
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def validate_context!(context)
|
|
161
|
+
ctx = String(context).b
|
|
162
|
+
if ctx.bytesize > 255
|
|
163
|
+
raise ArgumentError, "context must be at most 255 bytes (FIPS 204)"
|
|
164
|
+
end
|
|
165
|
+
end
|
|
77
166
|
end
|
|
78
167
|
|
|
79
168
|
class Keypair
|
|
@@ -125,6 +214,17 @@ module PQCrypto
|
|
|
125
214
|
true
|
|
126
215
|
end
|
|
127
216
|
|
|
217
|
+
def verify_io(io, signature, chunk_size: 1 << 20, context: "".b)
|
|
218
|
+
Signature.send(:_streaming_verify, self, io, signature, chunk_size, context)
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def verify_io!(io, signature, chunk_size: 1 << 20, context: "".b)
|
|
222
|
+
unless verify_io(io, signature, chunk_size: chunk_size, context: context)
|
|
223
|
+
raise PQCrypto::VerificationError, "Verification failed"
|
|
224
|
+
end
|
|
225
|
+
true
|
|
226
|
+
end
|
|
227
|
+
|
|
128
228
|
def ==(other)
|
|
129
229
|
return false unless other.is_a?(PublicKey) && other.algorithm == algorithm
|
|
130
230
|
PQCrypto.__send__(:native_ct_equals, other.to_bytes, @bytes)
|
|
@@ -142,6 +242,10 @@ module PQCrypto
|
|
|
142
242
|
|
|
143
243
|
private
|
|
144
244
|
|
|
245
|
+
def bytes_for_native
|
|
246
|
+
@bytes
|
|
247
|
+
end
|
|
248
|
+
|
|
145
249
|
def validate_length!
|
|
146
250
|
expected = Signature.details(@algorithm).fetch(:public_key_bytes)
|
|
147
251
|
raise InvalidKeyError, "Invalid signature public key length" unless @bytes.bytesize == expected
|
|
@@ -175,6 +279,10 @@ module PQCrypto
|
|
|
175
279
|
raise InvalidKeyError, e.message
|
|
176
280
|
end
|
|
177
281
|
|
|
282
|
+
def sign_io(io, chunk_size: 1 << 20, context: "".b)
|
|
283
|
+
Signature.send(:_streaming_sign, self, io, chunk_size, context)
|
|
284
|
+
end
|
|
285
|
+
|
|
178
286
|
def wipe!
|
|
179
287
|
PQCrypto.secure_wipe(@bytes)
|
|
180
288
|
self
|
|
@@ -197,6 +305,10 @@ module PQCrypto
|
|
|
197
305
|
|
|
198
306
|
private
|
|
199
307
|
|
|
308
|
+
def bytes_for_native
|
|
309
|
+
@bytes
|
|
310
|
+
end
|
|
311
|
+
|
|
200
312
|
def validate_length!
|
|
201
313
|
expected = Signature.details(@algorithm).fetch(:secret_key_bytes)
|
|
202
314
|
raise InvalidKeyError, "Invalid signature secret key length" unless @bytes.bytesize == expected
|
data/lib/pq_crypto/version.rb
CHANGED
data/lib/pq_crypto.rb
CHANGED
|
@@ -69,6 +69,17 @@ module PQCrypto
|
|
|
69
69
|
__test_sign_from_seed
|
|
70
70
|
].freeze
|
|
71
71
|
|
|
72
|
+
EXTERNAL_MU_METHODS = %i[
|
|
73
|
+
_native_mldsa_extract_tr
|
|
74
|
+
_native_mldsa_compute_tr
|
|
75
|
+
_native_mldsa_mu_builder_new
|
|
76
|
+
_native_mldsa_mu_builder_update
|
|
77
|
+
_native_mldsa_mu_builder_finalize
|
|
78
|
+
_native_mldsa_mu_builder_release
|
|
79
|
+
_native_mldsa_sign_mu
|
|
80
|
+
_native_mldsa_verify_mu
|
|
81
|
+
].freeze
|
|
82
|
+
|
|
72
83
|
class << PQCrypto
|
|
73
84
|
NativeBindings::NATIVE_METHODS.each do |name|
|
|
74
85
|
alias_name = :"native_#{name.to_s.sub(/\A__/, '')}"
|
|
@@ -78,6 +89,7 @@ module PQCrypto
|
|
|
78
89
|
|
|
79
90
|
private(*NativeBindings::NATIVE_METHODS)
|
|
80
91
|
private(*NativeBindings::NATIVE_METHODS.map { |n| :"native_#{n.to_s.sub(/\A__/, '')}" })
|
|
92
|
+
private(*NativeBindings::EXTERNAL_MU_METHODS)
|
|
81
93
|
end
|
|
82
94
|
end
|
|
83
95
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: pq_crypto
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Roman Haydarov
|
|
@@ -69,6 +69,7 @@ files:
|
|
|
69
69
|
- ext/pqcrypto/extconf.rb
|
|
70
70
|
- ext/pqcrypto/mldsa_api.h
|
|
71
71
|
- ext/pqcrypto/mlkem_api.h
|
|
72
|
+
- ext/pqcrypto/pq_externalmu.c
|
|
72
73
|
- ext/pqcrypto/pq_randombytes.c
|
|
73
74
|
- ext/pqcrypto/pqcrypto_ruby_secure.c
|
|
74
75
|
- ext/pqcrypto/pqcrypto_secure.c
|
|
@@ -86,6 +87,7 @@ files:
|
|
|
86
87
|
- ext/pqcrypto/vendor/pqclean/common/keccak4x/KeccakP-1600-times4-SIMD256.c
|
|
87
88
|
- ext/pqcrypto/vendor/pqclean/common/keccak4x/KeccakP-1600-times4-SnP.h
|
|
88
89
|
- ext/pqcrypto/vendor/pqclean/common/keccak4x/KeccakP-1600-unrolling.macros
|
|
90
|
+
- ext/pqcrypto/vendor/pqclean/common/keccak4x/Makefile
|
|
89
91
|
- ext/pqcrypto/vendor/pqclean/common/keccak4x/Makefile.Microsoft_nmake
|
|
90
92
|
- ext/pqcrypto/vendor/pqclean/common/keccak4x/SIMD256-config.h
|
|
91
93
|
- ext/pqcrypto/vendor/pqclean/common/keccak4x/align.h
|
|
@@ -99,6 +101,7 @@ files:
|
|
|
99
101
|
- ext/pqcrypto/vendor/pqclean/common/sp800-185.c
|
|
100
102
|
- ext/pqcrypto/vendor/pqclean/common/sp800-185.h
|
|
101
103
|
- ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/LICENSE
|
|
104
|
+
- ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/Makefile
|
|
102
105
|
- ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/Makefile.Microsoft_nmake
|
|
103
106
|
- ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/api.h
|
|
104
107
|
- ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/cbd.c
|
|
@@ -121,6 +124,7 @@ files:
|
|
|
121
124
|
- ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/verify.c
|
|
122
125
|
- ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/verify.h
|
|
123
126
|
- ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-65/clean/LICENSE
|
|
127
|
+
- ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-65/clean/Makefile
|
|
124
128
|
- ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-65/clean/Makefile.Microsoft_nmake
|
|
125
129
|
- ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-65/clean/api.h
|
|
126
130
|
- ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-65/clean/ntt.c
|