Class: IosAppAttest::Validators::ChallengeValidator
- Inherits:
-
BaseValidator
- Object
- BaseValidator
- IosAppAttest::Validators::ChallengeValidator
- Defined in:
- lib/ios_app_attest/validators/challenge_validator.rb
Overview
Validates challenge nonce and related aspects of the attestation
This validator is responsible for verifying the challenge nonce used in the attestation process. It validates that the nonce is valid, not expired, and matches the expected value. It also verifies the key ID against the certificate’s public key and provides methods for decrypting challenges.
Instance Attribute Summary collapse
-
#redis_client ⇒ Object
readonly
Returns the value of attribute redis_client.
Attributes inherited from BaseValidator
Instance Method Summary collapse
-
#decrypt_challenge(challenge, iv) ⇒ String
Decrypt challenge using AES-256-CBC.
-
#initialize(config, redis_client: nil, logger: nil) ⇒ ChallengeValidator
constructor
Initialize the challenge validator.
-
#validate_challenge(cred_cert, challenge_decrypted, auth_data) ⇒ Object
Verify challenge nonce from certificate.
-
#validate_key_id(cred_cert, key_id) ⇒ Object
Validate the key ID matches the certificate’s public key.
-
#validate_nonce(challenge_id, challenge_decrypted) ⇒ Object
Validate the challenge nonce against stored value.
Constructor Details
#initialize(config, redis_client: nil, logger: nil) ⇒ ChallengeValidator
Initialize the challenge validator
This initializes the validator with configuration and optional Redis client. The Redis client is used to store and retrieve nonces for verification. If no Redis client is provided, nonce verification will be skipped.
29 30 31 32 |
# File 'lib/ios_app_attest/validators/challenge_validator.rb', line 29 def initialize(config, redis_client: nil, logger: nil) super(config, logger: logger) @redis_client = redis_client end |
Instance Attribute Details
#redis_client ⇒ Object (readonly)
Returns the value of attribute redis_client.
18 19 20 |
# File 'lib/ios_app_attest/validators/challenge_validator.rb', line 18 def redis_client @redis_client end |
Instance Method Details
#decrypt_challenge(challenge, iv) ⇒ String
Decrypt challenge using AES-256-CBC
This method decrypts the challenge nonce using AES-256-CBC encryption with the provided initialization vector and the encryption key from configuration.
108 109 110 111 112 113 114 |
# File 'lib/ios_app_attest/validators/challenge_validator.rb', line 108 def decrypt_challenge(challenge, iv) cipher = OpenSSL::Cipher::AES256.new(:CBC) cipher.decrypt cipher.key = encryption_key cipher.iv = iv cipher.update(challenge) + cipher.final end |
#validate_challenge(cred_cert, challenge_decrypted, auth_data) ⇒ Object
Verify challenge nonce from certificate
This method verifies that the challenge nonce was correctly incorporated into the attestation by checking that the certificate contains a hash derived from the authentication data and challenge nonce. This ensures the attestation was created specifically for this challenge.
69 70 71 72 73 74 75 76 77 78 79 80 |
# File 'lib/ios_app_attest/validators/challenge_validator.rb', line 69 def validate_challenge(cred_cert, challenge_decrypted, auth_data) challenge_hash = sha256.digest(challenge_decrypted) extension = cred_cert.extensions.find { |e| e.oid == app_attest_oid } sequence = OpenSSL::ASN1.decode(OpenSSL::ASN1.decode(extension.to_der).value[1].value) to_verify = sequence.value[0].value[0].value expected_hash = sha256.digest(auth_data + challenge_hash) unless to_verify == expected_hash raise IosAppAttest::ChallengeError, 'Challenge verification failed' end end |
#validate_key_id(cred_cert, key_id) ⇒ Object
Validate the key ID matches the certificate’s public key
This method verifies that the key ID provided in the attestation parameters matches the hash of the public key from the credential certificate. This ensures the attestation is using the correct key pair.
91 92 93 94 95 96 97 98 |
# File 'lib/ios_app_attest/validators/challenge_validator.rb', line 91 def validate_key_id(cred_cert, key_id) uncompressed_point_key = cred_cert.public_key.public_key.to_octet_string(:uncompressed) expected_key_id = Base64.strict_encode64(sha256.digest(uncompressed_point_key)) unless key_id == expected_key_id raise IosAppAttest::ChallengeError, 'Key ID verification failed' end end |
#validate_nonce(challenge_id, challenge_decrypted) ⇒ Object
This method requires a Redis client to be provided during initialization. If no Redis client is available, this validation is skipped.
Validate the challenge nonce against stored value
This method verifies that the provided challenge nonce matches the one previously stored in Redis. After successful validation, the nonce is deleted from Redis to prevent replay attacks.
46 47 48 49 50 51 52 53 54 55 56 |
# File 'lib/ios_app_attest/validators/challenge_validator.rb', line 46 def validate_nonce(challenge_id, challenge_decrypted) return unless redis_client nonce = redis_client.get("nonce:#{challenge_id}") unless nonce && nonce == encode_base64(challenge_decrypted) raise IosAppAttest::ChallengeError, "Invalid or expired challenge nonce" end # Delete the nonce after successful validation to prevent replay attacks redis_client.del("nonce:#{challenge_id}") end |