Class: JWT::PQ::Key
- Inherits:
-
Object
- Object
- JWT::PQ::Key
- Defined in:
- lib/jwt/pq/key.rb
Overview
Represents an ML-DSA keypair (public + optional private key). Used as the signing/verification key for JWT operations.
Constant Summary collapse
- ALGORITHM_ALIASES =
rubocop:disable Metrics/ClassLength
{ ml_dsa_44: "ML-DSA-44", ml_dsa_65: "ML-DSA-65", ml_dsa_87: "ML-DSA-87" }.freeze
- ALGORITHM_OIDS =
{ "ML-DSA-44" => PqcAsn1::OID::ML_DSA_44, "ML-DSA-65" => PqcAsn1::OID::ML_DSA_65, "ML-DSA-87" => PqcAsn1::OID::ML_DSA_87 }.freeze
- OID_TO_ALGORITHM =
ALGORITHM_OIDS.invert.freeze
Instance Attribute Summary collapse
-
#algorithm ⇒ Object
readonly
Returns the value of attribute algorithm.
-
#private_key ⇒ Object
readonly
Returns the value of attribute private_key.
-
#public_key ⇒ Object
readonly
Returns the value of attribute public_key.
Class Method Summary collapse
-
.from_pem(pem_string) ⇒ Object
Import a Key from a PEM string (SPKI or PKCS#8).
-
.from_pem_pair(public_pem:, private_pem:) ⇒ Object
Import a Key from separate public and private PEM strings.
-
.from_public_key(algorithm, public_key_bytes) ⇒ Object
Create a Key from raw public key bytes (verification only).
-
.generate(algorithm) ⇒ Object
Generate a new keypair for the given algorithm.
Instance Method Summary collapse
-
#destroy! ⇒ Object
Zero and discard private key material from Ruby memory.
-
#initialize(algorithm:, public_key:, private_key: nil) ⇒ Key
constructor
A new instance of Key.
- #inspect ⇒ Object (also: #to_s)
-
#private? ⇒ Boolean
Whether this key can be used for signing.
-
#private_to_pem ⇒ Object
Export the private key as PEM (PKCS#8 format).
-
#sign(data) ⇒ Object
Sign data using the private key.
-
#to_pem ⇒ Object
Export the public key as PEM (SPKI format).
-
#verify(data, signature) ⇒ Object
Verify a signature using the public key.
Constructor Details
#initialize(algorithm:, public_key:, private_key: nil) ⇒ Key
Returns a new instance of Key.
26 27 28 29 30 31 32 33 |
# File 'lib/jwt/pq/key.rb', line 26 def initialize(algorithm:, public_key:, private_key: nil) @algorithm = resolve_algorithm(algorithm) @ml_dsa = MlDsa.new(@algorithm) @public_key = public_key @private_key = private_key validate! end |
Instance Attribute Details
#algorithm ⇒ Object (readonly)
Returns the value of attribute algorithm.
24 25 26 |
# File 'lib/jwt/pq/key.rb', line 24 def algorithm @algorithm end |
#private_key ⇒ Object (readonly)
Returns the value of attribute private_key.
24 25 26 |
# File 'lib/jwt/pq/key.rb', line 24 def private_key @private_key end |
#public_key ⇒ Object (readonly)
Returns the value of attribute public_key.
24 25 26 |
# File 'lib/jwt/pq/key.rb', line 24 def public_key @public_key end |
Class Method Details
.from_pem(pem_string) ⇒ Object
Import a Key from a PEM string (SPKI or PKCS#8).
84 85 86 87 88 89 90 91 92 93 94 95 |
# File 'lib/jwt/pq/key.rb', line 84 def self.from_pem(pem_string) info = PqcAsn1::DER.parse_pem(pem_string) alg_name = resolve_oid!(info.oid) case info.format when :spki then new(algorithm: alg_name, public_key: info.key) when :pkcs8 then build_from_pkcs8(info, alg_name) else raise KeyError, "Unsupported PEM format: #{info.format}" end ensure info&.key&.wipe! if info&.format == :pkcs8 end |
.from_pem_pair(public_pem:, private_pem:) ⇒ Object
Import a Key from separate public and private PEM strings.
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
# File 'lib/jwt/pq/key.rb', line 98 def self.from_pem_pair(public_pem:, private_pem:) pub_info = PqcAsn1::DER.parse_pem(public_pem) priv_info = PqcAsn1::DER.parse_pem(private_pem) pub_alg = OID_TO_ALGORITHM[pub_info.oid] priv_alg = OID_TO_ALGORITHM[priv_info.oid] raise KeyError, "Unknown OID in public PEM: #{pub_info.oid.dotted}" unless pub_alg raise KeyError, "Unknown OID in private PEM: #{priv_info.oid.dotted}" unless priv_alg raise KeyError, "Algorithm mismatch: public=#{pub_alg}, private=#{priv_alg}" unless pub_alg == priv_alg sk_bytes = extract_secure_bytes(priv_info.key) new(algorithm: pub_alg, public_key: pub_info.key, private_key: sk_bytes) ensure priv_info&.key&.wipe! end |
.from_public_key(algorithm, public_key_bytes) ⇒ Object
Create a Key from raw public key bytes (verification only).
45 46 47 |
# File 'lib/jwt/pq/key.rb', line 45 def self.from_public_key(algorithm, public_key_bytes) new(algorithm: algorithm, public_key: public_key_bytes) end |
.generate(algorithm) ⇒ Object
Generate a new keypair for the given algorithm.
36 37 38 39 40 41 42 |
# File 'lib/jwt/pq/key.rb', line 36 def self.generate(algorithm) alg_name = resolve_algorithm(algorithm) ml_dsa = MlDsa.new(alg_name) pk, sk = ml_dsa.keypair new(algorithm: alg_name, public_key: pk, private_key: sk) end |
Instance Method Details
#destroy! ⇒ Object
Zero and discard private key material from Ruby memory. After calling this, the key can only be used for verification.
68 69 70 71 72 73 74 75 76 |
# File 'lib/jwt/pq/key.rb', line 68 def destroy! if @private_key @private_key.replace("\0" * @private_key.bytesize) @private_key = nil end @sk_buffer&.clear @sk_buffer = nil true end |
#inspect ⇒ Object Also known as: to_s
78 79 80 |
# File 'lib/jwt/pq/key.rb', line 78 def inspect "#<#{self.class} algorithm=#{@algorithm} private=#{private?}>" end |
#private? ⇒ Boolean
Whether this key can be used for signing.
62 63 64 |
# File 'lib/jwt/pq/key.rb', line 62 def private? !@private_key.nil? end |
#private_to_pem ⇒ Object
Export the private key as PEM (PKCS#8 format).
123 124 125 126 127 128 129 |
# File 'lib/jwt/pq/key.rb', line 123 def private_to_pem raise KeyError, "Private key not available" unless @private_key oid = ALGORITHM_OIDS[@algorithm] secure_der = PqcAsn1::DER.build_pkcs8(oid, @private_key, public_key: @public_key) secure_der.to_pem end |
#sign(data) ⇒ Object
Sign data using the private key.
50 51 52 53 54 |
# File 'lib/jwt/pq/key.rb', line 50 def sign(data) raise KeyError, "Private key not available — cannot sign" unless @private_key @ml_dsa.sign_with_sk_buffer(data, sk_buffer) end |
#to_pem ⇒ Object
Export the public key as PEM (SPKI format).
116 117 118 119 120 |
# File 'lib/jwt/pq/key.rb', line 116 def to_pem oid = ALGORITHM_OIDS[@algorithm] der = PqcAsn1::DER.build_spki(oid, @public_key) PqcAsn1::PEM.encode(der, "PUBLIC KEY") end |
#verify(data, signature) ⇒ Object
Verify a signature using the public key.
57 58 59 |
# File 'lib/jwt/pq/key.rb', line 57 def verify(data, signature) @ml_dsa.verify_with_pk_buffer(data, signature, pk_buffer) end |