class U2F::FakeU2F

Constants

CURVE_NAME

Attributes

app_id[RW]
cert_subject[RW]
counter[RW]
key_handle_raw[RW]

Public Class Methods

new(app_id, options = {}) click to toggle source

Initialize a new FakeU2F device for use in tests.

app_id - The appId/origin this is being tested against. options - A Hash of optional parameters (optional).

:counter      - The initial counter for this device.
:key_handle   - The raw key-handle this device should use.
:cert_subject - The subject field for the certificate generated
                for this device.

Returns nothing.

# File lib/u2f/fake_u2f.rb, line 16
def initialize(app_id, options = {})
  @app_id = app_id
  @counter = options.fetch(:counter, 0)
  @key_handle_raw = options.fetch(:key_handle, SecureRandom.random_bytes(32))
  @cert_subject = options.fetch(:cert_subject, "/CN=U2FTest")
end

Public Instance Methods

cert_raw() click to toggle source

The raw device attestation certificate as returned in the registrationData field of a RegisterResponse Hash.

Returns a DER formatted certificate String.

# File lib/u2f/fake_u2f.rb, line 67
def cert_raw
  cert.to_der
end
origin_public_key_raw() click to toggle source

The appId specific public key as returned in the registrationData field of a RegisterResponse Hash.

Returns a binary formatted EC public key String.

# File lib/u2f/fake_u2f.rb, line 59
def origin_public_key_raw
  [origin_key.public_key.to_bn.to_s(16)].pack('H*')
end
register_response(challenge, error = false) click to toggle source

A registerResponse hash as returned by the u2f.register JavaScript API.

challenge - The challenge to sign. error - Boolean. Whether to return an error response (optional).

Returns a JSON encoded Hash String.

# File lib/u2f/fake_u2f.rb, line 29
def register_response(challenge, error = false)
  if error
    JSON.dump(:errorCode => 4)
  else
    client_data_json = client_data(U2F::ClientData::REGISTRATION_TYP, challenge)
    JSON.dump(
      :registrationData => reg_registration_data(client_data_json),
      :clientData => U2F.urlsafe_encode64(client_data_json)
    )
  end
end
sign_response(challenge) click to toggle source

A SignResponse hash as returned by the u2f.sign JavaScript API.

challenge - The challenge to sign.

Returns a JSON encoded Hash String.

# File lib/u2f/fake_u2f.rb, line 46
def sign_response(challenge)
  client_data_json = client_data(U2F::ClientData::AUTHENTICATION_TYP, challenge)
  JSON.dump(
    :clientData => U2F.urlsafe_encode64(client_data_json),
    :keyHandle => U2F.urlsafe_encode64(key_handle_raw),
    :signatureData => auth_signature_data(client_data_json)
  )
end

Private Instance Methods

auth_signature(client_data_json) click to toggle source

The signature field of a signatureData field of a SignResponse Hash.

client_data_json - The JSON encoded clientData String.

Returns an ECDSA signature String.

# File lib/u2f/fake_u2f.rb, line 127
def auth_signature(client_data_json)
  data = [
    U2F::DIGEST.digest(app_id),
    1, # User present
    counter,
    U2F::DIGEST.digest(client_data_json)
  ].pack("A32CNA32")

  origin_key.sign(U2F::DIGEST.new, data)
end
auth_signature_data(client_data_json) click to toggle source

The signatureData field of a SignResponse Hash.

client_data_json - The JSON encoded clientData String.

Returns a url-safe base64 encoded binary String.

# File lib/u2f/fake_u2f.rb, line 112
def auth_signature_data(client_data_json)
  ::U2F.urlsafe_encode64(
    [
      1, # User present
      self.counter += 1,
      auth_signature(client_data_json)
    ].pack("CNA*")
  )
end
cert() click to toggle source

The self-signed device attestation certificate.

Returns a OpenSSL::X509::Certificate instance.

# File lib/u2f/fake_u2f.rb, line 163
def cert
  @cert ||= OpenSSL::X509::Certificate.new.tap do |c|
    c.subject = c.issuer = OpenSSL::X509::Name.parse(cert_subject)
    c.not_before = Time.now
    c.not_after = Time.now + 365 * 24 * 60 * 60
    c.public_key = cert_key
    c.serial = 0x1
    c.version = 0x0
    c.sign cert_key, U2F::DIGEST.new
  end
end
cert_key() click to toggle source

The public key used for signing the device certificate.

Returns a OpenSSL::PKey::EC instance.

# File lib/u2f/fake_u2f.rb, line 178
def cert_key
  @cert_key ||= generate_ec_key
end
client_data(typ, challenge) click to toggle source

The clientData hash as returned by registration and authentication responses.

typ - The String value for the 'typ' field. challenge - The String url-safe base64 encoded challenge parameter.

Returns a JSON encoded Hash String.

# File lib/u2f/fake_u2f.rb, line 145
def client_data(typ, challenge)
  JSON.dump(
    :challenge => challenge,
    :origin    => app_id,
    :typ       => typ
  )
end
generate_ec_key() click to toggle source

Generate an eliptic curve public/private key.

Returns a OpenSSL::PKey::EC instance.

# File lib/u2f/fake_u2f.rb, line 185
def generate_ec_key
  OpenSSL::PKey::EC.new().tap do |ec|
    ec.group = OpenSSL::PKey::EC::Group.new(CURVE_NAME)
    ec.generate_key
    # https://bugs.ruby-lang.org/issues/8177
    ec.define_singleton_method(:private?) { private_key? }
    ec.define_singleton_method(:public?) { public_key? }
  end
end
origin_key() click to toggle source

The appId-specific public/private key.

Returns a OpenSSL::PKey::EC instance.

# File lib/u2f/fake_u2f.rb, line 156
def origin_key
  @origin_key ||= generate_ec_key
end
reg_registration_data(client_data_json) click to toggle source

The registrationData field returns in a RegisterResponse Hash.

client_data_json - The JSON encoded clientData String.

Returns a url-safe base64 encoded binary String.

# File lib/u2f/fake_u2f.rb, line 78
def reg_registration_data(client_data_json)
  U2F.urlsafe_encode64(
    [
      5,
      origin_public_key_raw,
      key_handle_raw.bytesize,
      key_handle_raw,
      cert_raw,
      reg_signature(client_data_json)
    ].pack("CA65CA#{key_handle_raw.bytesize}A#{cert_raw.bytesize}A*")
  )
end
reg_signature(client_data_json) click to toggle source

The signature field of a registrationData field of a RegisterResponse.

client_data_json - The JSON encoded clientData String.

Returns an ECDSA signature String.

# File lib/u2f/fake_u2f.rb, line 96
def reg_signature(client_data_json)
  payload = [
    "\x00",
    U2F::DIGEST.digest(app_id),
    U2F::DIGEST.digest(client_data_json),
    key_handle_raw,
    origin_public_key_raw
  ].join
  cert_key.sign(U2F::DIGEST.new, payload)
end