Class: AtprotoAuth::DPoP::KeyManager
- Inherits:
-
Object
- Object
- AtprotoAuth::DPoP::KeyManager
- Defined in:
- lib/atproto_auth/dpop/key_manager.rb
Overview
Manages ES256 keypair generation and storage for DPoP proofs. Provides functionality to generate new keys and store them securely. Uses JOSE for cryptographic operations and key format handling.
Defined Under Namespace
Classes: KeyError
Constant Summary collapse
- CURVE =
Default curve for ES256 key generation
"P-256"
- ALGORITHM =
Default algorithm for key usage
"ES256"
Instance Attribute Summary collapse
-
#keypair ⇒ JOSE::JWK
readonly
The current DPoP keypair.
Class Method Summary collapse
-
.from_jwk(jwk) ⇒ KeyManager
Creates a KeyManager instance from a JWK.
Instance Method Summary collapse
- #deep_stringify_keys(obj) ⇒ Object
-
#generate_keypair ⇒ JOSE::JWK
Generates a new ES256 keypair for DPoP usage.
-
#initialize(keypair = nil) ⇒ KeyManager
constructor
Creates a new KeyManager instance with an optional existing keypair.
-
#public_jwk ⇒ Hash
Returns the public key in JWK format.
-
#sign(data) ⇒ String
Signs data using the private key.
- #sign_segments(header, payload) ⇒ Object
-
#to_jwk(include_private: false) ⇒ Hash
Exports the keypair in JWK format.
-
#verify(signed_jws) ⇒ Boolean
Verifies a signed JWS.
Constructor Details
#initialize(keypair = nil) ⇒ KeyManager
Creates a new KeyManager instance with an optional existing keypair
23 24 25 26 |
# File 'lib/atproto_auth/dpop/key_manager.rb', line 23 def initialize(keypair = nil) @keypair = keypair || generate_keypair validate_keypair! end |
Instance Attribute Details
#keypair ⇒ JOSE::JWK (readonly)
Returns The current DPoP keypair.
18 19 20 |
# File 'lib/atproto_auth/dpop/key_manager.rb', line 18 def keypair @keypair end |
Class Method Details
.from_jwk(jwk) ⇒ KeyManager
Creates a KeyManager instance from a JWK
137 138 139 140 141 142 |
# File 'lib/atproto_auth/dpop/key_manager.rb', line 137 def self.from_jwk(jwk) keypair = JOSE::JWK.from_map(jwk) new(keypair) rescue StandardError => e raise KeyError, "Failed to import key: #{e.}" end |
Instance Method Details
#deep_stringify_keys(obj) ⇒ Object
98 99 100 101 102 103 104 105 106 107 108 109 |
# File 'lib/atproto_auth/dpop/key_manager.rb', line 98 def deep_stringify_keys(obj) case obj when Hash obj.each_with_object({}) do |(k, v), hash| hash[k.to_s] = deep_stringify_keys(v) end when Array obj.map { |v| deep_stringify_keys(v) } else obj end end |
#generate_keypair ⇒ JOSE::JWK
Generates a new ES256 keypair for DPoP usage
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
# File 'lib/atproto_auth/dpop/key_manager.rb', line 31 def generate_keypair # Generate base keypair base_key = JOSE::JWK.generate_key([:ec, CURVE]) base_map = base_key.to_map # Create new map with all required properties key_map = { "kty" => base_map["kty"], "crv" => base_map["crv"], "x" => base_map["x"], "y" => base_map["y"], "d" => base_map["d"], "use" => "sig", "kid" => generate_kid(base_map) } # Create new JWK with all properties JOSE::JWK.from_map(key_map) rescue StandardError => e raise KeyError, "Failed to generate keypair: #{e.}" end |
#public_jwk ⇒ Hash
Returns the public key in JWK format
55 56 57 58 59 60 61 62 63 |
# File 'lib/atproto_auth/dpop/key_manager.rb', line 55 def public_jwk jwk = @keypair.to_public.to_map.to_h # If somehow the properties aren't set, add them jwk["use"] ||= "sig" jwk["kid"] ||= generate_kid(jwk) jwk rescue StandardError => e raise KeyError, "Failed to export public key: #{e.}" end |
#sign(data) ⇒ String
Signs data using the private key
69 70 71 72 73 |
# File 'lib/atproto_auth/dpop/key_manager.rb', line 69 def sign(data) @keypair.sign(data).compact rescue StandardError => e raise KeyError, "Failed to sign data: #{e.}" end |
#sign_segments(header, payload) ⇒ Object
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
# File 'lib/atproto_auth/dpop/key_manager.rb', line 75 def sign_segments(header, payload) # Deep transform all keys to strings to avoid symbol comparison issues header = deep_stringify_keys(header) payload = deep_stringify_keys(payload) # Configure JOSE to use ES256 for signing signing_config = { "alg" => "ES256" } # Merge our header with JOSE's required fields full_header = header.merge(signing_config) # Convert payload to JSON string before signing payload_json = JSON.generate(payload) # Create the JWS with our header and payload jws = @keypair.sign(payload_json, full_header) # Get the compact serialization jws.compact rescue StandardError => e raise KeyError, "Failed to sign segments: #{e.}" end |
#to_jwk(include_private: false) ⇒ Hash
Exports the keypair in JWK format
126 127 128 129 130 131 |
# File 'lib/atproto_auth/dpop/key_manager.rb', line 126 def to_jwk(include_private: false) key = include_private ? @keypair : @keypair.to_public key.to_map rescue StandardError => e raise KeyError, "Failed to export key: #{e.}" end |
#verify(signed_jws) ⇒ Boolean
Verifies a signed JWS
115 116 117 118 119 120 |
# File 'lib/atproto_auth/dpop/key_manager.rb', line 115 def verify(signed_jws) verified, _payload, = @keypair.verify(signed_jws) verified rescue StandardError => e raise KeyError, "Failed to verify signature: #{e.}" end |