def testClientDataAuth(self): cd = model.ClientData(model.ClientData.TYP_AUTHENTICATION, 'ABCD', 'somemachine') obj = json.loads(cd.GetJson()) self.assertEquals(len(obj.keys()), 3) self.assertEquals(obj['typ'], model.ClientData.TYP_AUTHENTICATION) self.assertEquals(obj['challenge'], 'QUJDRA') self.assertEquals(obj['origin'], 'somemachine')
def testClientDataRegistration(self): cd = model.ClientData(model.ClientData.TYP_REGISTRATION, b'ABCD', 'somemachine') obj = json.loads(cd.GetJson()) self.assertEqual(len(list(obj.keys())), 3) self.assertEqual(obj['typ'], model.ClientData.TYP_REGISTRATION) self.assertEqual(obj['challenge'], 'QUJDRA') self.assertEqual(obj['origin'], 'somemachine')
def Register(self, app_id, challenge, registered_keys): """Registers app_id with the security key. Executes the U2F registration flow with the security key. Args: app_id: The app_id to register the security key against. challenge: Server challenge passed to the security key. registered_keys: List of keys already registered for this app_id+user. Returns: RegisterResponse with key_handle and attestation information in it ( encoded in FIDO U2F binary format within registration_data field). Raises: U2FError: There was some kind of problem with registration (e.g. the device was already registered or there was a timeout waiting for the test of user presence). """ client_data = model.ClientData(model.ClientData.TYP_REGISTRATION, challenge, self.origin) challenge_param = self.InternalSHA256(client_data.GetJson()) app_param = self.InternalSHA256(app_id) for key in registered_keys: try: # skip non U2F_V2 keys if key.version != u'U2F_V2': continue resp = self.security_key.CmdAuthenticate( challenge_param, app_param, key.key_handle, True) # check_only mode CmdAuthenticate should always raise some # exception raise errors.HardwareError('Should Never Happen') except errors.TUPRequiredError: # This indicates key was valid. Thus, no need to register raise errors.U2FError(errors.U2FError.DEVICE_INELIGIBLE) except errors.InvalidKeyHandleError as e: # This is the case of a key for a different token, so we just ignore it. pass except errors.HardwareError as e: raise errors.U2FError(errors.U2FError.BAD_REQUEST, e) # Now register the new key for _ in range(30): try: resp = self.security_key.CmdRegister(challenge_param, app_param) return model.RegisterResponse(resp, client_data) except errors.TUPRequiredError as e: self.security_key.CmdWink() time.sleep(0.5) except errors.HardwareError as e: raise errors.U2FError(errors.U2FError.BAD_REQUEST, e) raise errors.U2FError(errors.U2FError.TIMEOUT)
def Authenticate(self, app_id, challenge, registered_keys): """Authenticates app_id with the security key. Executes the U2F authentication/signature flow with the security key. Args: app_id: The app_id to register the security key against. challenge: Server challenge passed to the security key. registered_keys: List of keys already registered for this app_id+user. Returns: SignResponse with client_data, key_handle, and signature_data. The client data is an object, while the signature_data is encoded in FIDO U2F binary format. Raises: U2FError: There was some kind of problem with registration (e.g. the device was already registered or there was a timeout while waiting for the test of user presence.) """ client_data = model.ClientData(model.ClientData.TYP_AUTHENTICATION, challenge, self.origin) app_param = self.InternalSHA256(app_id) challenge_param = self.InternalSHA256(client_data.GetJson()) num_invalid_keys = 0 for key in registered_keys: try: if key.version != 'U2F_V2': continue for _ in range(10): try: resp = self.security_key.CmdAuthenticate( challenge_param, app_param, key.key_handle) return model.SignResponse(key.key_handle, resp, client_data) except errors.TUPRequiredError: self.security_key.CmdWink() time.sleep(0.5) except errors.InvalidKeyHandleError: num_invalid_keys += 1 continue except errors.HardwareError as e: raise errors.U2FError(errors.U2FError.BAD_REQUEST, e) if num_invalid_keys == len(registered_keys): # In this case, all provided keys were invalid. raise errors.U2FError(errors.U2FError.DEVICE_INELIGIBLE) # In this case, the TUP was not pressed. raise errors.U2FError(errors.U2FError.TIMEOUT)
def _BuildPluginRequest(self, app_id, challenge_data, origin): """Builds a JSON request in the form that the plugin expects.""" client_data_map = {} encoded_challenges = [] app_id_hash_encoded = self._Base64Encode(self._SHA256(app_id)) for challenge_item in challenge_data: key = challenge_item['key'] key_handle_encoded = self._Base64Encode(key.key_handle) raw_challenge = challenge_item['challenge'] client_data_json = model.ClientData( model.ClientData.TYP_AUTHENTICATION, raw_challenge, origin).GetJson() challenge_hash_encoded = self._Base64Encode( self._SHA256(client_data_json)) # Populate challenges list encoded_challenges.append({ 'appIdHash': app_id_hash_encoded, 'challengeHash': challenge_hash_encoded, 'keyHandle': key_handle_encoded, 'version': key.version, }) # Populate ClientData map key_challenge_pair = (key_handle_encoded, challenge_hash_encoded) client_data_map[key_challenge_pair] = client_data_json signing_request = { 'type': 'sign_helper_request', 'signData': encoded_challenges, 'timeoutSeconds': U2F_SIGNATURE_TIMEOUT_SECONDS, 'localAlways': True } return client_data_map, json.dumps(signing_request)
'4Vb0Bp2ydBCbIQu_5rNlKqPH6NK1TtnM7fA=='), 'origin': 'test_origin', 'signature_data_encoded': ('AQAAAI8wRQIhALlIPo6Hg8HwzELdYRIXnAnpsiHYCSXHex' 'CS34eiS2ixAiBt3TRmKE1A9WyMjc3JGrGI7gSPg-QzDSNL' 'aIj7JwcCTA=='), 'client_data_encoded': ('eyJjaGFsbGVuZ2UiOiAiWVhOa1ptRnpaR1kiLCAib3JpZ2luI' 'jogInRlc3Rfb3JpZ2luIiwgInR5cCI6ICJuYXZpZ2F0b3IuaW' 'QuZ2V0QXNzZXJ0aW9uIn0='), 'u2f_version': 'U2F_V2' } SIGN_SUCCESS['registered_key'] = model.RegisteredKey( base64.urlsafe_b64decode(SIGN_SUCCESS['key_handle_encoded'])) SIGN_SUCCESS['client_data'] = model.ClientData( model.ClientData.TYP_AUTHENTICATION, SIGN_SUCCESS['challenge'], SIGN_SUCCESS['origin']) @mock.patch.object(sys, 'stderr', new=mock.MagicMock()) class LocalAuthenticatorTest(unittest.TestCase): @mock.patch.object(localauthenticator.u2f, 'GetLocalU2FInterface') def testSignSuccess(self, mock_get_u2f_method): """Test successful signing with a valid key.""" # Prepare u2f mocks mock_u2f = mock.MagicMock() mock_get_u2f_method.return_value = mock_u2f mock_authenticate = mock.MagicMock() mock_u2f.Authenticate = mock_authenticate
def Authenticate(self, unused_app_id, challenge, unused_registered_keys): client_data = model.ClientData( model.ClientData.TYP_AUTHENTICATION, challenge, 'some_origin'.encode('ascii')) return model.SignResponse('key_handle', 'resp', client_data)