webauthn 3.0.0 → 3.4.2

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.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/.github/actions/install-openssl/action.yml +55 -0
  3. data/.github/actions/install-ruby/action.yml +84 -0
  4. data/.github/dependabot.yml +6 -0
  5. data/.github/workflows/build.yml +60 -10
  6. data/.github/workflows/git.yml +1 -1
  7. data/.rubocop.yml +1 -1
  8. data/CHANGELOG.md +77 -0
  9. data/README.md +5 -4
  10. data/docs/advanced_configuration.md +16 -15
  11. data/lib/webauthn/attestation_statement/base.rb +10 -1
  12. data/lib/webauthn/authenticator_assertion_response.rb +16 -2
  13. data/lib/webauthn/authenticator_attestation_response.rb +5 -4
  14. data/lib/webauthn/authenticator_response.rb +16 -6
  15. data/lib/webauthn/client_data.rb +4 -6
  16. data/lib/webauthn/configuration.rb +2 -0
  17. data/lib/webauthn/credential.rb +0 -1
  18. data/lib/webauthn/encoder.rb +5 -33
  19. data/lib/webauthn/encoders.rb +62 -0
  20. data/lib/webauthn/fake_authenticator/attestation_object.rb +10 -1
  21. data/lib/webauthn/fake_authenticator/authenticator_data.rb +3 -1
  22. data/lib/webauthn/fake_authenticator.rb +25 -4
  23. data/lib/webauthn/fake_client.rb +6 -1
  24. data/lib/webauthn/json_serializer.rb +45 -0
  25. data/lib/webauthn/public_key_credential/entity.rb +2 -24
  26. data/lib/webauthn/public_key_credential/options.rb +2 -24
  27. data/lib/webauthn/public_key_credential.rb +17 -2
  28. data/lib/webauthn/public_key_credential_with_assertion.rb +2 -1
  29. data/lib/webauthn/public_key_credential_with_attestation.rb +2 -2
  30. data/lib/webauthn/relying_party.rb +24 -7
  31. data/lib/webauthn/u2f_migrator.rb +5 -3
  32. data/lib/webauthn/version.rb +1 -1
  33. data/lib/webauthn.rb +1 -0
  34. data/webauthn.gemspec +5 -6
  35. metadata +26 -29
@@ -4,7 +4,6 @@ require "webauthn/public_key_credential/creation_options"
4
4
  require "webauthn/public_key_credential/request_options"
5
5
  require "webauthn/public_key_credential_with_assertion"
6
6
  require "webauthn/public_key_credential_with_attestation"
7
- require "webauthn/relying_party"
8
7
 
9
8
  module WebAuthn
10
9
  module Credential
@@ -1,46 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "base64"
3
+ require "webauthn/encoders"
4
4
 
5
5
  module WebAuthn
6
- def self.standard_encoder
7
- @standard_encoder ||= Encoder.new
8
- end
9
-
10
6
  class Encoder
7
+ extend Forwardable
8
+
11
9
  # https://www.w3.org/TR/webauthn-2/#base64url-encoding
12
10
  STANDARD_ENCODING = :base64url
13
11
 
14
- attr_reader :encoding
12
+ def_delegators :@encoder_klass, :encode, :decode
15
13
 
16
14
  def initialize(encoding = STANDARD_ENCODING)
17
- @encoding = encoding
18
- end
19
-
20
- def encode(data)
21
- case encoding
22
- when :base64
23
- Base64.strict_encode64(data)
24
- when :base64url
25
- Base64.urlsafe_encode64(data, padding: false)
26
- when nil, false
27
- data
28
- else
29
- raise "Unsupported or unknown encoding: #{encoding}"
30
- end
31
- end
32
-
33
- def decode(data)
34
- case encoding
35
- when :base64
36
- Base64.strict_decode64(data)
37
- when :base64url
38
- Base64.urlsafe_decode64(data)
39
- when nil, false
40
- data
41
- else
42
- raise "Unsupported or unknown encoding: #{encoding}"
43
- end
15
+ @encoder_klass = Encoders.lookup(encoding)
44
16
  end
45
17
  end
46
18
  end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WebAuthn
