Class: Unmagic::Passkeys::WebAuthn::Authenticator::Data
- Inherits:
-
Object
- Object
- Unmagic::Passkeys::WebAuthn::Authenticator::Data
- Defined in:
- lib/unmagic/passkeys/web_authn/authenticator/data.rb
Overview
Action Pack WebAuthn Authenticator Data
Decodes and represents the authenticator data structure from WebAuthn responses. This binary format contains information about the authenticator and, during registration, the newly created credential.
Structure
The authenticator data consists of:
-
RP ID Hash (32 bytes) - SHA-256 hash of the relying party ID
-
Flags (1 byte) - Bit flags for user presence, verification, etc.
-
Sign Count (4 bytes) - Signature counter for replay detection
-
Attested Credential Data (variable) - Present only during registration
Usage
data = Unmagic::Passkeys::WebAuthn::Authenticator::Data.decode(bytes)
data.user_present? # => true
data.user_verified? # => true
data.sign_count # => 42
data.credential_id # => "abc123..." (registration only)
data.public_key # => OpenSSL::PKey::EC (registration only)
Flags
user_present?-
Returns true if the user performed a test of user presence (e.g., touched the authenticator).
user_verified?-
Returns true if the user was verified through biometrics, PIN, or other method. This is stronger than mere presence.
backup_eligible?-
Returns true if the credential can be backed up (e.g., synced passkeys from Apple, Google, or Microsoft). Indicates multi-device credential support.
backed_up?-
Returns true if the credential is currently backed up to cloud storage. Useful for risk assessment—backed-up credentials may be accessible from multiple devices.
Constant Summary collapse
- RELYING_PARTY_ID_HASH_LENGTH =
Segment lengths
32- FLAGS_LENGTH =
1- SIGN_COUNT_LENGTH =
4- AAGUID_LENGTH =
16- CREDENTIAL_ID_LENGTH_BYTES =
2- USER_PRESENT_FLAG =
Flags
0x01- USER_VERIFIED_FLAG =
0x04- BACKUP_ELIGIBLE_FLAG =
0x08- BACKUP_STATE_FLAG =
0x10- ATTESTED_CREDENTIAL_DATA_FLAG =
0x40
Instance Attribute Summary collapse
-
#aaguid ⇒ Object
readonly
Returns the value of attribute aaguid.
-
#bytes ⇒ Object
readonly
Returns the value of attribute bytes.
-
#credential_id ⇒ Object
readonly
Returns the value of attribute credential_id.
-
#flags ⇒ Object
readonly
Returns the value of attribute flags.
-
#public_key_bytes ⇒ Object
readonly
Returns the value of attribute public_key_bytes.
-
#relying_party_id_hash ⇒ Object
readonly
Returns the value of attribute relying_party_id_hash.
-
#sign_count ⇒ Object
readonly
Returns the value of attribute sign_count.
Class Method Summary collapse
-
.decode(bytes) ⇒ Object
Decodes raw authenticator data bytes into a Data instance, parsing the RP ID hash, flags, sign count, and (if present) attested credential data.
-
.wrap(data) ⇒ Object
Wraps raw authenticator data into a Data instance.
Instance Method Summary collapse
-
#backed_up? ⇒ Boolean
Returns true if the credential is currently backed up to cloud storage.
-
#backup_eligible? ⇒ Boolean
Returns true if the credential is eligible for backup (e.g., synced passkey).
-
#initialize(bytes:, relying_party_id_hash:, flags:, sign_count:, aaguid: nil, credential_id:, public_key_bytes:) ⇒ Data
constructor
A new instance of Data.
-
#public_key ⇒ Object
Decodes the COSE public key bytes into an OpenSSL key object.
-
#user_present? ⇒ Boolean
Returns true if the user performed a test of presence (e.g., touched the authenticator).
-
#user_verified? ⇒ Boolean
Returns true if the user was verified via biometrics, PIN, or similar.
Constructor Details
#initialize(bytes:, relying_party_id_hash:, flags:, sign_count:, aaguid: nil, credential_id:, public_key_bytes:) ⇒ Data
Returns a new instance of Data.
135 136 137 138 139 140 141 142 143 |
# File 'lib/unmagic/passkeys/web_authn/authenticator/data.rb', line 135 def initialize(bytes:, relying_party_id_hash:, flags:, sign_count:, aaguid: nil, credential_id:, public_key_bytes:) @bytes = bytes @relying_party_id_hash = @flags = flags @sign_count = sign_count @aaguid = aaguid @credential_id = credential_id @public_key_bytes = public_key_bytes end |
Instance Attribute Details
#aaguid ⇒ Object (readonly)
Returns the value of attribute aaguid.
60 61 62 |
# File 'lib/unmagic/passkeys/web_authn/authenticator/data.rb', line 60 def aaguid @aaguid end |
#bytes ⇒ Object (readonly)
Returns the value of attribute bytes.
60 61 62 |
# File 'lib/unmagic/passkeys/web_authn/authenticator/data.rb', line 60 def bytes @bytes end |
#credential_id ⇒ Object (readonly)
Returns the value of attribute credential_id.
60 61 62 |
# File 'lib/unmagic/passkeys/web_authn/authenticator/data.rb', line 60 def credential_id @credential_id end |
#flags ⇒ Object (readonly)
Returns the value of attribute flags.
60 61 62 |
# File 'lib/unmagic/passkeys/web_authn/authenticator/data.rb', line 60 def flags @flags end |
#public_key_bytes ⇒ Object (readonly)
Returns the value of attribute public_key_bytes.
60 61 62 |
# File 'lib/unmagic/passkeys/web_authn/authenticator/data.rb', line 60 def public_key_bytes @public_key_bytes end |
#relying_party_id_hash ⇒ Object (readonly)
Returns the value of attribute relying_party_id_hash.
60 61 62 |
# File 'lib/unmagic/passkeys/web_authn/authenticator/data.rb', line 60 def @relying_party_id_hash end |
#sign_count ⇒ Object (readonly)
Returns the value of attribute sign_count.
60 61 62 |
# File 'lib/unmagic/passkeys/web_authn/authenticator/data.rb', line 60 def sign_count @sign_count end |
Class Method Details
.decode(bytes) ⇒ Object
Decodes raw authenticator data bytes into a Data instance, parsing the RP ID hash, flags, sign count, and (if present) attested credential data.
78 79 80 81 82 83 84 85 86 87 88 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 |
# File 'lib/unmagic/passkeys/web_authn/authenticator/data.rb', line 78 def decode(bytes) bytes = bytes.bytes if bytes.is_a?(String) minimum_length = RELYING_PARTY_ID_HASH_LENGTH + FLAGS_LENGTH + SIGN_COUNT_LENGTH if bytes.length < minimum_length raise Unmagic::Passkeys::WebAuthn::InvalidResponseError, "Authenticator data is too short" end position = 0 = bytes[position, RELYING_PARTY_ID_HASH_LENGTH].pack("C*") position += RELYING_PARTY_ID_HASH_LENGTH flags = bytes[position] position += FLAGS_LENGTH sign_count = bytes[position, SIGN_COUNT_LENGTH].pack("C*").unpack1("N") position += SIGN_COUNT_LENGTH aaguid = nil credential_id = nil public_key_bytes = nil if flags & ATTESTED_CREDENTIAL_DATA_FLAG != 0 if bytes.length < position + AAGUID_LENGTH + CREDENTIAL_ID_LENGTH_BYTES raise Unmagic::Passkeys::WebAuthn::InvalidResponseError, "Authenticator data is too short for attested credential data" end aaguid_bytes = bytes[position, AAGUID_LENGTH].pack("C*") aaguid = aaguid_bytes.unpack("H8H4H4H4H12").join("-") position += AAGUID_LENGTH credential_id_length = bytes[position, CREDENTIAL_ID_LENGTH_BYTES].pack("C*").unpack1("n") position += CREDENTIAL_ID_LENGTH_BYTES if bytes.length < position + credential_id_length + 1 raise Unmagic::Passkeys::WebAuthn::InvalidResponseError, "Authenticator data is too short for credential ID and public key" end credential_id = Base64.urlsafe_encode64(bytes[position, credential_id_length].pack("C*"), padding: false) position += credential_id_length public_key_bytes = bytes[position..].pack("C*") end new( bytes: bytes, relying_party_id_hash: , flags: flags, sign_count: sign_count, aaguid: aaguid, credential_id: credential_id, public_key_bytes: public_key_bytes ) end |
.wrap(data) ⇒ Object
Wraps raw authenticator data into a Data instance. Accepts an existing Data object (returned as-is), a Base64URL-encoded string, or raw binary.
65 66 67 68 69 70 71 72 73 74 |
# File 'lib/unmagic/passkeys/web_authn/authenticator/data.rb', line 65 def wrap(data) if data.is_a?(self) data else data = Base64.urlsafe_decode64(data) unless data.encoding == Encoding::BINARY decode(data) end rescue ArgumentError raise Unmagic::Passkeys::WebAuthn::InvalidResponseError, "Invalid base64 encoding in authenticator data" end |
Instance Method Details
#backed_up? ⇒ Boolean
Returns true if the credential is currently backed up to cloud storage. Only meaningful when backup_eligible? is true.
164 165 166 |
# File 'lib/unmagic/passkeys/web_authn/authenticator/data.rb', line 164 def backed_up? flags & BACKUP_STATE_FLAG != 0 end |
#backup_eligible? ⇒ Boolean
Returns true if the credential is eligible for backup (e.g., synced passkey). This indicates the authenticator supports multi-device credentials.
158 159 160 |
# File 'lib/unmagic/passkeys/web_authn/authenticator/data.rb', line 158 def backup_eligible? flags & BACKUP_ELIGIBLE_FLAG != 0 end |
#public_key ⇒ Object
Decodes the COSE public key bytes into an OpenSSL key object. Returns nil when no attested credential data is present (authentication responses).
171 172 173 |
# File 'lib/unmagic/passkeys/web_authn/authenticator/data.rb', line 171 def public_key @public_key ||= Unmagic::Passkeys::WebAuthn::CoseKey.decode(public_key_bytes).to_openssl_key if public_key_bytes end |
#user_present? ⇒ Boolean
Returns true if the user performed a test of presence (e.g., touched the authenticator).
147 148 149 |
# File 'lib/unmagic/passkeys/web_authn/authenticator/data.rb', line 147 def user_present? flags & USER_PRESENT_FLAG != 0 end |
#user_verified? ⇒ Boolean
Returns true if the user was verified via biometrics, PIN, or similar.
152 153 154 |
# File 'lib/unmagic/passkeys/web_authn/authenticator/data.rb', line 152 def user_verified? flags & USER_VERIFIED_FLAG != 0 end |