Class: JWT::PQ::Algorithms::HybridEdDsa Private
- Inherits:
-
Object
- Object
- JWT::PQ::Algorithms::HybridEdDsa
- Includes:
- JWA::SigningAlgorithm
- Defined in:
- lib/jwt/pq/algorithms/hybrid_eddsa.rb
Overview
This class is part of a private API. You should avoid using this class if possible, as it may be removed or be changed in the future.
JWT signing algorithm for hybrid EdDSA + ML-DSA signatures.
The signature is a simple concatenation: ed25519_sig (64 bytes) || ml_dsa_sig. This allows PQ-aware verifiers to validate both, while the fixed 64-byte Ed25519 prefix makes it possible to split the signatures deterministically.
Users interact with these algorithms via JWT.encode/JWT.decode by
name ("EdDSA+ML-DSA-*"); they never instantiate this class directly.
Constant Summary collapse
- ED25519_SIG_SIZE =
This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.
64
Instance Method Summary collapse
- #header ⇒ Object private
-
#initialize(alg) ⇒ HybridEdDsa
constructor
private
A new instance of HybridEdDsa.
-
#safe_ed25519_verify(verify_key, signature, data) ⇒ Object
private
Boolean-returning wrapper over
Ed25519::VerifyKey#verify, which raises on failure. - #sign(data:, signing_key:) ⇒ Object private
- #verify(data:, signature:, verification_key:) ⇒ Object private
Constructor Details
#initialize(alg) ⇒ HybridEdDsa
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.
Returns a new instance of HybridEdDsa.
23 24 25 26 27 |
# File 'lib/jwt/pq/algorithms/hybrid_eddsa.rb', line 23 def initialize(alg) @alg = alg @ml_dsa_algorithm = alg.sub("EdDSA+", "") @header = { "alg" => alg, "pq_alg" => @ml_dsa_algorithm }.freeze end |
Instance Method Details
#header ⇒ Object
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.
29 30 31 |
# File 'lib/jwt/pq/algorithms/hybrid_eddsa.rb', line 29 def header(*) @header end |
#safe_ed25519_verify(verify_key, signature, data) ⇒ Object
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.
Boolean-returning wrapper over Ed25519::VerifyKey#verify, which
raises on failure. Gives the verify path a uniform shape for both
halves (both produce a bool by assignment, rather than one via
return value and one via rescue).
82 83 84 85 86 87 |
# File 'lib/jwt/pq/algorithms/hybrid_eddsa.rb', line 82 def safe_ed25519_verify(verify_key, signature, data) verify_key.verify(signature, data) true rescue Ed25519::VerifyError false end |
#sign(data:, signing_key:) ⇒ Object
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.
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
# File 'lib/jwt/pq/algorithms/hybrid_eddsa.rb', line 33 def sign(data:, signing_key:) unless signing_key.is_a?(JWT::PQ::HybridKey) raise_sign_error!( "Expected a JWT::PQ::HybridKey, got #{signing_key.class}. " \ "Use JWT::PQ::HybridKey.generate to create a hybrid key." ) end raise_sign_error!("Both Ed25519 and ML-DSA private keys required") unless signing_key.private? # Delegate to HybridKey#sign so the Ed25519 and ML-DSA halves # are taken atomically under the hybrid key's mutex — a # concurrent destroy! can no longer slip between the two # component signatures. signing_key.sign(data) end |
#verify(data:, signature:, verification_key:) ⇒ Object
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.
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
# File 'lib/jwt/pq/algorithms/hybrid_eddsa.rb', line 49 def verify(data:, signature:, verification_key:) unless verification_key.is_a?(JWT::PQ::HybridKey) raise_verify_error!( "Expected a JWT::PQ::HybridKey, got #{verification_key.class}." ) end return false if signature.bytesize <= ED25519_SIG_SIZE ed_sig = signature.byteslice(0, ED25519_SIG_SIZE) ml_sig = signature.byteslice(ED25519_SIG_SIZE..) ed_valid = safe_ed25519_verify(verification_key.ed25519_verify_key, ed_sig, data) ml_valid = verification_key.ml_dsa_key.verify(data, ml_sig) # Bitwise `&`, not `&&`: both checks are already computed above, # and a bitwise AND over booleans has no short-circuit, so the # final combinator does not branch on which half failed. This # does not give a cryptographic constant-time guarantee (Ruby # can't), but it removes the obvious observable path. ed_valid & ml_valid # :nocov: — defensive rescue; Key#verify returns bool, does not raise PQ::Error in practice rescue JWT::PQ::Error false # :nocov: end |