4
+ def self.standard_encoder
5
+ @standard_encoder ||= Encoders.lookup(Encoder::STANDARD_ENCODING)
6
+ end
7
+
8
+ module Encoders
9
+ class << self
10
+ def lookup(encoding)
11
+ case encoding
12
+ when :base64
13
+ Base64Encoder
14
+ when :base64url
15
+ Base64UrlEncoder
16
+ when nil, false
17
+ NullEncoder
18
+ else
19
+ raise "Unsupported or unknown encoding: #{encoding}"
20
+ end
21
+ end
22
+ end
23
+
24
+ class Base64Encoder
25
+ def self.encode(data)
26
+ [data].pack("m0") # Base64.strict_encode64(data)
27
+ end
28
+
29
+ def self.decode(data)
30
+ data.unpack1("m0") # Base64.strict_decode64(data)
31
+ end
32
+ end
33
+
34
+ class Base64UrlEncoder
35
+ def self.encode(data)
36
+ data = [data].pack("m0") # Base64.urlsafe_encode64(data, padding: false)
37
+ data.chomp!("==") or data.chomp!("=")
38
+ data.tr!("+/", "-_")
39
+ data
40
+ end
41
+
42
+ def self.decode(data)
43
+ if !data.end_with?("=") && data.length % 4 != 0 # Base64.urlsafe_decode64(data)
44
+ data = data.ljust((data.length + 3) & ~3, "=")
45
+ end
46
+
47
+ data = data.tr("-_", "+/")
48
+ data.unpack1("m0")
49
+ end
50
+ end
51
+
52
+ class NullEncoder
53
+ def self.encode(data)
54
+ data
55
+ end
56
+
57
+ def self.decode(data)
58
+ data
59
+ end
60
+ end
61
+ end
62
+ end
@@ -61,7 +61,7 @@ module WebAuthn
61
61
  begin
62
62
  credential_data =
63
63
  if attested_credential_data
64
- { id: credential_id, public_key: credential_key.public_key }
64
+ { id: credential_id, public_key: credential_public_key }
65
65
  end
66
66
 
67
67
  AuthenticatorData.new(
@@ -76,6 +76,15 @@ module WebAuthn
76
76
  )
77
77
  end
78
78
  end
79
+
80
+ def credential_public_key
81
+ case credential_key
82
+ when OpenSSL::PKey::RSA, OpenSSL::PKey::EC
83
+ credential_key.public_key
84
+ when OpenSSL::PKey::PKey
85
+ OpenSSL::PKey.read(credential_key.public_to_der)
86
+ end
87
+ end
79
88
  end
80
89
  end
81
90
  end
@@ -140,7 +140,9 @@ module WebAuthn
140
140
 
141
141
  key = COSE::Key::EC2.from_pkey(credential[:public_key])
142
142
  key.alg = alg[key.crv]
143
-
143
+ when OpenSSL::PKey::PKey
144
+ key = COSE::Key::OKP.from_pkey(credential[:public_key])
145
+ key.alg = -8
144
146
  end
145
147
 
146
148
  key.serialize
@@ -20,10 +20,11 @@ module WebAuthn
20
20
  backup_eligibility: false,
21
21
  backup_state: false,
22
22
  attested_credential_data: true,
23
+ algorithm: nil,
23
24
  sign_count: nil,
24
25
  extensions: nil
25
26
  )
26
- credential_id, credential_key, credential_sign_count = new_credential
27
+ credential_id, credential_key, credential_sign_count = new_credential(algorithm)
27
28
  sign_count ||= credential_sign_count
28
29
 
29
30
  credentials[rp_id] ||= {}
@@ -85,7 +86,14 @@ module WebAuthn
85
86
  extensions: extensions
86
87
  ).serialize
87
88
 
88
- signature = credential_key.sign("SHA256", authenticator_data + client_data_hash)
89
+ signature_digest_algorithm =
90
+ case credential_key
91
+ when OpenSSL::PKey::RSA, OpenSSL::PKey::EC
92
+ 'SHA256'
93
+ when OpenSSL::PKey::PKey
94
+ nil
95
+ end
96
+ signature = credential_key.sign(signature_digest_algorithm, authenticator_data + client_data_hash)
89
97
  credential[:sign_count] += 1
90
98
 
