Class: U2F::U2F
- Inherits:
-
Object
- Object
- U2F::U2F
- Defined in:
- lib/u2f/u2f.rb
Instance Attribute Summary collapse
-
#app_id ⇒ Object
Returns the value of attribute app_id.
Class Method Summary collapse
-
.public_key_pem(key) ⇒ Object
- Convert a binary public key to PEM format * Args: -
key
-
Binary public key.
- Convert a binary public key to PEM format * Args: -
Instance Method Summary collapse
-
#authenticate!(challenge, response, registration_public_key, registration_counter) ⇒ Object
Authenticate a response from the U2F device.
-
#authentication_requests(key_handles) ⇒ Object
Generate data to be sent to the U2F device before authenticating.
-
#challenge ⇒ Object
Generates a 32 byte long random U2F challenge.
-
#initialize(app_id) ⇒ U2F
constructor
- Args: -
app_id
-
An application (facet) ID string.
- Args: -
-
#register!(challenges, response) ⇒ Object
Authenticate the response from the U2F device when registering.
-
#registration_requests ⇒ Object
Generate data to be used when registering a U2F device.
Constructor Details
#initialize(app_id) ⇒ U2F
-
Args:
app_id
-
An application (facet) ID string
8 9 10 |
# File 'lib/u2f/u2f.rb', line 8 def initialize(app_id) @app_id = app_id end |
Instance Attribute Details
#app_id ⇒ Object
Returns the value of attribute app_id.
3 4 5 |
# File 'lib/u2f/u2f.rb', line 3 def app_id @app_id end |
Class Method Details
.public_key_pem(key) ⇒ Object
Convert a binary public key to PEM format
-
Args:
key
-
Binary public key
-
Returns:
-
A base64 encoded public key
String
in PEM format
-
-
Raises:
PublicKeyDecodeError
-
if the
key
argument is incorrect
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 |
# File 'lib/u2f/u2f.rb', line 142 def self.public_key_pem(key) fail PublicKeyDecodeError unless key.bytesize == 65 && key.byteslice(0) == "\x04" # http://tools.ietf.org/html/rfc5480 der = OpenSSL::ASN1::Sequence([ OpenSSL::ASN1::Sequence([ OpenSSL::ASN1::ObjectId('1.2.840.10045.2.1'), # id-ecPublicKey OpenSSL::ASN1::ObjectId('1.2.840.10045.3.1.7') # secp256r1 ]), OpenSSL::ASN1::BitString(key) ]).to_der pem = "-----BEGIN PUBLIC KEY-----\r\n" + Base64.strict_encode64(der).scan(/.{1,64}/).join("\r\n") + "\r\n-----END PUBLIC KEY-----" pem end |
Instance Method Details
#authenticate!(challenge, response, registration_public_key, registration_counter) ⇒ Object
Authenticate a response from the U2F device
-
Args:
challenges
-
Array
of challenge strings
response
-
Response from the U2F device as a
SignResponse
object
registration_public_key
-
Public key of the registered U2F device as binary string
registration_counter
-
Integer
with the current counter value of the registered device.
-
Raises:
NoMatchingRequestError
-
if the challenge in the response doesn’t match any of the provided ones.
ClientDataTypeError
-
if the response is of the wrong type
AuthenticationFailedError
-
if the authentication failed
UserNotPresentError
-
if the user wasn’t present during the authentication
CounterTooLowError
-
if there is a counter mismatch between the registered one and the one in the response.
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
# File 'lib/u2f/u2f.rb', line 44 def authenticate!(challenge, response, registration_public_key, registration_counter) # TODO: check that it's the correct key_handle as well unless challenge == response.client_data.challenge fail NoMatchingRequestError end fail ClientDataTypeError unless response.client_data.authentication? pem = U2F.public_key_pem(registration_public_key) fail AuthenticationFailedError unless response.verify(app_id, pem) fail UserNotPresentError unless response.user_present? unless response.counter > registration_counter unless response.counter == 0 && registration_counter == 0 fail CounterTooLowError end end end |
#authentication_requests(key_handles) ⇒ Object
Generate data to be sent to the U2F device before authenticating
-
Args:
key_handles
-
Array
of previously registered U2F key handles
-
Returns:
-
An
Array
ofSignRequest
objects
-
21 22 23 24 25 26 |
# File 'lib/u2f/u2f.rb', line 21 def authentication_requests(key_handles) key_handles = [key_handles] unless key_handles.is_a? Array key_handles.map do |key_handle| SignRequest.new(key_handle) end end |
#challenge ⇒ Object
Generates a 32 byte long random U2F challenge
-
Returns:
-
Base64 urlsafe encoded challenge
-
73 74 75 |
# File 'lib/u2f/u2f.rb', line 73 def challenge ::U2F.urlsafe_encode64(SecureRandom.random_bytes(32)) end |
#register!(challenges, response) ⇒ Object
Authenticate the response from the U2F device when registering
-
Args:
challenges
-
Array
of challenge strings
response
-
Response of the U2F device as a
RegisterResponse
object
-
Returns:
-
A
Registration
object
-
-
Raises:
UnmatchedChallengeError
-
if the challenge in the response doesn’t match any of the provided ones.
ClientDataTypeError
-
if the response is of the wrong type
AttestationSignatureError
-
if the registration failed
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
# File 'lib/u2f/u2f.rb', line 103 def register!(challenges, response) challenges = [challenges] unless challenges.is_a? Array challenge = challenges.detect do |chg| chg == response.client_data.challenge end fail UnmatchedChallengeError unless challenge fail ClientDataTypeError unless response.client_data.registration? # Validate public key U2F.public_key_pem(response.public_key_raw) # TODO: # unless U2F.validate_certificate(response.certificate_raw) # fail AttestationVerificationError # end fail AttestationSignatureError unless response.verify(app_id) registration = Registration.new( response.key_handle, response.public_key, response.certificate ) registration end |
#registration_requests ⇒ Object
Generate data to be used when registering a U2F device
-
Returns:
-
An
Array
ofRegisterRequest
objects
-
83 84 85 86 |
# File 'lib/u2f/u2f.rb', line 83 def registration_requests # TODO: generate a request for each supported version [RegisterRequest.new(challenge)] end |