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 testSignMultipleSuccess(self, mock_get_u2f_method): """Test signing with multiple keys registered and one is eligible.""" # Prepare u2f mocks mock_u2f = mock.MagicMock() mock_get_u2f_method.return_value = mock_u2f mock_authenticate = mock.MagicMock() mock_u2f.Authenticate = mock_authenticate return_value = model.SignResponse( base64.urlsafe_b64decode(SIGN_SUCCESS['key_handle_encoded']), base64.urlsafe_b64decode(SIGN_SUCCESS['signature_data_encoded']), SIGN_SUCCESS['client_data']) mock_authenticate.side_effect = [ errors.U2FError(errors.U2FError.DEVICE_INELIGIBLE), return_value ] # Call LocalAuthenticator challenge_item = { 'key': SIGN_SUCCESS['registered_key'], 'challenge': SIGN_SUCCESS['challenge'] } challenge_data = [challenge_item, challenge_item] authenticator = localauthenticator.LocalAuthenticator('testorigin') response = authenticator.Authenticate(SIGN_SUCCESS['app_id'], challenge_data) # Validate that u2f authenticate was called with the correct values self.assertTrue(mock_authenticate.called) authenticate_args = mock_authenticate.call_args[0] self.assertEqual(len(authenticate_args), 3) self.assertEqual(authenticate_args[0], SIGN_SUCCESS['app_id']) self.assertEqual(authenticate_args[1], SIGN_SUCCESS['challenge']) registered_keys = authenticate_args[2] self.assertEqual(len(registered_keys), 1) self.assertEqual(registered_keys[0], SIGN_SUCCESS['registered_key']) # Validate authenticator response self.assertEquals(response.get('clientData'), SIGN_SUCCESS['client_data_encoded']) self.assertEquals(response.get('signatureData'), SIGN_SUCCESS['signature_data_encoded']) self.assertEquals(response.get('applicationId'), SIGN_SUCCESS['app_id']) self.assertEquals(response.get('keyHandle'), SIGN_SUCCESS['key_handle_encoded'])
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 mock_authenticate.return_value = model.SignResponse( base64.urlsafe_b64decode(SIGN_SUCCESS['key_handle_encoded']), base64.urlsafe_b64decode(SIGN_SUCCESS['signature_data_encoded']), SIGN_SUCCESS['client_data']) # Call LocalAuthenticator challenge_data = [{ 'key': SIGN_SUCCESS['registered_key'], 'challenge': SIGN_SUCCESS['challenge'] }] authenticator = localauthenticator.LocalAuthenticator('testorigin') self.assertTrue(authenticator.IsAvailable()) response = authenticator.Authenticate(SIGN_SUCCESS['app_id'], challenge_data) # Validate that u2f authenticate was called with the correct values self.assertTrue(mock_authenticate.called) authenticate_args = mock_authenticate.call_args[0] self.assertEqual(len(authenticate_args), 3) self.assertEqual(authenticate_args[0], SIGN_SUCCESS['app_id']) self.assertEqual(authenticate_args[1], SIGN_SUCCESS['challenge']) registered_keys = authenticate_args[2] self.assertEqual(len(registered_keys), 1) self.assertEqual(registered_keys[0], SIGN_SUCCESS['registered_key']) # Validate authenticator response self.assertEquals(response.get('clientData'), SIGN_SUCCESS['client_data_encoded']) self.assertEquals(response.get('signatureData'), SIGN_SUCCESS['signature_data_encoded']) self.assertEquals(response.get('applicationId'), SIGN_SUCCESS['app_id']) self.assertEquals(response.get('keyHandle'), SIGN_SUCCESS['key_handle_encoded'])
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)