91
99
  {
@@ -102,8 +110,21 @@ module WebAuthn
102
110
 
103
111
  attr_reader :credentials
104
112
 
105
- def new_credential
106
- [SecureRandom.random_bytes(16), OpenSSL::PKey::EC.generate("prime256v1"), 0]
113
+ def new_credential(algorithm)
114
+ algorithm ||= 'ES256'
115
+ credential_key =
116
+ case algorithm
117
+ when 'ES256'
118
+ OpenSSL::PKey::EC.generate('prime256v1')
119
+ when 'RS256'
120
+ OpenSSL::PKey::RSA.new(2048)
121
+ when 'EdDSA'
122
+ OpenSSL::PKey.generate_key("ED25519")
123
+ else
124
+ raise "Unsupported algorithm #{algorithm}"
125
+ end
126
+
127
+ [SecureRandom.random_bytes(16), credential_key, 0]
107
128
  end
108
129
 
109
130
  def hashed(target)
@@ -32,6 +32,7 @@ module WebAuthn
32
32
  backup_eligibility: false,
33
33
  backup_state: false,
34
34
  attested_credential_data: true,
35
+ credential_algorithm: nil,
35
36
  extensions: nil
36
37
  )
37
38
  rp_id ||= URI.parse(origin).host
@@ -47,6 +48,7 @@ module WebAuthn
47
48
  backup_eligibility: backup_eligibility,
48
49
  backup_state: backup_state,
49
50
  attested_credential_data: attested_credential_data,
51
+ algorithm: credential_algorithm,
50
52
  extensions: extensions
51
53
  )
52
54
 
@@ -64,10 +66,12 @@ module WebAuthn
64
66
  "type" => "public-key",
65
67
  "id" => internal_encoder.encode(id),
66
68
  "rawId" => encoder.encode(id),
69
+ "authenticatorAttachment" => 'platform',
67
70
  "clientExtensionResults" => extensions,
68
71
  "response" => {
69
72
  "attestationObject" => encoder.encode(attestation_object),
70
- "clientDataJSON" => encoder.encode(client_data_json)
73
+ "clientDataJSON" => encoder.encode(client_data_json),
74
+ "transports" => ["internal"],
71
75
  }
72
76
  }
73
77
  end
@@ -108,6 +112,7 @@ module WebAuthn
108
112
  "id" => internal_encoder.encode(assertion[:credential_id]),
109
113
  "rawId" => encoder.encode(assertion[:credential_id]),
110
114
  "clientExtensionResults" => extensions,
