lib/webauthn/fake_authenticator/authenticator_data.rb
# frozen_string_literal: true require "cose/key" require "cbor" require "securerandom" module WebAuthn class FakeAuthenticator class AuthenticatorData AAGUID = SecureRandom.random_bytes(16) attr_reader :sign_count def initialize( rp_id_hash:, credential: { id: SecureRandom.random_bytes(16), public_key: OpenSSL::PKey::EC.generate("prime256v1").public_key }, sign_count: 0, user_present: true, user_verified: !user_present, backup_eligibility: false, backup_state: false, aaguid: AAGUID, extensions: { "fakeExtension" => "fakeExtensionValue" } ) @rp_id_hash = rp_id_hash @credential = credential @sign_count = sign_count @user_present = user_present @user_verified = user_verified @backup_eligibility = backup_eligibility @backup_state = backup_state @aaguid = aaguid @extensions = extensions end def serialize rp_id_hash + flags + serialized_sign_count + attested_credential_data + extension_data end private attr_reader :rp_id_hash, :credential, :user_present, :user_verified, :extensions, :backup_eligibility, :backup_state def flags [ [ bit(:user_present), reserved_for_future_use_bit, bit(:user_verified), bit(:backup_eligibility), bit(:backup_state), reserved_for_future_use_bit, attested_credential_data_included_bit, extension_data_included_bit ].join ].pack("b*") end def serialized_sign_count [sign_count].pack('L>') end def attested_credential_data @attested_credential_data ||= if credential @aaguid + [credential[:id].length].pack("n*") + credential[:id] + cose_credential_public_key else "" end end def extension_data if extensions CBOR.encode(extensions) else "" end end def bit(flag) if context[flag] "1" else "0" end end def attested_credential_data_included_bit if attested_credential_data.empty? "0" else "1" end end def extension_data_included_bit if extension_data.empty? "0" else "1" end end def reserved_for_future_use_bit "0" end def context { user_present: user_present, user_verified: user_verified, backup_eligibility: backup_eligibility, backup_state: backup_state } end def cose_credential_public_key case credential[:public_key] when OpenSSL::PKey::RSA key = COSE::Key::RSA.from_pkey(credential[:public_key]) key.alg = -257 when OpenSSL::PKey::EC::Point alg = { COSE::Key::Curve.by_name("P-256").id => -7, COSE::Key::Curve.by_name("P-384").id => -35, COSE::Key::Curve.by_name("P-521").id => -36 } key = COSE::Key::EC2.from_pkey(credential[:public_key]) key.alg = alg[key.crv] when OpenSSL::PKey::PKey key = COSE::Key::OKP.from_pkey(credential[:public_key]) key.alg = -8 end key.serialize end def key_bytes(public_key) public_key.to_bn.to_s(2) end end end end