Class: Devicecheck::Assertion

Inherits:
Object
  • Object
show all
Defined in:
lib/devicecheck/assertion.rb

Instance Method Summary collapse

Constructor Details

#initialize(app_id:, pkey_der:) ⇒ Assertion

Initialize the assertion service by providing both the App ID and the DER-encoded key that is associated with that App instance that was previously saved after it was attested.



24
25
26
27
28
# File 'lib/devicecheck/assertion.rb', line 24

def initialize(app_id:, pkey_der:)
  @app_id = app_id
  @pkey = OpenSSL::PKey.read(pkey_der)
  @sha256 = OpenSSL::Digest.new('SHA256')
end

Instance Method Details

#assert(client_data:, client_data_challenge:, expected_challenge:, assertion_object:, count: 0) ⇒ Integer

Verifies an assertion generated by the generateAssertion method from DCAppAttestService.

The app must obtain a one time unique value from the server, which we will call challenge. Then, it will compute an assertion by embedding this challenge into the client_data.

For example, if client_data is:

{ "new_score": 100 }

Then it must embed the challenge into this data, for example:

{ "new_score": 100, "challenge": "..." }

Note that using JSON strings is just an example. It depends on the use case and the interface must be established between the mobile app and the server. This is why we expect another parameter (client_data_challenge) that contains the embedded challenge value, since this library does not make any assumption on the format or contents of client_data.

Parameters:

  • client_data (String)

    client data (with embedded challenge)

  • client_data_challenge (String)

    client data challenge - copy of the challenge that is embedded in client data

  • assertion_object (String)

    Base64-encoded assertion object

  • expected_challenge (String)

    the challenge that was previously sent, to be compared with what was provided by the client

  • count (Integer) (defaults to: 0)

    current assertion counter associated with this app - 0 if this is the first assertion

Returns:

  • (Integer)

    the authenticator counter value, to be stored for later use



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/devicecheck/assertion.rb', line 69

def assert(client_data:, client_data_challenge:, expected_challenge:, assertion_object:, count: 0)
  decoded_assertion_object = CBOR.decode(Base64.strict_decode64(assertion_object))

  signature = decoded_assertion_object['signature']
  authenticator_data = decoded_assertion_object['authenticatorData']

  (rp_id_hash, _, sign_count,) = Data::AuthenticatorData.unpack(authenticator_data)

  validate_signature!(signature, client_data, authenticator_data)

  validate_rp_id!(rp_id_hash)

  raise 'Failed count check' if sign_count < count
  raise 'Failed challenge check' unless client_data_challenge == expected_challenge

  sign_count
end