115
+ "authenticatorAttachment" => 'platform',
111
116
  "response" => {
112
117
  "clientDataJSON" => encoder.encode(client_data_json),
113
118
  "authenticatorData" => encoder.encode(assertion[:authenticator_data]),
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WebAuthn
4
+ module JSONSerializer
5
+ # Argument wildcard for Ruby on Rails controller automatic object JSON serialization
6
+ def as_json(*)
7
+ deep_camelize_keys(to_hash)
8
+ end
9
+
10
+ private
11
+
12
+ def to_hash
13
+ attributes.each_with_object({}) do |attribute_name, hash|
14
+ value = send(attribute_name)
15
+
16
+ if value.respond_to?(:as_json)
17
+ value = value.as_json
18
+ end
19
+
20
+ if value
21
+ hash[attribute_name] = value
22
+ end
23
+ end
24
+ end
25
+
26
+ def deep_camelize_keys(object)
27
+ case object
28
+ when Hash
29
+ object.each_with_object({}) do |(key, value), result|
30
+ result[camelize(key)] = deep_camelize_keys(value)
31
+ end
32
+ when Array
33
+ object.map { |element| deep_camelize_keys(element) }
34
+ else
35
+ object
36
+ end
37
+ end
38
+
39
+ def camelize(term)
40
+ first_term, *rest = term.to_s.split('_')
41
+
42
+ [first_term, *rest.map(&:capitalize)].join.to_sym
43
+ end
44
+ end
45
+ end
@@ -1,40 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "awrence"
4
-
5
3
  module WebAuthn
6
4
  class PublicKeyCredential
7
5
  class Entity
6
+ include JSONSerializer
7
+
8
8
  attr_reader :name
9
9
 
10
10
  def initialize(name:)
11
11
  @name = name
12
12
  end
13
13
 
14
- def as_json
15
- to_hash.to_camelback_keys
16
- end
17
-
18
14
  private
19
15
 
20
- def to_hash
21
- hash = {}
22
-
23
- attributes.each do |attribute_name|
24
- value = send(attribute_name)
25
-
26
- if value.respond_to?(:as_json)
27
- value = value.as_json
28
- end
29
-
30
- if value
31
- hash[attribute_name] = value
32
- end
33
- end
34
-
35
- hash
36
- end
37
-
38
16
  def attributes
39
17
  [:name]
40
18
  end
@@ -1,11 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "awrence"
4
3
  require "securerandom"
5
4
 
6
5
  module WebAuthn
7
6
  class PublicKeyCredential
8
7
  class Options
8
+ include JSONSerializer
9
+
9
10
  CHALLENGE_LENGTH = 32
10
11
 
11
12
  attr_reader :timeout, :extensions, :relying_party
@@ -20,31 +21,8 @@ module WebAuthn
20
21
  encoder.encode(raw_challenge)
21
22
  end
22
23
 
23
- # Argument wildcard for Ruby on Rails controller automatic object JSON serialization
24
- def as_json(*)
25
- to_hash.to_camelback_keys
26
- end
27
-
28
24
  private
29
25
 
30
- def to_hash
31
- hash = {}
32
-
33
- attributes.each do |attribute_name|
34
- value = send(attribute_name)
35
-
36
- if value.respond_to?(:as_json)
37
- value = value.as_json
38
- end
39
-
40
- if value
41
- hash[attribute_name] = value
42
- end
43
- end
44
-
45
- hash
46
- end
47
-
48
26
  def attributes
49
27
  [:challenge, :timeout, :extensions]
50
28
  end
@@ -4,7 +4,9 @@ require "webauthn/encoder"
4
4
 
5
5
  module WebAuthn
6
6
  class PublicKeyCredential
7
- attr_reader :type, :id, :raw_id, :client_extension_outputs, :response
7
+ class InvalidChallengeError < Error; end
8
+
9
+ attr_reader :type, :id, :raw_id, :client_extension_outputs, :authenticator_attachment, :response
8
10
 
9
11
  def self.from_client(credential, relying_party: WebAuthn.configuration.relying_party)
10
12
  new(
@@ -12,6 +14,7 @@ module WebAuthn
12
14
  id: credential["id"],
13
15
  raw_id: relying_party.encoder.decode(credential["rawId"]),
14
16
  client_extension_outputs: credential["clientExtensionResults"],
17
+ authenticator_attachment: credential["authenticatorAttachment"],
15
18
  response: response_class.from_client(credential["response"], relying_party: relying_party),
16
19
  relying_party: relying_party
17
20
  )
@@ -22,6 +25,7 @@ module WebAuthn
22
25
  id:,
23
26
  raw_id:,
24
27
  response:,
28
+ authenticator_attachment: nil,
25
29
  client_extension_outputs: {},
26
30
  relying_party: WebAuthn.configuration.relying_party
27
31
  )
@@ -29,11 +33,18 @@ module WebAuthn
29
33
  @id = id
30
34
  @raw_id = raw_id
31
35
  @client_extension_outputs = client_extension_outputs
36
+ @authenticator_attachment = authenticator_attachment
32
37
  @response = response
33
38
  @relying_party = relying_party
34
39
  end
35
40
 
36
- def verify(*_args)
41
+ def verify(challenge, *_args)
42
+ unless valid_class?(challenge)
43
+ msg = "challenge must be a String. input challenge class: #{challenge.class}"
44
+
45
+ raise(InvalidChallengeError, msg)
46
+ end
47
+
37
48
  valid_type? || raise("invalid type")
38
49
  valid_id? || raise("invalid id")
39
50
 
@@ -68,6 +79,10 @@ module WebAuthn
68
79
  raw_id && id && raw_id == WebAuthn.standard_encoder.decode(id)
69
80
  end
70
81
 
82
+ def valid_class?(challenge)
83
+ challenge.is_a?(String)
84
+ end
85
+
71
86
  def authenticator_data
72
87
  response&.authenticator_data
73
88
  end
@@ -9,13 +9,14 @@ module WebAuthn
9
9
  WebAuthn::AuthenticatorAssertionResponse
10
10
  end
11
11
 
12
- def verify(challenge, public_key:, sign_count:, user_verification: nil)
12
+ def verify(challenge, public_key:, sign_count:, user_presence: nil, user_verification: nil)
13
13
  super
14
14
 
15
15
  response.verify(
16
16
  encoder.decode(challenge),
17
17
  public_key: encoder.decode(public_key),
18
18
  sign_count: sign_count,
19
+ user_presence: user_presence,
19
20
  user_verification: user_verification,
20
21
  rp_id: appid_extension_output ? appid : nil
21
22
  )
@@ -9,10 +9,10 @@ module WebAuthn
9
9
  WebAuthn::AuthenticatorAttestationResponse
