Class: Darrrr::AccountProvider
- Inherits:
-
Object
- Object
- Darrrr::AccountProvider
- Includes:
- CryptoHelper, Provider
- Defined in:
- lib/darrrr/account_provider.rb
Constant Summary collapse
- PRIVATE_FIELDS =
Only applicable when acting as a recovery provider
[:symmetric_key, :signing_private_key]
- FIELDS =
[:tokensign_pubkeys_secp256r1].freeze
- URL_FIELDS =
[:issuer, :save_token_return, :recover_account_return, :privacy_policy, :icon_152px].freeze
- REQUIRED_FIELDS =
These are the fields required by the spec
FIELDS + URL_FIELDS
Constants included from Provider
Provider::MAX_RECOVERY_PROVIDER_CACHE_LENGTH, Provider::RECOVERY_PROVIDER_CACHE_LENGTH, Provider::REQUIRED_CRYPTO_OPS
Constants included from Constants
Constants::CLOCK_SKEW, Constants::COUNTERSIGNED_RECOVERY_TOKEN_TYPE, Constants::DIGEST, Constants::GROUP, Constants::PRIME_256_V1, Constants::PROTOCOL_VERSION, Constants::RECOVERY_TOKEN_TYPE, Constants::TOKEN_ID_BYTE_LENGTH, Constants::WELL_KNOWN_CONFIG_PATH
Instance Method Summary collapse
-
#dangerous_unverified_recovery_token(countersigned_token) ⇒ Object
Parses a countersigned_token and returns the nested recovery token WITHOUT verifying any signatures.
- #encryptor_key ⇒ Object
-
#generate_recovery_token(data:, audience:, context: nil, options: 0x00) ⇒ Object
Generates a binary token with an encrypted arbitrary data payload.
-
#to_h ⇒ Object
Used to serve content at /.well-known/delegated-account-recovery/configuration.
-
#unseal_keys(context = nil) ⇒ Object
The CryptoHelper defines an ‘unseal` method that requires us to define a `unseal_keys` method that will return the set of keys that are valid when verifying the signature on a sealed key.
-
#validate_countersigned_recovery_token!(countersigned_token, context = {}) ⇒ Object
Validates the countersigned recovery token by verifying the signature of the countersigned token, parsing out the origin recovery token, verifying the signature on the recovery token, and finally decrypting the data in the origin recovery token.
Methods included from Provider
#custom_encryptor=, #encryptor, included, #initialize, #load, #with_encryptor
Methods included from CryptoHelper
Instance Method Details
#dangerous_unverified_recovery_token(countersigned_token) ⇒ Object
Parses a countersigned_token and returns the nested recovery token WITHOUT verifying any signatures. This should only be used if no user context can be identified or if we’re extracting issuer information.
69 70 71 72 |
# File 'lib/darrrr/account_provider.rb', line 69 def dangerous_unverified_recovery_token(countersigned_token) parsed_countersigned_token = RecoveryToken.parse(Base64.strict_decode64(countersigned_token)) RecoveryToken.parse(parsed_countersigned_token.data) end |
#encryptor_key ⇒ Object
74 75 76 |
# File 'lib/darrrr/account_provider.rb', line 74 def encryptor_key :darrrr_account_provider_encryptor end |
#generate_recovery_token(data:, audience:, context: nil, options: 0x00) ⇒ Object
Generates a binary token with an encrypted arbitrary data payload.
data: value to encrypt in the token provider: the recovery provider/audience of the token context: arbitrary data passed on to underlying crypto operations options: the value to set for the options byte
returns a [RecoveryToken, b64 encoded sealed_token] tuple
59 60 61 62 63 64 |
# File 'lib/darrrr/account_provider.rb', line 59 def generate_recovery_token(data:, audience:, context: nil, options: 0x00) token = RecoveryToken.build(issuer: self, audience: audience, type: RECOVERY_TOKEN_TYPE, options: ) token.data = self.encryptor.encrypt(data, self, context) [token, seal(token, context)] end |
#to_h ⇒ Object
Used to serve content at /.well-known/delegated-account-recovery/configuration
40 41 42 43 44 45 46 47 48 49 |
# File 'lib/darrrr/account_provider.rb', line 40 def to_h { "issuer" => self.issuer, "tokensign-pubkeys-secp256r1" => self.unseal_keys.dup, "save-token-return" => self.save_token_return, "recover-account-return" => self.recover_account_return, "privacy-policy" => self.privacy_policy, "icon-152px" => self.icon_152px } end |
#unseal_keys(context = nil) ⇒ Object
The CryptoHelper defines an ‘unseal` method that requires us to define a `unseal_keys` method that will return the set of keys that are valid when verifying the signature on a sealed key.
returns the value of ‘tokensign_pubkeys_secp256r1` or executes a proc passing `self` as the first argument.
31 32 33 34 35 36 37 |
# File 'lib/darrrr/account_provider.rb', line 31 def unseal_keys(context = nil) if @tokensign_pubkeys_secp256r1.respond_to?(:call) @tokensign_pubkeys_secp256r1.call(context) else @tokensign_pubkeys_secp256r1 end end |
#validate_countersigned_recovery_token!(countersigned_token, context = {}) ⇒ Object
Validates the countersigned recovery token by verifying the signature of the countersigned token, parsing out the origin recovery token, verifying the signature on the recovery token, and finally decrypting the data in the origin recovery token.
countersigned_token: our original recovery token wrapped in recovery token instance that is signed by the recovery provider. context: arbitrary data to be passed to Provider#unseal.
returns a verified recovery token or raises an error if the token fails validation.
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
# File 'lib/darrrr/account_provider.rb', line 89 def validate_countersigned_recovery_token!(countersigned_token, context = {}) # 5. Validate the the issuer field is present in the token, # and that it matches the audience field in the original countersigned token. begin recovery_provider = RecoveryToken.recovery_provider_issuer(Base64.strict_decode64(countersigned_token)) rescue RecoveryTokenSerializationError => e raise CountersignedTokenError.new("Countersigned token is invalid: " + e., :countersigned_token_parse_error) rescue UnknownProviderError => e raise CountersignedTokenError.new(e., :recovery_token_invalid_issuer) end # 1. Parse the countersigned-token. # 2. Validate that the version field is 0. # 7. Retrieve the current Recovery Provider configuration as described in Section 2. # 8. Validate that the counter-signed token signature validates with a current element of the countersign-pubkeys-secp256r1 array. begin parsed_countersigned_token = recovery_provider.unseal(Base64.strict_decode64(countersigned_token), context) rescue TokenFormatError => e raise CountersignedTokenError.new(e., :countersigned_invalid_token_version) rescue CryptoError raise CountersignedTokenError.new("Countersigned token has an invalid signature", :countersigned_invalid_signature) end # 3. De-serialize the original recovery token from the data field. # 4. Validate the signature on the original recovery token. begin recovery_token = self.unseal(parsed_countersigned_token.data, context) rescue RecoveryTokenSerializationError => e raise CountersignedTokenError.new("Nested recovery token is invalid: " + e., :recovery_token_token_parse_error) rescue TokenFormatError => e raise CountersignedTokenError.new("Nested recovery token format error: #{e.}", :recovery_token_invalid_token_type) rescue CryptoError raise CountersignedTokenError.new("Nested recovery token has an invalid signature", :recovery_token_invalid_signature) end # 5. Validate the the issuer field is present in the countersigned-token, # and that it matches the audience field in the original token. countersigned_token_issuer = parsed_countersigned_token.issuer if countersigned_token_issuer.blank? || countersigned_token_issuer != recovery_token.audience || recovery_provider.origin != countersigned_token_issuer raise CountersignedTokenError.new("Validate the the issuer field is present in the countersigned-token, and that it matches the audience field in the original token", :recovery_token_invalid_issuer) end # 6. Validate the token binding for the countersigned token, if present. # (the token binding for the inner token is not relevant) # TODO not required, to be implemented later # 9. Decrypt the data field from the original recovery token and parse its information, if present. # no decryption here is attempted. Attempts to call `decode` will just fail. # 10. Apply any additional processing which provider-specific data in the opaque data portion may indicate is necessary. begin if DateTime.parse(parsed_countersigned_token.issued_time).utc < (Time.now - CLOCK_SKEW).utc raise CountersignedTokenError.new("Countersigned recovery token issued at time is too far in the past", :stale_token) end rescue ArgumentError raise CountersignedTokenError.new("Invalid countersigned token issued time", :invalid_issued_time) end recovery_token end |