Module: Linzer::MLDSA
- Defined in:
- lib/linzer/ml_dsa.rb,
lib/linzer/ml_dsa/gem_key.rb,
lib/linzer/ml_dsa/openssl_key.rb
Overview
ML-DSA support for HTTP Message Signatures as specified by https://c2sp.org/httpsig-pq
Two independent backends live under this namespace, both supporting all three FIPS 204 parameter sets (ML-DSA-44/65/87):
- OpenSSLKey -- backed directly by OpenSSL 3.5+, no extra gem dependency. Always loaded by this file.
- GemKey -- backed by the
ml_dsagem. Optional:require "linzer/ml_dsa/gem_key"yourself to use it (which requiresml_dsain turn).
Linzer.generate_ml_dsa_*_key/Linzer.new_ml_dsa_*_key dispatch
between them via a backend: keyword (:auto, :openssl, or :ml_dsa),
preferring OpenSSL when this build actually supports it.
Defined Under Namespace
Classes: GemKey, OpenSSLKey
Constant Summary collapse
- ALGORITHMS =
{ "ml-dsa-44" => MlDsa::ML_DSA_44, "ml-dsa-65" => MlDsa::ML_DSA_65, "ml-dsa-87" => MlDsa::ML_DSA_87 }.freeze
Class Method Summary collapse
-
.deserialize_raw_or_encoded_key(material, algorithm) ⇒ OpenSSL::PKey::PKey
private
Builds an OpenSSL key from ML-DSA material of unknown shape: raw FIPS 204 bytes for
algorithm(sniffed by exact byte length, the same approach GemKey uses for the gem backend) or an OpenSSL-encoded PEM/DER key, handled as a fallback. -
.openssl_supported?(algorithm) ⇒ Boolean
Checks whether this OpenSSL build can actually perform ML-DSA signing/verification for the given algorithm.
Class Method Details
.deserialize_raw_or_encoded_key(material, algorithm) ⇒ OpenSSL::PKey::PKey
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Builds an OpenSSL key from ML-DSA material of unknown shape: raw
FIPS 204 bytes for algorithm (sniffed by exact byte length, the
same approach GemKey uses for the gem backend) or
an OpenSSL-encoded PEM/DER key, handled as a fallback. Sniffing is
scoped to algorithm's own raw sizes, not all three parameter
sets' sizes at once, material sized for a different parameter
set than requested falls through to the OpenSSL::PKey.read
fallback and fails there (raw bytes aren't valid PEM/DER), rather
than silently being accepted and mislabeled.
214 215 216 217 218 219 220 221 222 223 |
# File 'lib/linzer/ml_dsa/openssl_key.rb', line 214 def deserialize_raw_or_encoded_key(material, algorithm) case material.bytesize when RAW_PUBLIC_KEY_BYTES.fetch(algorithm) wrap_raw_public_key(material, algorithm) when RAW_PRIVATE_KEY_BYTES.fetch(algorithm) wrap_raw_private_key(material, algorithm) else OpenSSL::PKey.read(material) end end |
.openssl_supported?(algorithm) ⇒ Boolean
Checks whether this OpenSSL build can actually perform ML-DSA signing/verification for the given algorithm.
Returns true only when the algorithm is both something this
OpenSSL-backed implementation has actually implemented (see
IMPLEMENTED_ALGORITHMS) and something the underlying OpenSSL
library itself supports. This is needed mostly because a
version-number check alone isn't reliable and some distributions ship
OpenSSL 3.5+ with ML-DSA disabled by crypto policy (see
https://github.com/ruby/openssl/issues/1075), so this actually
attempts a throwaway key generation rather than inspecting
OpenSSL::OPENSSL_VERSION.
Memoized per algorithm after the first check, so repeated calls are
free. Unknown or not-yet-implemented algorithm identifiers return
false rather than raising.
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 |
# File 'lib/linzer/ml_dsa/openssl_key.rb', line 182 def openssl_supported?(algorithm) cache = (@openssl_supported ||= {}) return cache[algorithm] if cache.key?(algorithm) cache[algorithm] = if IMPLEMENTED_ALGORITHMS.include?(algorithm) begin OpenSSL::PKey.generate_key(algorithm.upcase) true rescue OpenSSL::PKey::PKeyError false end else false end end |