1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 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 75 76 77 78
|
# frozen_string_literal: true
require "bindata"
require "cose/key"
require "webauthn/error"
module WebAuthn
class AttestedCredentialDataFormatError < WebAuthn::Error; end
class AuthenticatorData < BinData::Record
class AttestedCredentialData < BinData::Record
AAGUID_LENGTH = 16
ZEROED_AAGUID = 0.chr * AAGUID_LENGTH
ID_LENGTH_LENGTH = 2
endian :big
string :raw_aaguid, length: AAGUID_LENGTH
bit16 :id_length
string :id, read_length: :id_length
count_bytes_remaining :trailing_bytes_length
string :trailing_bytes, length: :trailing_bytes_length
# TODO: use keyword_init when we dropped Ruby 2.4 support
Credential =
Struct.new(:id, :public_key) do
def public_key_object
COSE::Key.deserialize(public_key).to_pkey
end
end
def self.deserialize(data)
read(data)
rescue EOFError
raise AttestedCredentialDataFormatError
end
def valid?
valid_credential_public_key?
end
def aaguid
raw_aaguid.unpack("H8H4H4H4H12").join("-")
end
def credential
@credential ||=
if valid?
Credential.new(id, public_key)
end
end
def length
if valid?
AAGUID_LENGTH + ID_LENGTH_LENGTH + id_length + public_key_length
end
end
private
def valid_credential_public_key?
cose_key = COSE::Key.deserialize(public_key)
!!cose_key.alg && WebAuthn.configuration.algorithms.include?(COSE::Algorithm.find(cose_key.alg).name)
end
def public_key
trailing_bytes[0..public_key_length - 1]
end
def public_key_length
@public_key_length ||=
CBOR.encode(CBOR::Unpacker.new(StringIO.new(trailing_bytes)).each.first).length
end
end
end
end
|