10
10
  end
11
11
 
12
- def verify(challenge, user_verification: nil)
12
+ def verify(challenge, user_presence: nil, user_verification: nil)
13
13
  super
14
14
 
15
- response.verify(encoder.decode(challenge), user_verification: user_verification)
15
+ response.verify(encoder.decode(challenge), user_presence: user_presence, user_verification: user_verification)
16
16
 
17
17
  true
18
18
  end
@@ -9,15 +9,16 @@ module WebAuthn
9
9
  class RootCertificateFinderNotSupportedError < Error; end
10
10
 
11
11
  class RelyingParty
12
+ DEFAULT_ALGORITHMS = ["ES256", "PS256", "RS256"].compact.freeze
13
+
12
14
  def self.if_pss_supported(algorithm)
13
15
  OpenSSL::PKey::RSA.instance_methods.include?(:verify_pss) ? algorithm : nil
14
16
  end
15
17
 
16
- DEFAULT_ALGORITHMS = ["ES256", "PS256", "RS256"].compact.freeze
17
-
18
18
  def initialize(
19
19
  algorithms: DEFAULT_ALGORITHMS.dup,
20
20
  encoding: WebAuthn::Encoder::STANDARD_ENCODING,
21
+ allowed_origins: nil,
21
22
  origin: nil,
22
23
  id: nil,
23
24
  name: nil,
@@ -30,7 +31,7 @@ module WebAuthn
30
31
  )
31
32
  @algorithms = algorithms
32
33
  @encoding = encoding
33
- @origin = origin
34
+ @allowed_origins = allowed_origins
34
35
  @id = id
35
36
  @name = name
36
37
  @verify_attestation_statement = verify_attestation_statement
@@ -38,12 +39,13 @@ module WebAuthn
38
39
  @silent_authentication = silent_authentication
39
40
  @acceptable_attestation_types = acceptable_attestation_types
40
41
  @legacy_u2f_appid = legacy_u2f_appid
42
+ self.origin = origin
41
43
  self.attestation_root_certificates_finders = attestation_root_certificates_finders
42
44
  end
43
45
 
44
46
  attr_accessor :algorithms,
45
47
  :encoding,
46
- :origin,
48
+ :allowed_origins,
47
49
  :id,
48
50
  :name,
49
51
  :verify_attestation_statement,
@@ -52,7 +54,7 @@ module WebAuthn
52
54
  :acceptable_attestation_types,
53
55
  :legacy_u2f_appid
54
56
 
55
- attr_reader :attestation_root_certificates_finders
57
+ attr_reader :attestation_root_certificates_finders, :origin
56
58
 
57
59
  # This is the user-data encoder.
58
60
  # Used to decode user input and to encode data provided to the user.
@@ -81,10 +83,10 @@ module WebAuthn
81
83
  )
82
84
  end
83
85
 
84
- def verify_registration(raw_credential, challenge, user_verification: nil)
86
+ def verify_registration(raw_credential, challenge, user_presence: nil, user_verification: nil)
85
87
  webauthn_credential = WebAuthn::Credential.from_create(raw_credential, relying_party: self)
86
88
 
87
- if webauthn_credential.verify(challenge, user_verification: user_verification)
89
+ if webauthn_credential.verify(challenge, user_presence: user_presence, user_verification: user_verification)
88
90
  webauthn_credential
89
91
  end
90
92
  end
@@ -99,6 +101,7 @@ module WebAuthn
99
101
  def verify_authentication(
100
102
  raw_credential,
101
103
  challenge,
104
+ user_presence: nil,
102
105
  user_verification: nil,
103
106
  public_key: nil,
104
107
  sign_count: nil
@@ -111,10 +114,24 @@ module WebAuthn
111
114
  challenge,
112
115
  public_key: public_key || stored_credential.public_key,
113
116
  sign_count: sign_count || stored_credential.sign_count,
117
+ user_presence: user_presence,
114
118
  user_verification: user_verification
115
119
  )
116
120
  block_given? ? [webauthn_credential, stored_credential] : webauthn_credential
117
121
  end
118
122
  end
