class U2F::U2F
Attributes
Public Class Methods
-
Args:
app_id
-
An application (facet) ID string
# File lib/u2f/u2f.rb, line 8 def initialize(app_id) @app_id = app_id end
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
# 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
Public Instance Methods
Authenticate a response from the U2F
device
-
Args:
-
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.
# 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
Generate data to be sent to the U2F
device before authenticating
-
Args:
key_handles
-
Array
of previously registeredU2F
key handles
-
Returns:
-
An
Array
ofSignRequest
objects
-
# 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
Generates a 32 byte long random U2F
challenge
-
Returns:
-
Base64 urlsafe encoded challenge
-
# File lib/u2f/u2f.rb, line 73 def challenge ::U2F.urlsafe_encode64(SecureRandom.random_bytes(32)) end
Authenticate the response from the U2F
device when registering
-
Args:
challenges
-
Array
of challenge strings
response
-
Response of the
U2F
device as aRegisterResponse
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
# 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
Generate data to be used when registering a U2F
device
-
Returns:
-
An
Array
ofRegisterRequest
objects
-
# File lib/u2f/u2f.rb, line 83 def registration_requests # TODO: generate a request for each supported version [RegisterRequest.new(challenge)] end