All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Initial release implementing NIST FIPS 204 ML-DSA (Module-Lattice-Based Digital Signature Algorithm) as a Ruby C extension.
- Three parameter sets: ML-DSA-44 (NIST Level 2), ML-DSA-65 (Level 3), ML-DSA-87 (Level 5), backed by vendored PQClean clean C implementation.
MlDsa.keygen(param_set)— key pair generation with optionalseed:for deterministic keygen from a 32-byte seed.SecretKey#sign(message, deterministic:, context:)— hedged (randomized, default) and deterministic signing with optional FIPS 204 context strings.PublicKey#verify(message, signature, context:)— signature verification.- Batch operations:
MlDsa.sign_many/MlDsa.verify_manyexecute multiple operations in a single GVL release for thread concurrency. MlDsa.batch { |b| ... }— block-based batch builder that collects sign or verify operations and executes them together.verify_manyreturnsArray[Result]with per-item.ok?and.reason(distinguisheswrong_signature_sizefromverification_failed).- Build-time parameter set selection:
--with-ml-dsa-params=44,65to compile only a subset (reduces binary size).
SecretKey#public_key— returns the associatedPublicKeyfrom keygen (nil for deserialized keys).SecretKey.from_seed(seed, param_set)— reconstruct from a 32-byte seed withpublic_keyattached automatically.SecretKey#seed— access the keygen seed for compact storage (nil if not created from a seed). Securely zeroed onwipe!and GC.PublicKey#fingerprint— SHA-256 prefix (32 hex chars) for identification in logs and UIs.PublicKey#created_at/SecretKey#created_at— timestamp set at construction for audit trails and key rotation policies.PublicKey#key_usage=/SecretKey#key_usage=— optionalSymbolmetadata for application-defined usage labels.
- Raw bytes:
to_bytes/from_byteswith auto-detection of parameter set from byte length. - Hex:
to_hex/from_hexwith auto-detection. - DER:
to_der/from_der— SubjectPublicKeyInfo for public keys, PKCS#8 / OneAsymmetricKey for secret keys (OIDs per FIPS 204). - PEM:
to_pem/from_pem— PEM-armored DER. - DER/PEM handled by the
pqc_asn1gem with no OpenSSL dependency. Secret key DER intermediates are held inPqcAsn1::SecureBuffer(mmap-protected, securely zeroed).
- Key bytes live in C-managed memory, securely zeroed on GC.
SecretKey#with_bytes { |buf| ... }— controlled access with automatic wipe on block exit (even on exception).SecretKey#wipe!— explicit zeroing; subsequent operations raiseMlDsa::Error.- Memory locking:
mlockprevents secret key pages from swapping to disk. - No
SecretKey#to_bytesor#to_hex— prevents accidental key leakage into logs, exception messages, or long-lived Ruby Strings. dup/cloneandMarshal.dumpraiseTypeErroron both key classes.
MlDsa::Config— encapsulates mutable state (subscribers, RNG). All operations accept optionalconfig:for per-Ractor or per-test contexts.MlDsa.subscribe { |event| ... }— audit logging hooks with:operation,:param_set,:count,:duration_ns. No key material exposed.MlDsa.random_source=— pluggable RNG for testing or HSM integration. Keygen uses it to generate a seed; signing uses it for the rnd nonce.yield_every:keyword on batch operations for cooperative fiber scheduling in async servers.
- GVL release: all crypto operations run without the Global VM Lock.
PublicKeyis Ractor-shareable (RUBY_TYPED_FROZEN_SHAREABLE, Ruby 3.0+).- Thread-safe wipe detection via C11
_Atomicwith acquire/release semantics. - Ractor-compatible instrumentation (silently no-op in non-main Ractors).
PQC::MlDsaalias withPQC.register/PQC.algorithms/PQC.algorithm(:name)registry for future multi-algorithm discovery.
MlDsa::Errorbase class with subclassesError::KeyGeneration,Error::Signing,Error::Deserialization.- Deserialization errors include
format,position, andreasonmetadata. ParameterSetincludesComparablefor sorting and comparison.
- Secure zeroing:
SecureZeroMemory(Windows),explicit_bzero(Linux/BSD/macOS),memset_s(C11), or volatile fallback with compiler fence. - Constant-time secret key comparison via XOR-accumulate with compiler fence.
- Platform-appropriate CSPRNG:
getrandom(2)(Linux),arc4random_buf(macOS/BSD),BCryptGenRandom(Windows). -fvisibility=hiddenprevents PQClean symbols from clashing with other gems.- Process-salted hashing for
#hashmethod. - Heap buffers freed via
rb_ensure— no leak on exceptions.