123
+
124
+ # DEPRECATED: This method will be removed in future.
125
+ def origin=(new_origin)
126
+ return if new_origin.nil?
127
+
128
+ warn(
129
+ "DEPRECATION WARNING: `WebAuthn.origin` is deprecated and will be removed in future. "\
130
+ "Please use `WebAuthn.allowed_origins` instead "\
131
+ "that also allows configuring multiple origins per Relying Party"
132
+ )
133
+
134
+ @allowed_origins ||= Array(new_origin) # rubocop:disable Naming/MemoizedInstanceVariableName
135
+ end
119
136
  end
120
137
  end
@@ -43,7 +43,9 @@ module WebAuthn
43
43
  end
44
44
 
45
45
  def attestation_trust_path
46
- @attestation_trust_path ||= [OpenSSL::X509::Certificate.new(Base64.strict_decode64(@certificate))]
46
+ @attestation_trust_path ||= [
47
+ OpenSSL::X509::Certificate.new(WebAuthn::Encoders::Base64Encoder.decode(@certificate))
48
+ ]
47
49
  end
48
50
 
49
51
  private
@@ -51,14 +53,14 @@ module WebAuthn
51
53
  # https://fidoalliance.org/specs/fido-v2.0-rd-20180702/fido-client-to-authenticator-protocol-v2.0-rd-20180702.html#u2f-authenticatorMakeCredential-interoperability
52
54
  # Let credentialId be a credentialIdLength byte array initialized with CTAP1/U2F response key handle bytes.
53
55
  def credential_id
54
- Base64.urlsafe_decode64(@key_handle)
56
+ WebAuthn::Encoders::Base64UrlEncoder.decode(@key_handle)
55
57
  end
56
58
 
57
59
  # Let x9encodedUserPublicKey be the user public key returned in the U2F registration response message [U2FRawMsgs].
58
60
  # Let coseEncodedCredentialPublicKey be the result of converting x9encodedUserPublicKey’s value from ANS X9.62 /
59
61
  # Sec-1 v2 uncompressed curve point representation [SEC1V2] to COSE_Key representation ([RFC8152] Section 7).
60
62
  def credential_cose_key
61
- decoded_public_key = Base64.strict_decode64(@public_key)
63
+ decoded_public_key = WebAuthn::Encoders::Base64Encoder.decode(@public_key)
62
64
  if WebAuthn::AttestationStatement::FidoU2f::PublicKey.uncompressed_point?(decoded_public_key)
63
65
  COSE::Key::EC2.new(
64
66
  alg: COSE::Algorithm.by_name("ES256").id,
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module WebAuthn
4
- VERSION = "3.0.0"
4
+ VERSION = "3.4.2"
5
5
  end
data/lib/webauthn.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "webauthn/json_serializer"
3
4
  require "webauthn/configuration"
4
5
  require "webauthn/credential"
5
6
  require "webauthn/credential_creation_options"
data/webauthn.gemspec CHANGED
@@ -34,19 +34,18 @@ Gem::Specification.new do |spec|
34
34
  spec.required_ruby_version = ">= 2.5"
35
35
 
36
36
  spec.add_dependency "android_key_attestation", "~> 0.3.0"
37
- spec.add_dependency "awrence", "~> 1.1"
38
37
  spec.add_dependency "bindata", "~> 2.4"
39
38
  spec.add_dependency "cbor", "~> 0.5.9"
40
39
  spec.add_dependency "cose", "~> 1.1"
41
40
  spec.add_dependency "openssl", ">= 2.2"
42
- spec.add_dependency "safety_net_attestation", "~> 0.4.0"
43
- spec.add_dependency "tpm-key_attestation", "~> 0.12.0"
41
+ spec.add_dependency "safety_net_attestation", "~> 0.5.0"
42
+ spec.add_dependency "tpm-key_attestation", "~> 0.14.0"
44
43
 
45
44
  spec.add_development_dependency "bundler", ">= 1.17", "< 3.0"
46
45
  spec.add_development_dependency "byebug", "~> 11.0"
47
46
  spec.add_development_dependency "rake", "~> 13.0"
48
47
  spec.add_development_dependency "rspec", "~> 3.8"
49
- spec.add_development_dependency "rubocop", "~> 1.9.1"
50
- spec.add_development_dependency "rubocop-rake", "~> 0.5.1"
51
- spec.add_development_dependency "rubocop-rspec", "~> 2.2.0"
48
+ spec.add_development_dependency "rubocop", "~> 1"
49
+ spec.add_development_dependency "rubocop-rake", "~> 0.5"
50
+ spec.add_development_dependency "rubocop-rspec", ">= 2.2", "< 4.0"
52
51
  end