def setUp(self): self.setUp_user_realms() set_policy(name="WebAuthn", scope=SCOPE.ENROLL, action='{0!s}={1!s},{2!s}={3!s}'.format(WEBAUTHNACTION.RELYING_PARTY_NAME, self.rp_name, WEBAUTHNACTION.RELYING_PARTY_ID, self.rp_id)) set_privacyidea_config(WEBAUTHNCONFIG.APP_ID, self.app_id) self.user = User(login='******', realm=self.realm1, resolver=self.resolvername1) # TODO: extract token enrollment into a local function # init token step 1 self.token1 = init_token({'type': 'webauthn', 'serial': self.serial1}, user=self.user) # TODO: use mocking to set nonce with patch('privacyidea.lib.tokens.webauthntoken.WebAuthnTokenClass._get_nonce') as mock_nonce: mock_nonce.return_value = webauthn_b64_decode(self.nonce1) res = self.token1.get_init_detail(self.init_params, self.user) self.assertEqual(self.serial1, res['serial'], res) self.assertEqual(self.nonce1, res['webAuthnRegisterRequest']['nonce'], res) # init token step 2 self.token1.update({ 'type': 'webauthn', 'serial': self.serial1, 'regdata': self.reg_data1, 'clientdata': self.client_data1, WEBAUTHNACTION.RELYING_PARTY_ID: self.rp_id, WEBAUTHNACTION.AUTHENTICATOR_ATTESTATION_LEVEL: ATTESTATION_LEVEL.NONE, 'HTTP_ORIGIN': self.app_id }) res = self.token1.get_init_detail() self.assertEqual('Yubico U2F EE Serial 61730834', res['webAuthnRegisterResponse']['subject'], res) # enroll the second webauthn token # init token step 1 self.token2 = init_token({'type': 'webauthn', 'serial': self.serial2}, user=self.user) with patch('privacyidea.lib.tokens.webauthntoken.WebAuthnTokenClass._get_nonce') as mock_nonce: mock_nonce.return_value = webauthn_b64_decode(self.nonce2) res = self.token2.get_init_detail(self.init_params, self.user) self.assertEqual(self.serial2, res['serial'], res) self.assertEqual(self.nonce2, res['webAuthnRegisterRequest']['nonce'], res) # init token step 2 self.token2.update({ 'type': 'webauthn', 'serial': self.serial2, 'regdata': self.reg_data2, 'clientdata': self.client_data2, WEBAUTHNACTION.RELYING_PARTY_ID: self.rp_id, WEBAUTHNACTION.AUTHENTICATOR_ATTESTATION_LEVEL: ATTESTATION_LEVEL.NONE, 'HTTP_ORIGIN': self.app_id }) res = self.token2.get_init_detail() self.assertEqual('Yubico U2F EE Serial 23925734103241087', res['webAuthnRegisterResponse']['subject'], res)
def test_03_token_update(self): with patch('privacyidea.lib.tokens.webauthntoken.WebAuthnTokenClass._get_nonce') as mock_nonce: mock_nonce.return_value = webauthn_b64_decode(REGISTRATION_CHALLENGE) self.token.get_init_detail(self.init_params, self.user) self.token.update({ 'type': 'webauthn', 'serial': self.token.token.serial, 'regdata': REGISTRATION_RESPONSE_TMPL['attObj'], 'clientdata': REGISTRATION_RESPONSE_TMPL['clientData'], WEBAUTHNACTION.RELYING_PARTY_ID: RP_ID, WEBAUTHNACTION.AUTHENTICATOR_ATTESTATION_LEVEL: ATTESTATION_LEVEL.NONE, 'HTTP_ORIGIN': ORIGIN }) web_authn_registration_response = self.token.get_init_detail().get("webAuthnRegisterResponse") self.assertTrue(web_authn_registration_response .get("subject") .startswith("Yubico U2F EE Serial")) self.assertTrue(self .token .get_tokeninfo(WEBAUTHNINFO.ATTESTATION_ISSUER) .startswith("CN=Yubico U2F Root CA Serial")) self.assertTrue(self .token .get_tokeninfo(WEBAUTHNINFO.ATTESTATION_SUBJECT) .startswith("CN=Yubico U2F EE Serial")) self.assertEqual(CRED_ID, self.token.decrypt_otpkey()) self.assertEqual(PUB_KEY, self.token.get_tokeninfo(WEBAUTHNINFO.PUB_KEY))
def test_07_none_attestation(self): self.init_params['nonce'] = webauthn_b64_decode( NONE_ATTESTATION_REGISTRATION_CHALLENGE) self.init_params[ WEBAUTHNACTION.AUTHENTICATOR_ATTESTATION_FORM] = 'none' self.user = User(login=NONE_ATTESTATION_USER_NAME) self.token.get_init_detail(self.init_params, self.user) self.token.update({ 'type': 'webauthn', 'serial': self.token.token.serial, 'regdata': NONE_ATTESTATION_REGISTRATION_RESPONSE_TMPL['attObj'], 'clientdata': NONE_ATTESTATION_REGISTRATION_RESPONSE_TMPL['clientData'], WEBAUTHNACTION.RELYING_PARTY_ID: RP_ID, WEBAUTHNACTION.AUTHENTICATOR_ATTESTATION_LEVEL: ATTESTATION_LEVEL.NONE, 'HTTP_ORIGIN': ORIGIN }) web_authn_registration_response = self.token.get_init_detail().get( 'webAuthnRegisterResponse') self.assertEqual(NONE_ATTESTATION_CRED_ID, self.token.decrypt_otpkey()) self.assertEqual(NONE_ATTESTATION_PUB_KEY, self.token.get_tokeninfo(WEBAUTHNINFO.PUB_KEY))
def _create_challenge(self): self.token.set_otpkey(hexlify_and_unicode( webauthn_b64_decode(CRED_ID))) self.token.add_tokeninfo(WEBAUTHNINFO.PUB_KEY, PUB_KEY) self.token.add_tokeninfo(WEBAUTHNINFO.RELYING_PARTY_ID, RP_ID) (_, _, _, response_details) = self.token.create_challenge( options=self.challenge_options) return response_details
def test_06_missing_origin(self): self.challenge_options['nonce'] = webauthn_b64_decode(ASSERTION_CHALLENGE) self._create_challenge() sign_count = self.token.check_otp(otpval=None, options={ "credentialid": CRED_ID, "authenticatordata": ASSERTION_RESPONSE_TMPL['authData'], "clientdata": ASSERTION_RESPONSE_TMPL['clientData'], "signaturedata": ASSERTION_RESPONSE_TMPL['signature'], "user": self.user, "challenge": hexlify_and_unicode(self.challenge_options['nonce']), "HTTP_ORIGIN": '', }) self.assertTrue(sign_count == -1)
def test_08_missing_attestation(self): self.init_params['nonce'] = webauthn_b64_decode(NONE_ATTESTATION_REGISTRATION_CHALLENGE) self.user = User(login=NONE_ATTESTATION_USER_NAME) self.token.get_init_detail(self.init_params, self.user) with self.assertRaises(RegistrationRejectedException): self.token.update({ 'type': 'webauthn', 'serial': self.token.token.serial, 'regdata': NONE_ATTESTATION_REGISTRATION_RESPONSE_TMPL['attObj'], 'clientdata': NONE_ATTESTATION_REGISTRATION_RESPONSE_TMPL['clientData'], WEBAUTHNACTION.RELYING_PARTY_ID: RP_ID, WEBAUTHNACTION.AUTHENTICATOR_ATTESTATION_LEVEL: ATTESTATION_LEVEL.UNTRUSTED, 'HTTP_ORIGIN': ORIGIN })
def test_05_no_user_presence_fail_assertion(self): webauthn_assertion_response = self.getAssertionResponse() auth_data = webauthn_b64_decode( webauthn_assertion_response.assertion_response['authData']) flags = struct.unpack('!B', auth_data[32:33])[0] flags = flags & ~AuthenticatorDataFlags.USER_PRESENT auth_data = auth_data[:32] + struct.pack('!B', flags) + auth_data[33:] webauthn_assertion_response.assertion_response[ 'authData'] = webauthn_b64_encode(auth_data) # FIXME This *should* fail because UP=0, but will fail anyway later on because the signature is invalid. # TODO Build a mock Authenticator implementation, to be able to sign arbitrary authenticator data statements. # TODO Sign an authenticator data statement with UP=0 and test against that so that the signature is valid. with self.assertRaises(AuthenticationRejectedException): webauthn_assertion_response.verify()
def test_07_webauthn_b64_decode(self): self.assertEqual(webauthn_b64_decode(URL_DECODE_TEST_STRING), URL_DECODE_EXPECTED_RESULT)