Class: JWT::PQ::MlDsa
- Inherits:
-
Object
- Object
- JWT::PQ::MlDsa
- Defined in:
- lib/jwt/pq/ml_dsa.rb
Overview
Ruby wrapper around liboqs ML-DSA operations. Handles memory allocation, FFI calls, and cleanup.
Constant Summary collapse
- ALGORITHMS =
{ "ML-DSA-44" => { public_key: 1312, secret_key: 2560, signature: 2420, nist_level: 2 }, "ML-DSA-65" => { public_key: 1952, secret_key: 4032, signature: 3309, nist_level: 3 }, "ML-DSA-87" => { public_key: 2592, secret_key: 4896, signature: 4627, nist_level: 5 } }.freeze
Instance Attribute Summary collapse
-
#algorithm ⇒ Object
readonly
Returns the value of attribute algorithm.
Class Method Summary collapse
Instance Method Summary collapse
-
#initialize(algorithm) ⇒ MlDsa
constructor
A new instance of MlDsa.
-
#keypair ⇒ Object
Generate a new keypair.
-
#public_key_size ⇒ Object
Key sizes for this algorithm.
- #secret_key_size ⇒ Object
-
#sign(message, secret_key) ⇒ Object
Sign a message with a secret key.
-
#sign_with_sk_buffer(message, sk_buf) ⇒ Object
Faster sign path: takes a pre-populated FFI::MemoryPointer holding the secret key.
- #signature_size ⇒ Object
-
#verify(message, signature, public_key) ⇒ Object
Verify a signature against a message and public key.
-
#verify_with_pk_buffer(message, signature, pk_buf) ⇒ Object
Faster verify path: takes a pre-populated FFI::MemoryPointer holding the public key.
Constructor Details
#initialize(algorithm) ⇒ MlDsa
Returns a new instance of MlDsa.
44 45 46 47 48 49 50 51 52 53 54 |
# File 'lib/jwt/pq/ml_dsa.rb', line 44 def initialize(algorithm) algorithm = algorithm.to_s unless ALGORITHMS.key?(algorithm) raise UnsupportedAlgorithmError, "Unsupported algorithm: #{algorithm}. " \ "Supported: #{ALGORITHMS.keys.join(", ")}" end @algorithm = algorithm @sizes = ALGORITHMS[algorithm] end |
Instance Attribute Details
#algorithm ⇒ Object (readonly)
Returns the value of attribute algorithm.
42 43 44 |
# File 'lib/jwt/pq/ml_dsa.rb', line 42 def algorithm @algorithm end |
Class Method Details
.sign_handle(algorithm) ⇒ Object
17 18 19 20 21 22 23 24 25 26 |
# File 'lib/jwt/pq/ml_dsa.rb', line 17 def self.sign_handle(algorithm) @sign_handles[algorithm] || @sign_handles_mutex.synchronize do @sign_handles[algorithm] ||= begin h = LibOQS.OQS_SIG_new(algorithm) raise LiboqsError, "Failed to initialize #{algorithm}" if h.null? h end end end |
.verify_handle(algorithm) ⇒ Object
31 32 33 34 35 36 37 38 39 40 |
# File 'lib/jwt/pq/ml_dsa.rb', line 31 def self.verify_handle(algorithm) @verify_handles[algorithm] || @verify_handles_mutex.synchronize do @verify_handles[algorithm] ||= begin h = LibOQS.OQS_SIG_new(algorithm) raise LiboqsError, "Failed to initialize #{algorithm}" if h.null? h end end end |
Instance Method Details
#keypair ⇒ Object
Generate a new keypair. Returns [public_key_bytes, secret_key_bytes]
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/jwt/pq/ml_dsa.rb', line 58 def keypair sig = LibOQS.OQS_SIG_new(@algorithm) raise LiboqsError, "Failed to initialize #{@algorithm}" if sig.null? pk = FFI::MemoryPointer.new(:uint8, @sizes[:public_key]) sk = FFI::MemoryPointer.new(:uint8, @sizes[:secret_key]) status = LibOQS.OQS_SIG_keypair(sig, pk, sk) raise LiboqsError, "Keypair generation failed for #{@algorithm}" unless status == LibOQS::OQS_SUCCESS [pk.read_bytes(@sizes[:public_key]), sk.read_bytes(@sizes[:secret_key])] ensure sk&.clear LibOQS.OQS_SIG_free(sig) if sig && !sig.null? end |
#public_key_size ⇒ Object
Key sizes for this algorithm
129 130 131 |
# File 'lib/jwt/pq/ml_dsa.rb', line 129 def public_key_size @sizes[:public_key] end |
#secret_key_size ⇒ Object
133 134 135 |
# File 'lib/jwt/pq/ml_dsa.rb', line 133 def secret_key_size @sizes[:secret_key] end |
#sign(message, secret_key) ⇒ Object
Sign a message with a secret key. Returns the signature bytes.
76 77 78 79 80 81 82 83 84 |
# File 'lib/jwt/pq/ml_dsa.rb', line 76 def sign(, secret_key) validate_key_size!(secret_key, :secret_key) sk_buf = FFI::MemoryPointer.new(:uint8, secret_key.bytesize) sk_buf.put_bytes(0, secret_key) sign_with_sk_buffer(, sk_buf) ensure sk_buf&.clear end |
#sign_with_sk_buffer(message, sk_buf) ⇒ Object
Faster sign path: takes a pre-populated FFI::MemoryPointer holding the secret key. Caller is responsible for buffer lifecycle (allocation, zeroing). Used by JWT::PQ::Key to avoid re-allocating+copying the secret key on every sign call.
90 91 92 93 94 95 96 97 98 99 100 101 |
# File 'lib/jwt/pq/ml_dsa.rb', line 90 def sign_with_sk_buffer(, sk_buf) sig = self.class.sign_handle(@algorithm) sig_buf = FFI::MemoryPointer.new(:uint8, @sizes[:signature]) sig_len = FFI::MemoryPointer.new(:size_t) msg_buf = FFI::MemoryPointer.from_string() status = LibOQS.OQS_SIG_sign(sig, sig_buf, sig_len, msg_buf, .bytesize, sk_buf) raise SignatureError, "Signing failed for #{@algorithm}" unless status == LibOQS::OQS_SUCCESS sig_buf.read_bytes(sig_len.read(:size_t)) end |
#signature_size ⇒ Object
137 138 139 |
# File 'lib/jwt/pq/ml_dsa.rb', line 137 def signature_size @sizes[:signature] end |
#verify(message, signature, public_key) ⇒ Object
Verify a signature against a message and public key. Returns true if valid, false otherwise.
105 106 107 108 109 110 111 |
# File 'lib/jwt/pq/ml_dsa.rb', line 105 def verify(, signature, public_key) validate_key_size!(public_key, :public_key) pk_buf = FFI::MemoryPointer.new(:uint8, public_key.bytesize) pk_buf.put_bytes(0, public_key) verify_with_pk_buffer(, signature, pk_buf) end |
#verify_with_pk_buffer(message, signature, pk_buf) ⇒ Object
Faster verify path: takes a pre-populated FFI::MemoryPointer holding the public key. Caller is responsible for buffer lifecycle. Used by JWT::PQ::Key to avoid re-allocating+copying the public key on every verify call.
117 118 119 120 121 122 123 124 125 126 |
# File 'lib/jwt/pq/ml_dsa.rb', line 117 def verify_with_pk_buffer(, signature, pk_buf) sig = self.class.verify_handle(@algorithm) msg_buf = FFI::MemoryPointer.from_string() sig_buf = FFI::MemoryPointer.new(:uint8, signature.bytesize) sig_buf.put_bytes(0, signature) status = LibOQS.OQS_SIG_verify(sig, msg_buf, .bytesize, sig_buf, signature.bytesize, pk_buf) status == LibOQS::OQS_SUCCESS end |