-
Notifications
You must be signed in to change notification settings - Fork 54
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor: create a class to deserialize stored public keys
* this will allow to keep the code isolated and more maintainable * at the same time, the WebAuthn::PublicKey.deserialize interface will be available for public use
- Loading branch information
Showing
2 changed files
with
115 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
# frozen_string_literal: true | ||
|
||
require "webauthn/attestation_statement/fido_u2f/public_key" | ||
require "cose/key" | ||
require "cose/key/ec2" | ||
require "cose/algorithm" | ||
|
||
module WebAuthn | ||
class PublicKey | ||
def self.deserialize(public_key) | ||
cose_key = if WebAuthn::AttestationStatement::FidoU2f::PublicKey.uncompressed_point?(public_key) | ||
# Gem version v1.11.0 and lower, used to behave so that Credential#public_key | ||
# returned an EC P-256 uncompressed point. | ||
# | ||
# Because of https://github.com/cedarcode/webauthn-ruby/issues/137 this was changed | ||
# and Credential#public_key started returning the unchanged COSE_Key formatted | ||
# credentialPublicKey (as in https://www.w3.org/TR/webauthn/#credentialpublickey). | ||
# | ||
# Given that the credential public key is expected to be stored long-term by the gem | ||
# user and later be passed as the public_key argument in the | ||
# AuthenticatorAssertionResponse.verify call, we then need to support the two formats. | ||
COSE::Key::EC2.new( | ||
alg: COSE::Algorithm.by_name("ES256").id, | ||
crv: 1, | ||
x: public_key[1..32], | ||
y: public_key[33..-1] | ||
) | ||
else | ||
COSE::Key.deserialize(public_key) | ||
end | ||
|
||
new(cose_key: cose_key) | ||
end | ||
|
||
attr_reader :cose_key | ||
|
||
def initialize(cose_key:) | ||
@cose_key = cose_key | ||
end | ||
|
||
def pkey | ||
@cose_key.to_pkey | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
# frozen_string_literal: true | ||
|
||
require "spec_helper" | ||
|
||
require "webauthn/public_key" | ||
require "support/seeds" | ||
require "cose" | ||
require "openssl" | ||
|
||
RSpec.describe "PublicKey" do | ||
let(:uncompressed_point_public_key) do | ||
Base64.strict_decode64(seeds[:u2f_migration][:stored_credential][:public_key]) | ||
end | ||
let(:cose_public_key) do | ||
Base64.urlsafe_decode64( | ||
"pQECAyYgASFYIPJKd_-Rl0QtQwbLggjGC_EbUFIMriCkdc2yuaukkBuNIlggaBsBjCwnMzFL7OUGJNm4b-HVpFNUa_NbsHGARuYKHfU" | ||
) | ||
end | ||
let(:webauthn_public_key) { WebAuthn::PublicKey.deserialize(public_key) } | ||
|
||
describe ".deserialize" do | ||
context "when invalid public key" do | ||
let(:public_key) { 'invalidinvalid' } | ||
|
||
it "should fail" do | ||
expect { webauthn_public_key }.to raise_error(CBOR::MalformedFormatError) | ||
end | ||
end | ||
end | ||
|
||
describe "#pkey" do | ||
let(:pkey) { webauthn_public_key.pkey } | ||
|
||
context "when public key stored in uncompressed point format" do | ||
let(:public_key) { uncompressed_point_public_key } | ||
|
||
it "should return ssl pkey" do | ||
expect(pkey).to be_instance_of(OpenSSL::PKey::EC) | ||
end | ||
end | ||
|
||
context "when public key stored in cose format" do | ||
let(:public_key) { cose_public_key } | ||
|
||
it "should return ssl pkey" do | ||
expect(pkey).to be_instance_of(OpenSSL::PKey::EC) | ||
end | ||
end | ||
end | ||
|
||
describe "#cose_key" do | ||
let(:cose_key) { webauthn_public_key.cose_key } | ||
|
||
context "when public key stored in uncompressed point format" do | ||
let(:public_key) { uncompressed_point_public_key } | ||
|
||
it "should return EC2 cose key" do | ||
expect(cose_key).to be_instance_of(COSE::Key::EC2) | ||
end | ||
end | ||
|
||
context "when public key stored in cose format" do | ||
let(:public_key) { cose_public_key } | ||
|
||
it "should return cose key" do | ||
expect(cose_key).to be_a(COSE::Key::Base) | ||
end | ||
end | ||
end | ||
end |