webauthn 2.1.0 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/dependabot.yml +6 -0
- data/.github/workflows/build.yml +50 -0
- data/.github/workflows/git.yml +21 -0
- data/.rubocop.yml +121 -13
- data/CHANGELOG.md +169 -0
- data/CONTRIBUTING.md +0 -5
- data/README.md +80 -14
- data/SECURITY.md +7 -4
- data/docs/advanced_configuration.md +174 -0
- data/docs/u2f_migration.md +14 -20
- data/lib/cose/rsapkcs1_algorithm.rb +50 -0
- data/lib/webauthn/attestation_object.rb +47 -0
- data/lib/webauthn/attestation_statement/android_key.rb +27 -33
- data/lib/webauthn/attestation_statement/android_safetynet.rb +27 -11
- data/lib/webauthn/attestation_statement/apple.rb +65 -0
- data/lib/webauthn/attestation_statement/base.rb +114 -21
- data/lib/webauthn/attestation_statement/fido_u2f.rb +8 -6
- data/lib/webauthn/attestation_statement/none.rb +7 -1
- data/lib/webauthn/attestation_statement/packed.rb +14 -42
- data/lib/webauthn/attestation_statement/tpm.rb +38 -75
- data/lib/webauthn/attestation_statement.rb +24 -21
- data/lib/webauthn/authenticator_assertion_response.rb +22 -11
- data/lib/webauthn/authenticator_attestation_response.rb +31 -92
- data/lib/webauthn/authenticator_data/attested_credential_data.rb +33 -49
- data/lib/webauthn/authenticator_data.rb +59 -51
- data/lib/webauthn/authenticator_response.rb +24 -11
- data/lib/webauthn/client_data.rb +4 -6
- data/lib/webauthn/configuration.rb +38 -40
- data/lib/webauthn/credential.rb +4 -4
- data/lib/webauthn/credential_creation_options.rb +2 -0
- data/lib/webauthn/credential_request_options.rb +2 -0
- data/lib/webauthn/encoder.rb +13 -4
- data/lib/webauthn/fake_authenticator/attestation_object.rb +25 -4
- data/lib/webauthn/fake_authenticator/authenticator_data.rb +25 -10
- data/lib/webauthn/fake_authenticator.rb +49 -8
- data/lib/webauthn/fake_client.rb +41 -8
- data/lib/webauthn/json_serializer.rb +45 -0
- data/lib/webauthn/public_key.rb +21 -2
- data/lib/webauthn/public_key_credential/creation_options.rb +3 -3
- data/lib/webauthn/public_key_credential/entity.rb +5 -28
- data/lib/webauthn/public_key_credential/options.rb +11 -32
- data/lib/webauthn/public_key_credential/request_options.rb +11 -1
- data/lib/webauthn/public_key_credential.rb +52 -8
- data/lib/webauthn/public_key_credential_with_assertion.rb +16 -2
- data/lib/webauthn/public_key_credential_with_attestation.rb +2 -2
- data/lib/webauthn/relying_party.rb +137 -0
- data/lib/webauthn/u2f_migrator.rb +8 -4
- data/lib/webauthn/version.rb +1 -1
- data/lib/webauthn.rb +1 -0
- data/webauthn.gemspec +15 -12
- metadata +56 -60
- data/.travis.yml +0 -36
- data/Appraisals +0 -17
- data/gemfiles/cose_head.gemfile +0 -7
- data/gemfiles/openssl_2_0.gemfile +0 -7
- data/gemfiles/openssl_2_1.gemfile +0 -7
- data/gemfiles/openssl_head.gemfile +0 -7
- data/lib/android_safetynet/attestation_response.rb +0 -116
- data/lib/cose/rsassa_algorithm.rb +0 -10
- data/lib/tpm/constants.rb +0 -44
- data/lib/tpm/s_attest/s_certify_info.rb +0 -14
- data/lib/tpm/s_attest.rb +0 -26
- data/lib/tpm/sized_buffer.rb +0 -13
- data/lib/tpm/t_public/s_ecc_parms.rb +0 -17
- data/lib/tpm/t_public/s_rsa_parms.rb +0 -17
- data/lib/tpm/t_public.rb +0 -32
- data/lib/webauthn/attestation_statement/android_key/authorization_list.rb +0 -39
- data/lib/webauthn/attestation_statement/android_key/key_description.rb +0 -37
- data/lib/webauthn/attestation_statement/tpm/cert_info.rb +0 -44
- data/lib/webauthn/attestation_statement/tpm/pub_area.rb +0 -85
- data/lib/webauthn/security_utils.rb +0 -20
- data/lib/webauthn/signature_verifier.rb +0 -77
data/.travis.yml
DELETED
@@ -1,36 +0,0 @@
|
|
1
|
-
dist: bionic
|
2
|
-
language: ruby
|
3
|
-
cache: bundler
|
4
|
-
|
5
|
-
rvm:
|
6
|
-
- ruby-head
|
7
|
-
- 2.7.0
|
8
|
-
- 2.6.5
|
9
|
-
- 2.5.7
|
10
|
-
- 2.4.9
|
11
|
-
- 2.3.8
|
12
|
-
|
13
|
-
gemfile:
|
14
|
-
- gemfiles/cose_head.gemfile
|
15
|
-
- gemfiles/openssl_head.gemfile
|
16
|
-
- gemfiles/openssl_2_1.gemfile
|
17
|
-
- gemfiles/openssl_2_0.gemfile
|
18
|
-
|
19
|
-
matrix:
|
20
|
-
fast_finish: true
|
21
|
-
allow_failures:
|
22
|
-
- rvm: ruby-head
|
23
|
-
- gemfile: gemfiles/cose_head.gemfile
|
24
|
-
- gemfile: gemfiles/openssl_head.gemfile
|
25
|
-
|
26
|
-
addons:
|
27
|
-
apt:
|
28
|
-
packages:
|
29
|
-
- libfaketime
|
30
|
-
|
31
|
-
before_install:
|
32
|
-
- gem install bundler -v "~> 2.0"
|
33
|
-
|
34
|
-
before_script:
|
35
|
-
- export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1
|
36
|
-
- export FAKETIME_NO_CACHE=1
|
data/Appraisals
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
appraise "cose_head" do
|
4
|
-
gem "cose", git: "https://github.com/cedarcode/cose-ruby"
|
5
|
-
end
|
6
|
-
|
7
|
-
appraise "openssl_head" do
|
8
|
-
gem "openssl", git: "https://github.com/ruby/openssl"
|
9
|
-
end
|
10
|
-
|
11
|
-
appraise "openssl_2_1" do
|
12
|
-
gem "openssl", "~> 2.1.0"
|
13
|
-
end
|
14
|
-
|
15
|
-
appraise "openssl_2_0" do
|
16
|
-
gem "openssl", "~> 2.0.0"
|
17
|
-
end
|
data/gemfiles/cose_head.gemfile
DELETED
@@ -1,116 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "base64"
|
4
|
-
require "jwt"
|
5
|
-
require "webauthn/security_utils"
|
6
|
-
|
7
|
-
module AndroidSafetynet
|
8
|
-
# Decoupled from WebAuthn, candidate for extraction
|
9
|
-
# Reference: https://developer.android.com/training/safetynet/attestation.html
|
10
|
-
class AttestationResponse
|
11
|
-
class VerificationError < StandardError; end
|
12
|
-
class LeafCertificateSubjectError < VerificationError; end
|
13
|
-
class NonceMismatchError < VerificationError; end
|
14
|
-
class SignatureError < VerificationError; end
|
15
|
-
class ResponseMissingError < VerificationError; end
|
16
|
-
class TimestampError < VerificationError; end
|
17
|
-
class TrustworthinessError < VerificationError; end
|
18
|
-
|
19
|
-
CERTIRICATE_CHAIN_HEADER = "x5c"
|
20
|
-
VALID_SUBJECT_HOSTNAME = "attest.android.com"
|
21
|
-
HEADERS_POSITION = 1
|
22
|
-
PAYLOAD_POSITION = 0
|
23
|
-
LEEWAY = 60
|
24
|
-
|
25
|
-
# FIXME: Should probably be limited to only roots published by Google
|
26
|
-
# See https://github.com/cedarcode/webauthn-ruby/issues/160#issuecomment-487941201
|
27
|
-
@trust_store = OpenSSL::X509::Store.new.tap { |trust_store| trust_store.set_default_paths }
|
28
|
-
|
29
|
-
class << self
|
30
|
-
attr_accessor :trust_store
|
31
|
-
end
|
32
|
-
|
33
|
-
attr_reader :response
|
34
|
-
|
35
|
-
def initialize(response)
|
36
|
-
@response = response
|
37
|
-
end
|
38
|
-
|
39
|
-
def verify(nonce, trustworthiness: true)
|
40
|
-
if response
|
41
|
-
valid_nonce?(nonce) || raise(NonceMismatchError)
|
42
|
-
valid_attestation_domain? || raise(LeafCertificateSubjectError)
|
43
|
-
valid_signature? || raise(SignatureError)
|
44
|
-
valid_timestamp? || raise(TimestampError)
|
45
|
-
trustworthy? || raise(TrustworthinessError) if trustworthiness
|
46
|
-
|
47
|
-
true
|
48
|
-
else
|
49
|
-
raise(ResponseMissingError)
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
def cts_profile_match?
|
54
|
-
payload["ctsProfileMatch"]
|
55
|
-
end
|
56
|
-
|
57
|
-
def leaf_certificate
|
58
|
-
certificate_chain[0]
|
59
|
-
end
|
60
|
-
|
61
|
-
def certificate_chain
|
62
|
-
@certificate_chain ||= headers[CERTIRICATE_CHAIN_HEADER].map do |cert|
|
63
|
-
OpenSSL::X509::Certificate.new(Base64.strict_decode64(cert))
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
def valid_timestamp?
|
68
|
-
now = Time.now
|
69
|
-
Time.at((payload["timestampMs"] / 1000.0).round).between?(now - LEEWAY, now)
|
70
|
-
end
|
71
|
-
|
72
|
-
private
|
73
|
-
|
74
|
-
def valid_nonce?(nonce)
|
75
|
-
WebAuthn::SecurityUtils.secure_compare(payload["nonce"], nonce)
|
76
|
-
end
|
77
|
-
|
78
|
-
def valid_attestation_domain?
|
79
|
-
common_name = leaf_certificate&.subject&.to_a&.assoc('CN')
|
80
|
-
|
81
|
-
if common_name
|
82
|
-
common_name[1] == VALID_SUBJECT_HOSTNAME
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
def valid_signature?
|
87
|
-
JWT.decode(response, leaf_certificate.public_key, true, algorithms: ["ES256", "RS256"])
|
88
|
-
rescue JWT::VerificationError
|
89
|
-
false
|
90
|
-
end
|
91
|
-
|
92
|
-
def trustworthy?
|
93
|
-
!trust_store || trust_store.verify(leaf_certificate, signing_certificates)
|
94
|
-
end
|
95
|
-
|
96
|
-
def trust_store
|
97
|
-
self.class.trust_store
|
98
|
-
end
|
99
|
-
|
100
|
-
def signing_certificates
|
101
|
-
certificate_chain[1..-1]
|
102
|
-
end
|
103
|
-
|
104
|
-
def headers
|
105
|
-
jws_parts[HEADERS_POSITION]
|
106
|
-
end
|
107
|
-
|
108
|
-
def payload
|
109
|
-
jws_parts[PAYLOAD_POSITION]
|
110
|
-
end
|
111
|
-
|
112
|
-
def jws_parts
|
113
|
-
@jws_parts ||= JWT.decode(response, nil, false)
|
114
|
-
end
|
115
|
-
end
|
116
|
-
end
|
@@ -1,10 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "cose"
|
4
|
-
|
5
|
-
RSASSAAlgorithm = Struct.new(:id, :name, :hash_function, :kty)
|
6
|
-
|
7
|
-
COSE::Algorithm.register(RSASSAAlgorithm.new(-257, "RS256", "SHA256", COSE::Key::RSA::KTY_RSA))
|
8
|
-
COSE::Algorithm.register(RSASSAAlgorithm.new(-258, "RS384", "SHA384", COSE::Key::RSA::KTY_RSA))
|
9
|
-
COSE::Algorithm.register(RSASSAAlgorithm.new(-259, "RS512", "SHA512", COSE::Key::RSA::KTY_RSA))
|
10
|
-
COSE::Algorithm.register(RSASSAAlgorithm.new(-65535, "RS1", "SHA1", COSE::Key::RSA::KTY_RSA))
|
data/lib/tpm/constants.rb
DELETED
@@ -1,44 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module TPM
|
4
|
-
# Section 6 in https://trustedcomputinggroup.org/wp-content/uploads/TPM-Rev-2.0-Part-2-Structures-01.38.pdf
|
5
|
-
|
6
|
-
GENERATED_VALUE = 0xFF544347
|
7
|
-
|
8
|
-
ST_ATTEST_CERTIFY = 0x8017
|
9
|
-
|
10
|
-
# Algorithms
|
11
|
-
ALG_RSA = 0x0001
|
12
|
-
ALG_SHA1 = 0x0004
|
13
|
-
ALG_SHA256 = 0x000B
|
14
|
-
ALG_NULL = 0x0010
|
15
|
-
ALG_RSASSA = 0x0014
|
16
|
-
ALG_RSAPSS = 0x0016
|
17
|
-
ALG_ECDSA = 0x0018
|
18
|
-
ALG_ECC = 0x0023
|
19
|
-
|
20
|
-
# ECC curves
|
21
|
-
ECC_NIST_P256 = 0x0003
|
22
|
-
|
23
|
-
# https://trustedcomputinggroup.org/resource/vendor-id-registry/ section 2 "TPM Capabilities Vendor ID (CAP_VID)"
|
24
|
-
VENDOR_IDS = {
|
25
|
-
"id:414D4400" => "AMD",
|
26
|
-
"id:41544D4C" => "Atmel",
|
27
|
-
"id:4252434D" => "Broadcom",
|
28
|
-
"id:49424D00" => "IBM",
|
29
|
-
"id:49465800" => "Infineon",
|
30
|
-
"id:494E5443" => "Intel",
|
31
|
-
"id:4C454E00" => "Lenovo",
|
32
|
-
"id:4E534D20" => "National Semiconductor",
|
33
|
-
"id:4E545A00" => "Nationz",
|
34
|
-
"id:4E544300" => "Nuvoton Technology",
|
35
|
-
"id:51434F4D" => "Qualcomm",
|
36
|
-
"id:534D5343" => "SMSC",
|
37
|
-
"id:53544D20" => "ST Microelectronics",
|
38
|
-
"id:534D534E" => "Samsung",
|
39
|
-
"id:534E5300" => "Sinosun",
|
40
|
-
"id:54584E00" => "Texas Instruments",
|
41
|
-
"id:57454300" => "Winbond",
|
42
|
-
"id:524F4343" => "Fuzhou Rockchip",
|
43
|
-
}.freeze
|
44
|
-
end
|
@@ -1,14 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "bindata"
|
4
|
-
require "tpm/sized_buffer"
|
5
|
-
|
6
|
-
module TPM
|
7
|
-
class SAttest < BinData::Record
|
8
|
-
# Section 10.12.3 in https://trustedcomputinggroup.org/wp-content/uploads/TPM-Rev-2.0-Part-2-Structures-01.38.pdf
|
9
|
-
class SCertifyInfo < BinData::Record
|
10
|
-
sized_buffer :name
|
11
|
-
sized_buffer :qualified_name
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
data/lib/tpm/s_attest.rb
DELETED
@@ -1,26 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "bindata"
|
4
|
-
require "tpm/constants"
|
5
|
-
require "tpm/sized_buffer"
|
6
|
-
require "tpm/s_attest/s_certify_info"
|
7
|
-
|
8
|
-
module TPM
|
9
|
-
# Section 10.12.8 in https://trustedcomputinggroup.org/wp-content/uploads/TPM-Rev-2.0-Part-2-Structures-01.38.pdf
|
10
|
-
class SAttest < BinData::Record
|
11
|
-
endian :big
|
12
|
-
|
13
|
-
uint32 :magic
|
14
|
-
uint16 :attested_type
|
15
|
-
sized_buffer :qualified_signer
|
16
|
-
sized_buffer :extra_data
|
17
|
-
|
18
|
-
# s_clock_info :clock_info
|
19
|
-
# uint64 :firmware_version
|
20
|
-
skip length: 25
|
21
|
-
|
22
|
-
choice :attested, selection: :attested_type do
|
23
|
-
s_certify_info TPM::ST_ATTEST_CERTIFY
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
data/lib/tpm/sized_buffer.rb
DELETED
@@ -1,13 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "bindata"
|
4
|
-
|
5
|
-
module TPM
|
6
|
-
# Section 10.4 in https://trustedcomputinggroup.org/wp-content/uploads/TPM-Rev-2.0-Part-2-Structures-01.38.pdf
|
7
|
-
class SizedBuffer < BinData::Record
|
8
|
-
endian :big
|
9
|
-
|
10
|
-
uint16 :buffer_size, value: lambda { buffer.size }
|
11
|
-
string :buffer, read_length: :buffer_size
|
12
|
-
end
|
13
|
-
end
|
@@ -1,17 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "bindata"
|
4
|
-
|
5
|
-
module TPM
|
6
|
-
class TPublic < BinData::Record
|
7
|
-
# Section 12.2.3.6 in https://trustedcomputinggroup.org/wp-content/uploads/TPM-Rev-2.0-Part-2-Structures-01.38.pdf
|
8
|
-
class SEccParms < BinData::Record
|
9
|
-
endian :big
|
10
|
-
|
11
|
-
uint16 :symmetric
|
12
|
-
uint16 :scheme
|
13
|
-
uint16 :curve_id
|
14
|
-
uint16 :kdf
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
@@ -1,17 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "bindata"
|
4
|
-
|
5
|
-
module TPM
|
6
|
-
class TPublic < BinData::Record
|
7
|
-
# Section 12.2.3.5 in https://trustedcomputinggroup.org/wp-content/uploads/TPM-Rev-2.0-Part-2-Structures-01.38.pdf
|
8
|
-
class SRsaParms < BinData::Record
|
9
|
-
endian :big
|
10
|
-
|
11
|
-
uint16 :symmetric
|
12
|
-
uint16 :scheme
|
13
|
-
uint16 :key_bits
|
14
|
-
uint32 :exponent
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
data/lib/tpm/t_public.rb
DELETED
@@ -1,32 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "bindata"
|
4
|
-
require "tpm/constants"
|
5
|
-
require "tpm/sized_buffer"
|
6
|
-
require "tpm/t_public/s_ecc_parms"
|
7
|
-
require "tpm/t_public/s_rsa_parms"
|
8
|
-
|
9
|
-
module TPM
|
10
|
-
# Section 12.2.4 in https://trustedcomputinggroup.org/wp-content/uploads/TPM-Rev-2.0-Part-2-Structures-01.38.pdf
|
11
|
-
class TPublic < BinData::Record
|
12
|
-
endian :big
|
13
|
-
|
14
|
-
uint16 :alg_type
|
15
|
-
uint16 :name_alg
|
16
|
-
|
17
|
-
# :object_attributes
|
18
|
-
skip length: 4
|
19
|
-
|
20
|
-
sized_buffer :auth_policy
|
21
|
-
|
22
|
-
choice :parameters, selection: :alg_type do
|
23
|
-
s_ecc_parms TPM::ALG_ECC
|
24
|
-
s_rsa_parms TPM::ALG_RSA
|
25
|
-
end
|
26
|
-
|
27
|
-
choice :unique, selection: :alg_type do
|
28
|
-
sized_buffer TPM::ALG_ECC
|
29
|
-
sized_buffer TPM::ALG_RSA
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
@@ -1,39 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "webauthn/attestation_statement/base"
|
4
|
-
|
5
|
-
module WebAuthn
|
6
|
-
module AttestationStatement
|
7
|
-
class AndroidKey < Base
|
8
|
-
class AuthorizationList
|
9
|
-
PURPOSE_TAG = 1
|
10
|
-
ALL_APPLICATIONS_TAG = 600
|
11
|
-
ORIGIN_TAG = 702
|
12
|
-
|
13
|
-
def initialize(sequence)
|
14
|
-
@sequence = sequence
|
15
|
-
end
|
16
|
-
|
17
|
-
def purpose
|
18
|
-
find_by_tag(PURPOSE_TAG)&.value&.at(0)&.value&.at(0)&.value
|
19
|
-
end
|
20
|
-
|
21
|
-
def all_applications
|
22
|
-
find_by_tag(ALL_APPLICATIONS_TAG)&.value
|
23
|
-
end
|
24
|
-
|
25
|
-
def origin
|
26
|
-
find_by_tag(ORIGIN_TAG)&.value&.at(0)&.value
|
27
|
-
end
|
28
|
-
|
29
|
-
private
|
30
|
-
|
31
|
-
attr_reader :sequence
|
32
|
-
|
33
|
-
def find_by_tag(tag)
|
34
|
-
sequence.detect { |data| data.tag == tag }
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
@@ -1,37 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "webauthn/attestation_statement/android_key/authorization_list"
|
4
|
-
require "webauthn/attestation_statement/base"
|
5
|
-
|
6
|
-
module WebAuthn
|
7
|
-
module AttestationStatement
|
8
|
-
class AndroidKey < Base
|
9
|
-
class KeyDescription
|
10
|
-
# https://developer.android.com/training/articles/security-key-attestation#certificate_schema
|
11
|
-
ATTESTATION_CHALLENGE_INDEX = 4
|
12
|
-
SOFTWARE_ENFORCED_INDEX = 6
|
13
|
-
TEE_ENFORCED_INDEX = 7
|
14
|
-
|
15
|
-
def initialize(sequence)
|
16
|
-
@sequence = sequence
|
17
|
-
end
|
18
|
-
|
19
|
-
def attestation_challenge
|
20
|
-
sequence[ATTESTATION_CHALLENGE_INDEX].value
|
21
|
-
end
|
22
|
-
|
23
|
-
def tee_enforced
|
24
|
-
@tee_enforced ||= AuthorizationList.new(sequence[TEE_ENFORCED_INDEX].value)
|
25
|
-
end
|
26
|
-
|
27
|
-
def software_enforced
|
28
|
-
@software_enforced ||= AuthorizationList.new(sequence[SOFTWARE_ENFORCED_INDEX].value)
|
29
|
-
end
|
30
|
-
|
31
|
-
private
|
32
|
-
|
33
|
-
attr_reader :sequence
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
@@ -1,44 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "openssl"
|
4
|
-
require "tpm/constants"
|
5
|
-
require "tpm/s_attest"
|
6
|
-
require "webauthn/attestation_statement/base"
|
7
|
-
|
8
|
-
module WebAuthn
|
9
|
-
module AttestationStatement
|
10
|
-
class TPM < Base
|
11
|
-
class CertInfo
|
12
|
-
TPM_TO_OPENSSL_HASH_ALG = {
|
13
|
-
::TPM::ALG_SHA1 => "SHA1",
|
14
|
-
::TPM::ALG_SHA256 => "SHA256"
|
15
|
-
}.freeze
|
16
|
-
|
17
|
-
def initialize(data)
|
18
|
-
@data = data
|
19
|
-
end
|
20
|
-
|
21
|
-
def valid?(attested_data, extra_data)
|
22
|
-
s_attest.magic == ::TPM::GENERATED_VALUE &&
|
23
|
-
valid_name?(attested_data) &&
|
24
|
-
s_attest.extra_data.buffer == extra_data
|
25
|
-
end
|
26
|
-
|
27
|
-
private
|
28
|
-
|
29
|
-
attr_reader :data
|
30
|
-
|
31
|
-
def valid_name?(attested_data)
|
32
|
-
name_hash_alg = s_attest.attested.name.buffer[0..1].unpack("n")[0]
|
33
|
-
name = s_attest.attested.name.buffer[2..-1]
|
34
|
-
|
35
|
-
name == OpenSSL::Digest.digest(TPM_TO_OPENSSL_HASH_ALG[name_hash_alg], attested_data)
|
36
|
-
end
|
37
|
-
|
38
|
-
def s_attest
|
39
|
-
@s_attest ||= ::TPM::SAttest.read(data)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
@@ -1,85 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "cose"
|
4
|
-
require "cose/rsassa_algorithm"
|
5
|
-
require "tpm/constants"
|
6
|
-
require "tpm/t_public"
|
7
|
-
require "webauthn/attestation_statement/base"
|
8
|
-
|
9
|
-
module WebAuthn
|
10
|
-
module AttestationStatement
|
11
|
-
class TPM < Base
|
12
|
-
class PubArea
|
13
|
-
BYTE_LENGTH = 8
|
14
|
-
|
15
|
-
COSE_ECC_TO_TPM_ALG = {
|
16
|
-
COSE::Algorithm.by_name("ES256").id => ::TPM::ALG_ECDSA,
|
17
|
-
}.freeze
|
18
|
-
|
19
|
-
COSE_RSA_TO_TPM_ALG = {
|
20
|
-
COSE::Algorithm.by_name("RS256").id => ::TPM::ALG_RSASSA,
|
21
|
-
COSE::Algorithm.by_name("PS256").id => ::TPM::ALG_RSAPSS,
|
22
|
-
}.freeze
|
23
|
-
|
24
|
-
COSE_TO_TPM_CURVE = {
|
25
|
-
COSE::Key::Curve.by_name("P-256").id => ::TPM::ECC_NIST_P256
|
26
|
-
}.freeze
|
27
|
-
|
28
|
-
def initialize(data)
|
29
|
-
@data = data
|
30
|
-
end
|
31
|
-
|
32
|
-
def valid?(public_key)
|
33
|
-
cose_key = COSE::Key.deserialize(public_key)
|
34
|
-
|
35
|
-
case cose_key
|
36
|
-
when COSE::Key::EC2
|
37
|
-
valid_ecc_key?(cose_key)
|
38
|
-
when COSE::Key::RSA
|
39
|
-
valid_rsa_key?(cose_key)
|
40
|
-
else
|
41
|
-
raise "Unsupported or unknown TPM key type"
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
private
|
46
|
-
|
47
|
-
attr_reader :data
|
48
|
-
|
49
|
-
def valid_ecc_key?(cose_key)
|
50
|
-
valid_symmetric? &&
|
51
|
-
valid_scheme?(COSE_ECC_TO_TPM_ALG[cose_key.alg]) &&
|
52
|
-
parameters.curve_id == COSE_TO_TPM_CURVE[cose_key.crv] &&
|
53
|
-
unique == cose_key.x + cose_key.y
|
54
|
-
end
|
55
|
-
|
56
|
-
def valid_rsa_key?(cose_key)
|
57
|
-
valid_symmetric? &&
|
58
|
-
valid_scheme?(COSE_RSA_TO_TPM_ALG[cose_key.alg]) &&
|
59
|
-
parameters.key_bits == cose_key.n.size * BYTE_LENGTH &&
|
60
|
-
unique == cose_key.n
|
61
|
-
end
|
62
|
-
|
63
|
-
def valid_symmetric?
|
64
|
-
parameters.symmetric == ::TPM::ALG_NULL
|
65
|
-
end
|
66
|
-
|
67
|
-
def valid_scheme?(scheme)
|
68
|
-
parameters.scheme == ::TPM::ALG_NULL || parameters.scheme == scheme
|
69
|
-
end
|
70
|
-
|
71
|
-
def unique
|
72
|
-
t_public.unique.buffer
|
73
|
-
end
|
74
|
-
|
75
|
-
def parameters
|
76
|
-
t_public.parameters
|
77
|
-
end
|
78
|
-
|
79
|
-
def t_public
|
80
|
-
@t_public = ::TPM::TPublic.read(data)
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
85
|
-
end
|
@@ -1,20 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "securecompare"
|
4
|
-
|
5
|
-
module WebAuthn
|
6
|
-
module SecurityUtils
|
7
|
-
# Constant time string comparison, for variable length strings.
|
8
|
-
# This code was adapted from Rails ActiveSupport::SecurityUtils
|
9
|
-
#
|
10
|
-
# The values are first processed by SHA256, so that we don't leak length info
|
11
|
-
# via timing attacks.
|
12
|
-
def secure_compare(first_string, second_string)
|
13
|
-
first_string_sha256 = ::Digest::SHA256.digest(first_string)
|
14
|
-
second_string_sha256 = ::Digest::SHA256.digest(second_string)
|
15
|
-
|
16
|
-
SecureCompare.compare(first_string_sha256, second_string_sha256) && first_string == second_string
|
17
|
-
end
|
18
|
-
module_function :secure_compare
|
19
|
-
end
|
20
|
-
end
|