From 80b3667dfa6ce47749d2f6d9b41fa38dcf1e3ca8 Mon Sep 17 00:00:00 2001 From: Sorah Fukumori Date: Mon, 1 Oct 2018 12:34:21 +0900 Subject: [PATCH 1/2] feat: Parse signCount on AuthenticatorData To allow RP to check signature count mismatch https://www.w3.org/TR/2018/CR-webauthn-20180807/#sign-counter --- lib/webauthn/authenticator_data.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/webauthn/authenticator_data.rb b/lib/webauthn/authenticator_data.rb index 08e0c3a6..e8b8bf3d 100644 --- a/lib/webauthn/authenticator_data.rb +++ b/lib/webauthn/authenticator_data.rb @@ -10,6 +10,8 @@ class AuthenticatorData FLAGS_LENGTH = 1 SIGN_COUNT_LENGTH = 4 + SIGN_COUNT_POSITION = RP_ID_HASH_LENGTH + FLAGS_LENGTH + USER_PRESENT_FLAG_POSITION = 0 ATTESTED_CREDENTIAL_DATA_INCLUDED_FLAG_POSITION = 6 @@ -40,6 +42,10 @@ def credential attested_credential_data.credential end + def sign_count + @sign_count ||= data_at(SIGN_COUNT_POSITION, SIGN_COUNT_LENGTH).unpack1('L>') + end + private attr_reader :data From 509c6dc49418cc83832c85b1cdad00c500e4ecf7 Mon Sep 17 00:00:00 2001 From: Sorah Fukumori Date: Mon, 1 Oct 2018 12:56:28 +0900 Subject: [PATCH 2/2] test: Add unit test for AuthenticatorData --- spec/support/fake_authenticator.rb | 13 +++++---- spec/webauthn/authenticator_data_spec.rb | 36 ++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 6 deletions(-) create mode 100644 spec/webauthn/authenticator_data_spec.rb diff --git a/spec/support/fake_authenticator.rb b/spec/support/fake_authenticator.rb index 556a8e37..862cd50e 100644 --- a/spec/support/fake_authenticator.rb +++ b/spec/support/fake_authenticator.rb @@ -2,9 +2,10 @@ class FakeAuthenticator class Base - def initialize(challenge: fake_challenge, rp_id: "localhost", context: {}) + def initialize(challenge: fake_challenge, rp_id: "localhost", sign_count: 0, context: {}) @challenge = challenge @rp_id = rp_id + @sign_count = sign_count @context = context end @@ -24,14 +25,14 @@ def credential_id @credential_id ||= SecureRandom.random_bytes(16) end - private - - attr_reader :challenge, :context, :rp_id - def rp_id_hash OpenSSL::Digest::SHA256.digest(rp_id) end + private + + attr_reader :challenge, :context, :rp_id + def raw_flags ["#{user_present_bit}00000#{attested_credential_data_present_bit}0"].pack("b*") end @@ -49,7 +50,7 @@ def attested_credential_data end def raw_sign_count - "0000" + [@sign_count].pack('L>') end def user_present_bit diff --git a/spec/webauthn/authenticator_data_spec.rb b/spec/webauthn/authenticator_data_spec.rb new file mode 100644 index 00000000..36a3bade --- /dev/null +++ b/spec/webauthn/authenticator_data_spec.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +RSpec.describe WebAuthn::AuthenticatorData do + let(:authenticator) do + FakeAuthenticator::Base.new(rp_id: rp_id, sign_count: sign_count, context: { user_present: user_presence }) + end + + let(:rp_id) { "localhost" } + let(:sign_count) { 42 } + let(:user_presence) { true } + + let(:authenticator_data) { described_class.new(authenticator.authenticator_data) } + + describe "#rp_id_hash" do + subject { authenticator_data.rp_id_hash } + it { is_expected.to eq(authenticator.rp_id_hash) } + end + + describe "#sign_count" do + subject { authenticator_data.sign_count } + it { is_expected.to eq(42) } + end + + describe "#user_present?" do + subject { authenticator_data.user_present? } + context "when UP flag is set" do + let(:user_presence) { true } + it { is_expected.to be_truthy } + end + + context "when UP flag is not set" do + let(:user_presence) { false } + it { is_expected.to be_falsy } + end + end +end