pq_crypto 0.3.1 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +56 -0
- data/CHANGELOG.md +50 -0
- data/GET_STARTED.md +374 -30
- data/README.md +59 -195
- data/SECURITY.md +101 -82
- data/ext/pqcrypto/extconf.rb +85 -9
- data/ext/pqcrypto/mldsa_api.h +71 -1
- data/ext/pqcrypto/mlkem_api.h +24 -0
- data/ext/pqcrypto/pq_externalmu.c +310 -0
- data/ext/pqcrypto/pqcrypto_ruby_secure.c +784 -85
- data/ext/pqcrypto/pqcrypto_secure.c +179 -72
- data/ext/pqcrypto/pqcrypto_secure.h +103 -7
- data/ext/pqcrypto/pqcrypto_version.h +7 -0
- data/ext/pqcrypto/vendor/.vendored +1 -1
- data/ext/pqcrypto/vendor/pqclean/common/keccak4x/Makefile +8 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-1024/clean/LICENSE +5 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-1024/clean/Makefile +19 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-1024/clean/Makefile.Microsoft_nmake +23 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-1024/clean/api.h +18 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-1024/clean/cbd.c +83 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-1024/clean/cbd.h +11 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-1024/clean/indcpa.c +327 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-1024/clean/indcpa.h +22 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-1024/clean/kem.c +164 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-1024/clean/kem.h +23 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-1024/clean/ntt.c +146 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-1024/clean/ntt.h +14 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-1024/clean/params.h +36 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-1024/clean/poly.c +311 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-1024/clean/poly.h +37 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-1024/clean/polyvec.c +198 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-1024/clean/polyvec.h +26 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-1024/clean/reduce.c +41 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-1024/clean/reduce.h +13 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-1024/clean/symmetric-shake.c +71 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-1024/clean/symmetric.h +30 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-1024/clean/verify.c +67 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-1024/clean/verify.h +13 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-512/clean/LICENSE +5 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-512/clean/Makefile +19 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-512/clean/Makefile.Microsoft_nmake +23 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-512/clean/api.h +18 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-512/clean/cbd.c +108 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-512/clean/cbd.h +11 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-512/clean/indcpa.c +327 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-512/clean/indcpa.h +22 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-512/clean/kem.c +164 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-512/clean/kem.h +23 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-512/clean/ntt.c +146 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-512/clean/ntt.h +14 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-512/clean/params.h +36 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-512/clean/poly.c +299 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-512/clean/poly.h +37 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-512/clean/polyvec.c +188 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-512/clean/polyvec.h +26 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-512/clean/reduce.c +41 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-512/clean/reduce.h +13 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-512/clean/symmetric-shake.c +71 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-512/clean/symmetric.h +30 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-512/clean/verify.c +67 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-512/clean/verify.h +13 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/Makefile +19 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-44/clean/LICENSE +5 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-44/clean/Makefile +19 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-44/clean/Makefile.Microsoft_nmake +23 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-44/clean/api.h +50 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-44/clean/ntt.c +98 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-44/clean/ntt.h +10 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-44/clean/packing.c +261 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-44/clean/packing.h +31 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-44/clean/params.h +44 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-44/clean/poly.c +848 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-44/clean/poly.h +52 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-44/clean/polyvec.c +415 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-44/clean/polyvec.h +65 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-44/clean/reduce.c +69 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-44/clean/reduce.h +17 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-44/clean/rounding.c +98 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-44/clean/rounding.h +14 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-44/clean/sign.c +407 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-44/clean/sign.h +47 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-44/clean/symmetric-shake.c +26 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-44/clean/symmetric.h +34 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-65/clean/Makefile +19 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-87/clean/LICENSE +5 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-87/clean/Makefile +19 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-87/clean/Makefile.Microsoft_nmake +23 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-87/clean/api.h +50 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-87/clean/ntt.c +98 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-87/clean/ntt.h +10 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-87/clean/packing.c +261 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-87/clean/packing.h +31 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-87/clean/params.h +44 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-87/clean/poly.c +823 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-87/clean/poly.h +52 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-87/clean/polyvec.c +415 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-87/clean/polyvec.h +65 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-87/clean/reduce.c +69 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-87/clean/reduce.h +17 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-87/clean/rounding.c +92 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-87/clean/rounding.h +14 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-87/clean/sign.c +407 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-87/clean/sign.h +47 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-87/clean/symmetric-shake.c +26 -0
- data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-87/clean/symmetric.h +34 -0
- data/lib/pq_crypto/algorithm_registry.rb +200 -0
- data/lib/pq_crypto/hybrid_kem.rb +1 -12
- data/lib/pq_crypto/kem.rb +104 -13
- data/lib/pq_crypto/pkcs8.rb +387 -0
- data/lib/pq_crypto/serialization.rb +1 -14
- data/lib/pq_crypto/signature.rb +231 -13
- data/lib/pq_crypto/spki.rb +131 -0
- data/lib/pq_crypto/version.rb +1 -1
- data/lib/pq_crypto.rb +90 -19
- data/script/vendor_libs.rb +4 -0
- metadata +99 -3
data/lib/pq_crypto/signature.rb
CHANGED
|
@@ -6,22 +6,33 @@ module PQCrypto
|
|
|
6
6
|
module Signature
|
|
7
7
|
CANONICAL_ALGORITHM = :ml_dsa_65
|
|
8
8
|
|
|
9
|
-
DETAILS =
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
9
|
+
DETAILS = AlgorithmRegistry.details_for_family(:ml_dsa).freeze
|
|
10
|
+
|
|
11
|
+
NATIVE_DISPATCH = {
|
|
12
|
+
ml_dsa_44: {
|
|
13
|
+
keypair: :native_ml_dsa_44_keypair,
|
|
14
|
+
sign: :native_ml_dsa_44_sign,
|
|
15
|
+
verify: :native_ml_dsa_44_verify,
|
|
16
|
+
keypair_from_seed: :native_ml_dsa_44_keypair_from_seed,
|
|
17
|
+
}.freeze,
|
|
18
|
+
ml_dsa_65: {
|
|
19
|
+
keypair: :native_sign_keypair,
|
|
20
|
+
sign: :native_sign,
|
|
21
|
+
verify: :native_verify,
|
|
22
|
+
keypair_from_seed: :native_ml_dsa_keypair_from_seed,
|
|
23
|
+
}.freeze,
|
|
24
|
+
ml_dsa_87: {
|
|
25
|
+
keypair: :native_ml_dsa_87_keypair,
|
|
26
|
+
sign: :native_ml_dsa_87_sign,
|
|
27
|
+
verify: :native_ml_dsa_87_verify,
|
|
28
|
+
keypair_from_seed: :native_ml_dsa_87_keypair_from_seed,
|
|
18
29
|
}.freeze,
|
|
19
30
|
}.freeze
|
|
20
31
|
|
|
21
32
|
class << self
|
|
22
33
|
def generate(algorithm = CANONICAL_ALGORITHM)
|
|
23
|
-
resolve_algorithm!(algorithm)
|
|
24
|
-
public_key, secret_key = PQCrypto.__send__(:
|
|
34
|
+
algorithm = resolve_algorithm!(algorithm)
|
|
35
|
+
public_key, secret_key = PQCrypto.__send__(native_method_for(algorithm, :keypair))
|
|
25
36
|
Keypair.new(PublicKey.new(algorithm, public_key), SecretKey.new(algorithm, secret_key))
|
|
26
37
|
end
|
|
27
38
|
|
|
@@ -59,6 +70,26 @@ module PQCrypto
|
|
|
59
70
|
SecretKey.new(resolved_algorithm, bytes)
|
|
60
71
|
end
|
|
61
72
|
|
|
73
|
+
def public_key_from_spki_der(der, algorithm: nil)
|
|
74
|
+
resolved_algorithm, bytes = SPKI.decode_der(der)
|
|
75
|
+
validate_algorithm_match!(algorithm, resolved_algorithm) if algorithm
|
|
76
|
+
PublicKey.new(resolve_algorithm!(resolved_algorithm), bytes)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def public_key_from_spki_pem(pem, algorithm: nil)
|
|
80
|
+
resolved_algorithm, bytes = SPKI.decode_pem(pem)
|
|
81
|
+
validate_algorithm_match!(algorithm, resolved_algorithm) if algorithm
|
|
82
|
+
PublicKey.new(resolve_algorithm!(resolved_algorithm), bytes)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def secret_key_from_pkcs8_der(der)
|
|
86
|
+
secret_key_from_decoded_pkcs8(*PKCS8.decode_der(der))
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def secret_key_from_pkcs8_pem(pem)
|
|
90
|
+
secret_key_from_decoded_pkcs8(*PKCS8.decode_pem(pem))
|
|
91
|
+
end
|
|
92
|
+
|
|
62
93
|
def details(algorithm)
|
|
63
94
|
DETAILS.fetch(resolve_algorithm!(algorithm)).dup
|
|
64
95
|
end
|
|
@@ -74,6 +105,138 @@ module PQCrypto
|
|
|
74
105
|
|
|
75
106
|
raise UnsupportedAlgorithmError, "Unsupported signature algorithm: #{algorithm.inspect}"
|
|
76
107
|
end
|
|
108
|
+
|
|
109
|
+
def secret_key_from_decoded_pkcs8(algorithm, format, material)
|
|
110
|
+
algorithm = resolve_algorithm!(algorithm)
|
|
111
|
+
|
|
112
|
+
case format
|
|
113
|
+
when :expanded
|
|
114
|
+
SecretKey.new(algorithm, material)
|
|
115
|
+
when :seed
|
|
116
|
+
_public_key, expanded = PQCrypto.__send__(native_method_for(algorithm, :keypair_from_seed), material)
|
|
117
|
+
SecretKey.new(algorithm, expanded)
|
|
118
|
+
when :both
|
|
119
|
+
_seed, expanded = material
|
|
120
|
+
SecretKey.new(algorithm, expanded)
|
|
121
|
+
else
|
|
122
|
+
raise SerializationError, "Unsupported ML-DSA PKCS#8 private key format: #{format.inspect}"
|
|
123
|
+
end
|
|
124
|
+
rescue ArgumentError => e
|
|
125
|
+
raise InvalidKeyError, e.message
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def native_method_for(algorithm, operation)
|
|
129
|
+
NATIVE_DISPATCH.fetch(resolve_algorithm!(algorithm)).fetch(operation)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def validate_algorithm_match!(expected_algorithm, actual_algorithm)
|
|
133
|
+
expected = resolve_algorithm!(expected_algorithm)
|
|
134
|
+
return if expected == actual_algorithm
|
|
135
|
+
|
|
136
|
+
raise SerializationError,
|
|
137
|
+
"Expected #{expected.inspect}, got #{actual_algorithm.inspect} (SPKI key algorithm mismatch)"
|
|
138
|
+
rescue UnsupportedAlgorithmError => e
|
|
139
|
+
raise SerializationError, e.message
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def _streaming_sign(secret_key, io, chunk_size, context)
|
|
143
|
+
validate_streaming_algorithm!(secret_key.algorithm)
|
|
144
|
+
validate_chunk_size!(chunk_size)
|
|
145
|
+
context = validate_context!(context)
|
|
146
|
+
validate_io!(io)
|
|
147
|
+
|
|
148
|
+
sk_bytes = secret_key.__send__(:bytes_for_native)
|
|
149
|
+
begin
|
|
150
|
+
tr = PQCrypto.__send__(:_native_mldsa_extract_tr, sk_bytes)
|
|
151
|
+
rescue ArgumentError => e
|
|
152
|
+
raise InvalidKeyError, e.message
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
builder = PQCrypto.__send__(:_native_mldsa_mu_builder_new, tr, context)
|
|
156
|
+
builder_consumed = false
|
|
157
|
+
mu = nil
|
|
158
|
+
begin
|
|
159
|
+
_drain_io_into_builder(io, builder, chunk_size)
|
|
160
|
+
mu = PQCrypto.__send__(:_native_mldsa_mu_builder_finalize, builder)
|
|
161
|
+
builder_consumed = true
|
|
162
|
+
PQCrypto.__send__(:_native_mldsa_sign_mu, mu, sk_bytes)
|
|
163
|
+
ensure
|
|
164
|
+
PQCrypto.__send__(:_native_mldsa_mu_builder_release, builder) unless builder_consumed
|
|
165
|
+
PQCrypto.secure_wipe(tr) if tr && !tr.frozen?
|
|
166
|
+
PQCrypto.secure_wipe(mu) if mu && !mu.frozen?
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def _streaming_verify(public_key, io, signature, chunk_size, context)
|
|
171
|
+
validate_streaming_algorithm!(public_key.algorithm)
|
|
172
|
+
validate_chunk_size!(chunk_size)
|
|
173
|
+
context = validate_context!(context)
|
|
174
|
+
validate_io!(io)
|
|
175
|
+
|
|
176
|
+
pk_bytes = public_key.__send__(:bytes_for_native)
|
|
177
|
+
begin
|
|
178
|
+
tr = PQCrypto.__send__(:_native_mldsa_compute_tr, pk_bytes)
|
|
179
|
+
rescue ArgumentError => e
|
|
180
|
+
raise InvalidKeyError, e.message
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
builder = PQCrypto.__send__(:_native_mldsa_mu_builder_new, tr, context)
|
|
184
|
+
builder_consumed = false
|
|
185
|
+
mu = nil
|
|
186
|
+
sig_bytes = String(signature).b
|
|
187
|
+
begin
|
|
188
|
+
_drain_io_into_builder(io, builder, chunk_size)
|
|
189
|
+
mu = PQCrypto.__send__(:_native_mldsa_mu_builder_finalize, builder)
|
|
190
|
+
builder_consumed = true
|
|
191
|
+
PQCrypto.__send__(:_native_mldsa_verify_mu, mu, sig_bytes, pk_bytes)
|
|
192
|
+
ensure
|
|
193
|
+
PQCrypto.__send__(:_native_mldsa_mu_builder_release, builder) unless builder_consumed
|
|
194
|
+
|
|
195
|
+
PQCrypto.secure_wipe(tr) if tr && !tr.frozen?
|
|
196
|
+
PQCrypto.secure_wipe(mu) if mu && !mu.frozen?
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def _drain_io_into_builder(io, builder, chunk_size)
|
|
201
|
+
buffer = String.new(capacity: chunk_size).b
|
|
202
|
+
loop do
|
|
203
|
+
result = io.read(chunk_size, buffer)
|
|
204
|
+
break if result.nil?
|
|
205
|
+
|
|
206
|
+
chunk = result.equal?(buffer) ? buffer : result
|
|
207
|
+
chunk_bytes = chunk.encoding == Encoding::BINARY ? chunk : chunk.b
|
|
208
|
+
break if chunk_bytes.bytesize.zero?
|
|
209
|
+
|
|
210
|
+
PQCrypto.__send__(:_native_mldsa_mu_builder_update, builder, chunk_bytes)
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def validate_io!(io)
|
|
215
|
+
unless io.respond_to?(:read)
|
|
216
|
+
raise ArgumentError, "io must respond to #read"
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def validate_chunk_size!(chunk_size)
|
|
221
|
+
unless chunk_size.is_a?(Integer) && chunk_size > 0
|
|
222
|
+
raise ArgumentError, "chunk_size must be a positive Integer"
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
def validate_context!(context)
|
|
227
|
+
ctx = String(context).b
|
|
228
|
+
if ctx.bytesize > 255
|
|
229
|
+
raise ArgumentError, "context must be at most 255 bytes (FIPS 204)"
|
|
230
|
+
end
|
|
231
|
+
ctx
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def validate_streaming_algorithm!(algorithm)
|
|
235
|
+
return if resolve_algorithm!(algorithm) == CANONICAL_ALGORITHM
|
|
236
|
+
|
|
237
|
+
raise UnsupportedAlgorithmError,
|
|
238
|
+
"Streaming sign_io/verify_io currently supports only #{CANONICAL_ALGORITHM.inspect}"
|
|
239
|
+
end
|
|
77
240
|
end
|
|
78
241
|
|
|
79
242
|
class Keypair
|
|
@@ -114,8 +277,16 @@ module PQCrypto
|
|
|
114
277
|
Serialization.public_key_to_pqc_container_pem(@algorithm, @bytes)
|
|
115
278
|
end
|
|
116
279
|
|
|
280
|
+
def to_spki_der
|
|
281
|
+
SPKI.encode_der(@algorithm, @bytes)
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
def to_spki_pem
|
|
285
|
+
SPKI.encode_pem(@algorithm, @bytes)
|
|
286
|
+
end
|
|
287
|
+
|
|
117
288
|
def verify(message, signature)
|
|
118
|
-
PQCrypto.__send__(:
|
|
289
|
+
PQCrypto.__send__(Signature.send(:native_method_for, @algorithm, :verify), String(message).b, String(signature).b, @bytes)
|
|
119
290
|
rescue ArgumentError => e
|
|
120
291
|
raise InvalidKeyError, e.message
|
|
121
292
|
end
|
|
@@ -125,6 +296,17 @@ module PQCrypto
|
|
|
125
296
|
true
|
|
126
297
|
end
|
|
127
298
|
|
|
299
|
+
def verify_io(io, signature, chunk_size: 1 << 20, context: "".b)
|
|
300
|
+
Signature.send(:_streaming_verify, self, io, signature, chunk_size, context)
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
def verify_io!(io, signature, chunk_size: 1 << 20, context: "".b)
|
|
304
|
+
unless verify_io(io, signature, chunk_size: chunk_size, context: context)
|
|
305
|
+
raise PQCrypto::VerificationError, "Verification failed"
|
|
306
|
+
end
|
|
307
|
+
true
|
|
308
|
+
end
|
|
309
|
+
|
|
128
310
|
def ==(other)
|
|
129
311
|
return false unless other.is_a?(PublicKey) && other.algorithm == algorithm
|
|
130
312
|
PQCrypto.__send__(:native_ct_equals, other.to_bytes, @bytes)
|
|
@@ -142,6 +324,10 @@ module PQCrypto
|
|
|
142
324
|
|
|
143
325
|
private
|
|
144
326
|
|
|
327
|
+
def bytes_for_native
|
|
328
|
+
@bytes
|
|
329
|
+
end
|
|
330
|
+
|
|
145
331
|
def validate_length!
|
|
146
332
|
expected = Signature.details(@algorithm).fetch(:public_key_bytes)
|
|
147
333
|
raise InvalidKeyError, "Invalid signature public key length" unless @bytes.bytesize == expected
|
|
@@ -169,12 +355,40 @@ module PQCrypto
|
|
|
169
355
|
Serialization.secret_key_to_pqc_container_pem(@algorithm, @bytes)
|
|
170
356
|
end
|
|
171
357
|
|
|
358
|
+
def to_pkcs8_der(format: :expanded)
|
|
359
|
+
case format
|
|
360
|
+
when :expanded
|
|
361
|
+
PKCS8.encode_der(@algorithm, @bytes, format: :expanded)
|
|
362
|
+
when :seed, :both
|
|
363
|
+
raise SerializationError,
|
|
364
|
+
"ML-DSA seed/both PKCS#8 export requires original seed material; use PQCrypto::PKCS8.encode_der/encode_pem directly"
|
|
365
|
+
else
|
|
366
|
+
raise SerializationError, "Unsupported PKCS#8 private key format: #{format.inspect}"
|
|
367
|
+
end
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
def to_pkcs8_pem(format: :expanded)
|
|
371
|
+
case format
|
|
372
|
+
when :expanded
|
|
373
|
+
PKCS8.encode_pem(@algorithm, @bytes, format: :expanded)
|
|
374
|
+
when :seed, :both
|
|
375
|
+
raise SerializationError,
|
|
376
|
+
"ML-DSA seed/both PKCS#8 export requires original seed material; use PQCrypto::PKCS8.encode_der/encode_pem directly"
|
|
377
|
+
else
|
|
378
|
+
raise SerializationError, "Unsupported PKCS#8 private key format: #{format.inspect}"
|
|
379
|
+
end
|
|
380
|
+
end
|
|
381
|
+
|
|
172
382
|
def sign(message)
|
|
173
|
-
PQCrypto.__send__(:
|
|
383
|
+
PQCrypto.__send__(Signature.send(:native_method_for, @algorithm, :sign), String(message).b, @bytes)
|
|
174
384
|
rescue ArgumentError => e
|
|
175
385
|
raise InvalidKeyError, e.message
|
|
176
386
|
end
|
|
177
387
|
|
|
388
|
+
def sign_io(io, chunk_size: 1 << 20, context: "".b)
|
|
389
|
+
Signature.send(:_streaming_sign, self, io, chunk_size, context)
|
|
390
|
+
end
|
|
391
|
+
|
|
178
392
|
def wipe!
|
|
179
393
|
PQCrypto.secure_wipe(@bytes)
|
|
180
394
|
self
|
|
@@ -197,6 +411,10 @@ module PQCrypto
|
|
|
197
411
|
|
|
198
412
|
private
|
|
199
413
|
|
|
414
|
+
def bytes_for_native
|
|
415
|
+
@bytes
|
|
416
|
+
end
|
|
417
|
+
|
|
200
418
|
def validate_length!
|
|
201
419
|
expected = Signature.details(@algorithm).fetch(:secret_key_bytes)
|
|
202
420
|
raise InvalidKeyError, "Invalid signature secret key length" unless @bytes.bytesize == expected
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "openssl"
|
|
4
|
+
|
|
5
|
+
module PQCrypto
|
|
6
|
+
module SPKI
|
|
7
|
+
PEM_LABEL = "PUBLIC KEY"
|
|
8
|
+
PEM_BEGIN = "-----BEGIN #{PEM_LABEL}-----"
|
|
9
|
+
PEM_END = "-----END #{PEM_LABEL}-----"
|
|
10
|
+
|
|
11
|
+
class << self
|
|
12
|
+
def encode_der(algorithm_symbol, public_key_bytes)
|
|
13
|
+
entry = AlgorithmRegistry.fetch(algorithm_symbol)
|
|
14
|
+
validate_public_key_algorithm!(algorithm_symbol, entry)
|
|
15
|
+
|
|
16
|
+
bytes = String(public_key_bytes).b
|
|
17
|
+
expected = entry.fetch(:public_key_bytes)
|
|
18
|
+
unless bytes.bytesize == expected
|
|
19
|
+
raise SerializationError,
|
|
20
|
+
"Invalid #{algorithm_symbol.inspect} public key length: expected #{expected}, got #{bytes.bytesize}"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
OpenSSL::ASN1::Sequence.new([
|
|
24
|
+
OpenSSL::ASN1::Sequence.new([
|
|
25
|
+
OpenSSL::ASN1::ObjectId.new(AlgorithmRegistry.standard_oid(algorithm_symbol)),
|
|
26
|
+
]),
|
|
27
|
+
OpenSSL::ASN1::BitString.new(bytes),
|
|
28
|
+
]).to_der.b
|
|
29
|
+
rescue OpenSSL::ASN1::ASN1Error => e
|
|
30
|
+
raise SerializationError, e.message
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def encode_pem(algorithm_symbol, public_key_bytes)
|
|
34
|
+
der = encode_der(algorithm_symbol, public_key_bytes)
|
|
35
|
+
body = encode_base64(der).scan(/.{1,64}/).join("\n")
|
|
36
|
+
"#{PEM_BEGIN}\n#{body}\n#{PEM_END}\n"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def decode_der(der)
|
|
40
|
+
input = String(der).b
|
|
41
|
+
outer = decode_asn1(input)
|
|
42
|
+
raise SerializationError, "SPKI DER contains trailing data" unless outer.to_der.b == input
|
|
43
|
+
raise SerializationError, "SPKI must be an ASN.1 SEQUENCE" unless outer.is_a?(OpenSSL::ASN1::Sequence)
|
|
44
|
+
raise SerializationError, "SPKI SEQUENCE must contain exactly 2 elements" unless outer.value.size == 2
|
|
45
|
+
|
|
46
|
+
algorithm_identifier, subject_public_key = outer.value
|
|
47
|
+
algorithm = decode_algorithm_identifier(algorithm_identifier)
|
|
48
|
+
entry = AlgorithmRegistry.fetch(algorithm)
|
|
49
|
+
validate_public_key_algorithm!(algorithm, entry)
|
|
50
|
+
|
|
51
|
+
unless subject_public_key.is_a?(OpenSSL::ASN1::BitString)
|
|
52
|
+
raise SerializationError, "SPKI subjectPublicKey must be a BIT STRING"
|
|
53
|
+
end
|
|
54
|
+
unless subject_public_key.unused_bits.zero?
|
|
55
|
+
raise SerializationError, "SPKI subjectPublicKey must have zero unused bits"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
bytes = String(subject_public_key.value).b
|
|
59
|
+
expected = entry.fetch(:public_key_bytes)
|
|
60
|
+
unless bytes.bytesize == expected
|
|
61
|
+
raise SerializationError,
|
|
62
|
+
"Invalid #{algorithm.inspect} SPKI public key length: expected #{expected}, got #{bytes.bytesize}"
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
[algorithm, bytes]
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def decode_pem(pem)
|
|
69
|
+
der = der_from_pem(pem)
|
|
70
|
+
decode_der(der)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
private
|
|
74
|
+
|
|
75
|
+
def decode_asn1(der)
|
|
76
|
+
OpenSSL::ASN1.decode(der)
|
|
77
|
+
rescue OpenSSL::ASN1::ASN1Error => e
|
|
78
|
+
raise SerializationError, e.message
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def decode_algorithm_identifier(value)
|
|
82
|
+
unless value.is_a?(OpenSSL::ASN1::Sequence)
|
|
83
|
+
raise SerializationError, "SPKI algorithm must be an AlgorithmIdentifier SEQUENCE"
|
|
84
|
+
end
|
|
85
|
+
unless value.value.size == 1
|
|
86
|
+
raise SerializationError, "SPKI AlgorithmIdentifier parameters must be absent"
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
oid = value.value.first
|
|
90
|
+
raise SerializationError, "SPKI AlgorithmIdentifier must contain an OBJECT IDENTIFIER" unless oid.is_a?(OpenSSL::ASN1::ObjectId)
|
|
91
|
+
|
|
92
|
+
algorithm = AlgorithmRegistry.by_standard_oid(oid.oid)
|
|
93
|
+
raise SerializationError, "Unsupported SPKI algorithm OID: #{oid.oid}" if algorithm.nil?
|
|
94
|
+
|
|
95
|
+
algorithm
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def validate_public_key_algorithm!(algorithm_symbol, entry)
|
|
99
|
+
return if %i[ml_kem ml_dsa].include?(entry.fetch(:family))
|
|
100
|
+
|
|
101
|
+
raise SerializationError, "SPKI public key codec is not supported for #{algorithm_symbol.inspect}"
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def encode_base64(bytes)
|
|
105
|
+
[String(bytes).b].pack("m0")
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def decode_base64(body)
|
|
109
|
+
compact = body.gsub(/[\r\n]/, "")
|
|
110
|
+
unless compact.match?(/\A(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=)?\z/)
|
|
111
|
+
raise SerializationError, "Invalid SPKI PEM: invalid base64"
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
compact.unpack1("m0").b
|
|
115
|
+
rescue ArgumentError => e
|
|
116
|
+
raise SerializationError, e.message
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def der_from_pem(pem)
|
|
120
|
+
text = String(pem)
|
|
121
|
+
match = text.match(/\A#{Regexp.escape(PEM_BEGIN)}\r?\n(?<body>[A-Za-z0-9+\/=\r\n]+)\r?\n#{Regexp.escape(PEM_END)}[ \t\r\n]*\z/)
|
|
122
|
+
raise SerializationError, "Invalid SPKI PEM: expected #{PEM_LABEL.inspect} label" unless match
|
|
123
|
+
|
|
124
|
+
body = match[:body]
|
|
125
|
+
raise SerializationError, "Invalid SPKI PEM: embedded NUL in body" if body.include?("\0")
|
|
126
|
+
|
|
127
|
+
decode_base64(body)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
data/lib/pq_crypto/version.rb
CHANGED
data/lib/pq_crypto.rb
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "rbconfig"
|
|
4
|
-
require_relative "pq_crypto/version"
|
|
5
|
-
require_relative "pq_crypto/errors"
|
|
6
|
-
|
|
7
3
|
begin
|
|
8
|
-
require "pqcrypto/pqcrypto_secure"
|
|
4
|
+
require "pqcrypto/pqcrypto_secure" # native extension first
|
|
9
5
|
rescue LoadError => original_error
|
|
6
|
+
require "rbconfig"
|
|
7
|
+
|
|
10
8
|
ext_dir = File.expand_path("pqcrypto", __dir__)
|
|
11
9
|
extensions = [".#{RbConfig::CONFIG.fetch('DLEXT')}", ".bundle", ".so"].uniq
|
|
12
10
|
search_dirs = [ext_dir, File.join(ext_dir, "pqcrypto")].uniq
|
|
@@ -30,13 +28,21 @@ rescue LoadError => original_error
|
|
|
30
28
|
raise original_error unless loaded
|
|
31
29
|
end
|
|
32
30
|
|
|
31
|
+
require_relative "pq_crypto/errors"
|
|
32
|
+
require_relative "pq_crypto/version"
|
|
33
|
+
require_relative "pq_crypto/algorithm_registry"
|
|
33
34
|
require_relative "pq_crypto/serialization"
|
|
35
|
+
require_relative "pq_crypto/spki"
|
|
36
|
+
require_relative "pq_crypto/pkcs8"
|
|
37
|
+
require_relative "pq_crypto/kem"
|
|
38
|
+
require_relative "pq_crypto/signature"
|
|
39
|
+
require_relative "pq_crypto/hybrid_kem"
|
|
34
40
|
|
|
35
41
|
module PQCrypto
|
|
36
42
|
SUITES = {
|
|
37
|
-
kem:
|
|
38
|
-
hybrid_kem:
|
|
39
|
-
signature:
|
|
43
|
+
kem: AlgorithmRegistry.supported_kems,
|
|
44
|
+
hybrid_kem: AlgorithmRegistry.supported_hybrid_kems,
|
|
45
|
+
signature: AlgorithmRegistry.supported_signatures,
|
|
40
46
|
}.freeze
|
|
41
47
|
|
|
42
48
|
NATIVE_EXTENSION_LOADED = true
|
|
@@ -44,14 +50,32 @@ module PQCrypto
|
|
|
44
50
|
module NativeBindings
|
|
45
51
|
NATIVE_METHODS = %i[
|
|
46
52
|
ml_kem_keypair
|
|
53
|
+
ml_kem_keypair_from_seed
|
|
47
54
|
ml_kem_encapsulate
|
|
48
55
|
ml_kem_decapsulate
|
|
56
|
+
ml_kem_512_keypair
|
|
57
|
+
ml_kem_512_keypair_from_seed
|
|
58
|
+
ml_kem_512_encapsulate
|
|
59
|
+
ml_kem_512_decapsulate
|
|
60
|
+
ml_kem_1024_keypair
|
|
61
|
+
ml_kem_1024_keypair_from_seed
|
|
62
|
+
ml_kem_1024_encapsulate
|
|
63
|
+
ml_kem_1024_decapsulate
|
|
49
64
|
hybrid_kem_keypair
|
|
50
65
|
hybrid_kem_encapsulate
|
|
51
66
|
hybrid_kem_decapsulate
|
|
52
67
|
sign_keypair
|
|
53
68
|
sign
|
|
54
69
|
verify
|
|
70
|
+
ml_dsa_44_keypair
|
|
71
|
+
ml_dsa_44_keypair_from_seed
|
|
72
|
+
ml_dsa_keypair_from_seed
|
|
73
|
+
ml_dsa_44_sign
|
|
74
|
+
ml_dsa_44_verify
|
|
75
|
+
ml_dsa_87_keypair
|
|
76
|
+
ml_dsa_87_keypair_from_seed
|
|
77
|
+
ml_dsa_87_sign
|
|
78
|
+
ml_dsa_87_verify
|
|
55
79
|
ct_equals
|
|
56
80
|
secure_wipe
|
|
57
81
|
version
|
|
@@ -65,8 +89,25 @@ module PQCrypto
|
|
|
65
89
|
secret_key_from_pqc_container_pem
|
|
66
90
|
__test_ml_kem_keypair_from_seed
|
|
67
91
|
__test_ml_kem_encapsulate_from_seed
|
|
92
|
+
__test_ml_kem_512_encapsulate_from_seed
|
|
93
|
+
__test_ml_kem_1024_encapsulate_from_seed
|
|
68
94
|
__test_sign_keypair_from_seed
|
|
95
|
+
__test_ml_dsa_44_keypair_from_seed
|
|
96
|
+
__test_ml_dsa_87_keypair_from_seed
|
|
69
97
|
__test_sign_from_seed
|
|
98
|
+
__test_ml_dsa_44_sign_from_seed
|
|
99
|
+
__test_ml_dsa_87_sign_from_seed
|
|
100
|
+
].freeze
|
|
101
|
+
|
|
102
|
+
EXTERNAL_MU_METHODS = %i[
|
|
103
|
+
_native_mldsa_extract_tr
|
|
104
|
+
_native_mldsa_compute_tr
|
|
105
|
+
_native_mldsa_mu_builder_new
|
|
106
|
+
_native_mldsa_mu_builder_update
|
|
107
|
+
_native_mldsa_mu_builder_finalize
|
|
108
|
+
_native_mldsa_mu_builder_release
|
|
109
|
+
_native_mldsa_sign_mu
|
|
110
|
+
_native_mldsa_verify_mu
|
|
70
111
|
].freeze
|
|
71
112
|
|
|
72
113
|
class << PQCrypto
|
|
@@ -78,6 +119,7 @@ module PQCrypto
|
|
|
78
119
|
|
|
79
120
|
private(*NativeBindings::NATIVE_METHODS)
|
|
80
121
|
private(*NativeBindings::NATIVE_METHODS.map { |n| :"native_#{n.to_s.sub(/\A__/, '')}" })
|
|
122
|
+
private(*NativeBindings::EXTERNAL_MU_METHODS)
|
|
81
123
|
end
|
|
82
124
|
end
|
|
83
125
|
|
|
@@ -115,32 +157,61 @@ module PQCrypto
|
|
|
115
157
|
end
|
|
116
158
|
|
|
117
159
|
module Testing
|
|
118
|
-
|
|
119
|
-
|
|
160
|
+
KEM_KEYPAIR_METHODS = {
|
|
161
|
+
ml_kem_512: :native_ml_kem_512_keypair_from_seed,
|
|
162
|
+
ml_kem_768: :native_ml_kem_keypair_from_seed,
|
|
163
|
+
ml_kem_1024: :native_ml_kem_1024_keypair_from_seed,
|
|
164
|
+
}.freeze
|
|
165
|
+
|
|
166
|
+
KEM_ENCAPSULATE_METHODS = {
|
|
167
|
+
ml_kem_512: :native_test_ml_kem_512_encapsulate_from_seed,
|
|
168
|
+
ml_kem_768: :native_test_ml_kem_encapsulate_from_seed,
|
|
169
|
+
ml_kem_1024: :native_test_ml_kem_1024_encapsulate_from_seed,
|
|
170
|
+
}.freeze
|
|
171
|
+
|
|
172
|
+
MLDSA_KEYPAIR_METHODS = {
|
|
173
|
+
ml_dsa_44: :native_test_ml_dsa_44_keypair_from_seed,
|
|
174
|
+
ml_dsa_65: :native_test_sign_keypair_from_seed,
|
|
175
|
+
ml_dsa_87: :native_test_ml_dsa_87_keypair_from_seed,
|
|
176
|
+
}.freeze
|
|
177
|
+
|
|
178
|
+
MLDSA_SIGN_METHODS = {
|
|
179
|
+
ml_dsa_44: :native_test_ml_dsa_44_sign_from_seed,
|
|
180
|
+
ml_dsa_65: :native_test_sign_from_seed,
|
|
181
|
+
ml_dsa_87: :native_test_ml_dsa_87_sign_from_seed,
|
|
182
|
+
}.freeze
|
|
183
|
+
|
|
184
|
+
def self.ml_kem_keypair_from_seed(seed, algorithm: :ml_kem_768)
|
|
185
|
+
PQCrypto.__send__(KEM_KEYPAIR_METHODS.fetch(algorithm), String(seed).b)
|
|
186
|
+
rescue KeyError
|
|
187
|
+
raise UnsupportedAlgorithmError, "Unsupported ML-KEM KAT algorithm: #{algorithm.inspect}"
|
|
120
188
|
rescue ArgumentError => e
|
|
121
189
|
raise InvalidKeyError, e.message
|
|
122
190
|
end
|
|
123
191
|
|
|
124
|
-
def self.ml_kem_encapsulate_from_seed(public_key, seed)
|
|
125
|
-
PQCrypto.__send__(
|
|
192
|
+
def self.ml_kem_encapsulate_from_seed(public_key, seed, algorithm: :ml_kem_768)
|
|
193
|
+
PQCrypto.__send__(KEM_ENCAPSULATE_METHODS.fetch(algorithm), String(public_key).b, String(seed).b)
|
|
194
|
+
rescue KeyError
|
|
195
|
+
raise UnsupportedAlgorithmError, "Unsupported ML-KEM KAT algorithm: #{algorithm.inspect}"
|
|
126
196
|
rescue ArgumentError => e
|
|
127
197
|
raise InvalidKeyError, e.message
|
|
128
198
|
end
|
|
129
199
|
|
|
130
|
-
def self.ml_dsa_keypair_from_seed(seed)
|
|
131
|
-
PQCrypto.__send__(
|
|
200
|
+
def self.ml_dsa_keypair_from_seed(seed, algorithm: :ml_dsa_65)
|
|
201
|
+
PQCrypto.__send__(MLDSA_KEYPAIR_METHODS.fetch(algorithm), String(seed).b)
|
|
202
|
+
rescue KeyError
|
|
203
|
+
raise UnsupportedAlgorithmError, "Unsupported ML-DSA KAT algorithm: #{algorithm.inspect}"
|
|
132
204
|
rescue ArgumentError => e
|
|
133
205
|
raise InvalidKeyError, e.message
|
|
134
206
|
end
|
|
135
207
|
|
|
136
|
-
def self.ml_dsa_sign_from_seed(message, secret_key, seed)
|
|
137
|
-
PQCrypto.__send__(
|
|
208
|
+
def self.ml_dsa_sign_from_seed(message, secret_key, seed, algorithm: :ml_dsa_65)
|
|
209
|
+
PQCrypto.__send__(MLDSA_SIGN_METHODS.fetch(algorithm), String(message).b, String(secret_key).b, String(seed).b)
|
|
210
|
+
rescue KeyError
|
|
211
|
+
raise UnsupportedAlgorithmError, "Unsupported ML-DSA KAT algorithm: #{algorithm.inspect}"
|
|
138
212
|
rescue ArgumentError => e
|
|
139
213
|
raise InvalidKeyError, e.message
|
|
140
214
|
end
|
|
141
215
|
end
|
|
142
216
|
end
|
|
143
217
|
|
|
144
|
-
require_relative "pq_crypto/kem"
|
|
145
|
-
require_relative "pq_crypto/hybrid_kem"
|
|
146
|
-
require_relative "pq_crypto/signature"
|
data/script/vendor_libs.rb
CHANGED
|
@@ -19,8 +19,12 @@ DEFAULT_PQCLEAN = {
|
|
|
19
19
|
}.freeze
|
|
20
20
|
|
|
21
21
|
KEEP_DIRS = %w[
|
|
22
|
+
crypto_kem/ml-kem-512/clean
|
|
22
23
|
crypto_kem/ml-kem-768/clean
|
|
24
|
+
crypto_kem/ml-kem-1024/clean
|
|
25
|
+
crypto_sign/ml-dsa-44/clean
|
|
23
26
|
crypto_sign/ml-dsa-65/clean
|
|
27
|
+
crypto_sign/ml-dsa-87/clean
|
|
24
28
|
common
|
|
25
29
|
].freeze
|
|
26
30
|
|