Class: JWT::PQ::JWK
- Inherits:
-
Object
- Object
- JWT::PQ::JWK
- Defined in:
- lib/jwt/pq/jwk.rb
Overview
JWK (JSON Web Key) import/export for ML-DSA keys.
Follows the draft-ietf-cose-dilithium conventions for the AKP
("Algorithm Key Pair") key type:
kty:"AKP"alg:"ML-DSA-44","ML-DSA-65", or"ML-DSA-87"pub: base64url-encoded public key (no padding)priv: base64url-encoded private key (optional, no padding)kid: RFC 7638 thumbprint over the required members
Constant Summary collapse
- ALGORITHMS =
Algorithm names accepted in the
algfield. MlDsa::ALGORITHMS.keys.freeze
- KTY =
Value of the
ktyfield for all ML-DSA JWKs. "AKP"
Instance Attribute Summary collapse
-
#key ⇒ JWT::PQ::Key
readonly
The wrapped key.
Class Method Summary collapse
- .compute_thumbprint(algorithm, public_key) ⇒ String private
-
.import(jwk_hash) ⇒ JWT::PQ::Key
Import a Key from a JWK hash.
Instance Method Summary collapse
-
#export(include_private: false) ⇒ Hash{Symbol=>String}
Export the key as a JWK hash.
-
#initialize(key) ⇒ JWK
constructor
Wrap a Key for JWK operations.
-
#thumbprint ⇒ String
Compute the JWK Thumbprint (RFC 7638) used as
kid.
Constructor Details
Instance Attribute Details
#key ⇒ JWT::PQ::Key (readonly)
Returns the wrapped key.
34 35 36 |
# File 'lib/jwt/pq/jwk.rb', line 34 def key @key end |
Class Method Details
.compute_thumbprint(algorithm, public_key) ⇒ String
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.
Compute an RFC 7638 thumbprint from algorithm + public key bytes without allocating a JWT::PQ::JWK or Key wrapper.
119 120 121 122 123 124 125 126 127 128 129 130 |
# File 'lib/jwt/pq/jwk.rb', line 119 def self.compute_thumbprint(algorithm, public_key) # RFC 7638 §3.2: canonical JSON over the required members in # lexicographic order (alg, kty, pub), no whitespace. Using # `JSON.generate` over an ordered Hash instead of string # interpolation so a future algorithm or key-byte change that # introduces a character needing JSON escape does not silently # produce a divergent thumbprint. pub_b64 = ::Base64.urlsafe_encode64(public_key, padding: false) canonical = JSON.generate({ alg: algorithm, kty: KTY, pub: pub_b64 }) digest = OpenSSL::Digest::SHA256.digest(canonical) ::Base64.urlsafe_encode64(digest, padding: false) end |
.import(jwk_hash) ⇒ JWT::PQ::Key
Import a Key from a JWK hash.
Accepts string or symbol keys. Validates kty, alg, and the
presence/base64url-ness of pub (and priv if present).
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
# File 'lib/jwt/pq/jwk.rb', line 79 def self.import(jwk_hash) raise KeyError, "Expected a Hash for JWK, got #{jwk_hash.class}" unless jwk_hash.is_a?(Hash) jwk = normalize_keys(jwk_hash) validate_kty!(jwk) alg = validate_alg!(jwk) raise KeyError, "Missing 'pub' in JWK" unless jwk.key?("pub") raise KeyError, "'pub' must be a String, got #{jwk["pub"].class}" unless jwk["pub"].is_a?(String) pub_bytes = decode_field(jwk, "pub") if jwk.key?("priv") raise KeyError, "'priv' must be a String, got #{jwk["priv"].class}" unless jwk["priv"].is_a?(String) priv_bytes = decode_field(jwk, "priv") Key.new(algorithm: alg, public_key: pub_bytes, private_key: priv_bytes) else Key.new(algorithm: alg, public_key: pub_bytes) end end |
Instance Method Details
#export(include_private: false) ⇒ Hash{Symbol=>String}
Export the key as a JWK hash.
By default, only the public material is included. Pass
include_private: true to emit the priv field as well (and only
when the wrapped key actually has a private component).
55 56 57 58 59 60 61 62 63 64 65 66 |
# File 'lib/jwt/pq/jwk.rb', line 55 def export(include_private: false) jwk = { kty: KTY, alg: @key.algorithm, pub: base64url_encode(@key.public_key), kid: thumbprint } jwk[:priv] = base64url_encode(@key.private_key) if include_private && @key.private? jwk end |
#thumbprint ⇒ String
Compute the JWK Thumbprint (RFC 7638) used as kid.
Delegates to Key#jwk_thumbprint, which memoizes the result on the key — repeated calls on the same key avoid recomputing the canonical JSON + SHA-256 digest.
108 109 110 |
# File 'lib/jwt/pq/jwk.rb', line 108 def thumbprint @key.jwk_thumbprint end |