def test_send_cbor_ok(self): ctap = CTAP2(mock.MagicMock()) ctap.device.call.return_value = b'\0' + cbor.dumps({1: b'response'}) self.assertEqual({1: b'response'}, ctap.send_cbor(2, b'foobar')) ctap.device.call.assert_called_with(0x10, b'\2' + cbor.dumps(b'foobar'), None, None)
def authenticate_begin(): if not credentials: abort(404) auth_data, state = server.authenticate_begin(credentials) session['state'] = state return cbor.dumps(auth_data)
def generate_get_info_response_message(): _AAGUID = a2b_hex('F8A011F38C0A4D15800617111F9EDC7D') # f8a011f38c0a4d15800617111f9edc7d _INFO = a2b_hex( 'a60182665532465f5632684649444f5f325f3002826375766d6b686d61632d7365637265740350f8a011f38c0a4d15800617111f9edc7d04a462726bf5627570f564706c6174f469636c69656e7450696ef4051904b0068101' ) # noqa # VERSIONS = 1 # EXTENSIONS = 2 # AAGUID = 3 # OPTIONS = 4 # MAX_MSG_SIZE = 5 # PIN_PROTOCOLS = 6 info_data = { 1: ['U2F_V2', 'FIDO_2_0'], 2: ['uvm', 'hmac-secret'], 3: _AAGUID, 4: { 'clientPin': False, 'plat': False, 'rk': True, 'up': True }, 5: 1200, 6: [1] } ret = b'\0' + cbor.dumps(info_data) #ret = b'\0' + _INFO return SW_NO_ERROR, ret
def registration_begin(user, authenticator): user_webauthn_tokens = user.credentials.filter(FidoCredential) if user_webauthn_tokens.count >= current_app.config['WEBAUTHN_MAX_ALLOWED_TOKENS']: current_app.logger.error('User tried to register more than {} tokens.'.format( current_app.config['WEBAUTHN_MAX_ALLOWED_TOKENS'])) return {'_status': 'error', 'message': 'security.webauthn.max_allowed_tokens'} creds = make_credentials(user_webauthn_tokens.to_list()) server = get_webauthn_server(current_app.config['FIDO2_RP_ID']) if user.given_name is None or user.surname is None or user.display_name is None: return {'_status': 'error', 'message': 'security.webauthn-missing-pdata'} registration_data, state = server.register_begin( { 'id': str(user.eppn).encode('ascii'), 'name': "{} {}".format(user.given_name, user.surname), 'displayName': user.display_name }, credentials=creds, user_verification=USER_VERIFICATION.DISCOURAGED, authenticator_attachment=authenticator, ) session['_webauthn_state_'] = state current_app.logger.info('User {} has started registration of a webauthn token'.format(user)) current_app.logger.debug('Webauthn Registration data: {}.'.format(registration_data)) current_app.stats.count(name='webauthn_register_begin') encoded_data = base64.urlsafe_b64encode(cbor.dumps(registration_data)).decode('ascii') encoded_data = encoded_data.rstrip('=') return { 'csrf_token': session.new_csrf_token(), 'registration_data': encoded_data }
def authenticate_begin(): if not credentials: abort(404) auth_data = server.authenticate_begin(credentials) session['challenge'] = auth_data['publicKey']['challenge'] return cbor.dumps(auth_data)
def authenticate_begin(request): server = getServer() credentials = getUserCredentials( request.session.get("base_username", request.user.username)) auth_data, state = server.authenticate_begin(credentials) request.session['fido_state'] = state return HttpResponse(cbor.dumps(auth_data), content_type="application/octet-stream")
def begin_registeration(request): server = getServer() registration_data, state = server.register_begin({ u'id': request.user.username.encode("utf8"), u'name': (request.user.first_name + " " + request.user.last_name), u'displayName': request.user.username, }, getUserCredentials(request.user.username)) request.session['fido_state'] = state return HttpResponse(cbor.dumps(registration_data))
def authenticate_complete(): data = cbor.loads(request.get_data())[0] credential_id = data['credentialId'] client_data = ClientData(data['clientDataJSON']) auth_data = AuthenticatorData(data['authenticatorData']) signature = data['signature'] credentials = session['userobj']['credentials'] fidoserver.authenticate_complete(credentials, credential_id, session.pop('challenge'), client_data, auth_data, signature) return cbor.dumps({'status': 'OK'})
def register_begin(): userobj = session['userobj'] userreq = { 'id': userobj['userid'], 'name': userobj['username'], 'displayName': userobj['username'] } credentials = userobj['credentials'] registration_data = fidoserver.register_begin(rp, userreq, credentials) session['challenge'] = registration_data['publicKey']['challenge'] return cbor.dumps(registration_data)
def register_begin(): registration_data, state = server.register_begin({ 'id': b'user_id', 'name': 'a_user', 'displayName': 'A. User', 'icon': 'https://example.com/image.png' }, credentials) session['state'] = state print('\n\n\n\n') print(registration_data) print('\n\n\n\n') return cbor.dumps(registration_data)
def register_complete(): data = cbor.loads(request.get_data())[0] client_data = ClientData(data['clientDataJSON']) att_obj = AttestationObject(data['attestationObject']) print('clientData', client_data) print('AttestationObject:', att_obj) auth_data = server.register_complete(session['state'], client_data, att_obj) credentials.append(auth_data.credential_data) print('REGISTERED CREDENTIAL:', auth_data.credential_data) return cbor.dumps({'status': 'OK'})
def register_begin(): registration_data = server.register_begin( { 'id': b'user_id', 'name': 'a_user', 'displayName': 'A. User', 'icon': 'https://example.com/image.png' }, credentials) session['challenge'] = registration_data['publicKey']['challenge'] print('\n\n\n\n') print(registration_data) print('\n\n\n\n') return cbor.dumps(registration_data)
def u2f_complete(): data = cbor.loads(request.get_data())[0] client_data = ClientData.from_b64(data['clientData']) reg_data = RegistrationData.from_b64(data['registrationData']) print('clientData', client_data) print('U2F RegistrationData:', reg_data) att_obj = AttestationObject.from_ctap1(sha256(b'https://localhost:5000'), reg_data) print('AttestationObject:', att_obj) auth_data = att_obj.auth_data credentials.append(auth_data.credential_data) print('REGISTERED U2F CREDENTIAL:', auth_data.credential_data) return cbor.dumps({'status': 'OK'})
def authenticate_complete(): if not credentials: abort(404) data = cbor.loads(request.get_data())[0] credential_id = data['credentialId'] client_data = ClientData(data['clientDataJSON']) auth_data = AuthenticatorData(data['authenticatorData']) signature = data['signature'] print('clientData', client_data) print('AuthenticatorData', auth_data) server.authenticate_complete(session.pop('state'), credentials, credential_id, client_data, auth_data, signature) print('ASSERTION OK') return cbor.dumps({'status': 'OK'})
def test_get_assertion(self): ctap = CTAP2(mock.MagicMock()) ctap.device.call.return_value = b'\0' + _GA_RESP resp = ctap.get_assertion(1, 2) ctap.device.call.assert_called_with(0x10, b'\2' + cbor.dumps({ 1: 1, 2: 2 }), None, None) self.assertIsInstance(resp, AssertionResponse) self.assertEqual(resp, _GA_RESP) self.assertEqual(resp.credential, _CRED) self.assertEqual(resp.auth_data, _AUTH_DATA_GA) self.assertEqual(resp.signature, _SIGNATURE) self.assertIsNone(resp.user) self.assertIsNone(resp.number_of_credentials)
def register_complete(): data = cbor.loads(request.get_data())[0] client_data = ClientData(data['clientDataJSON']) att_obj = AttestationObject(data['attestationObject']) val = session['challenge'] auth_data = fidoserver.register_complete(val, client_data, att_obj) # Check if key has already been registered certificateid = att_obj.auth_data.credential_data.credential_id fileid = session['userobj']['userid'] + "_" + b2a_hex(certificateid).decode() pickle.dump(att_obj.auth_data.credential_data, open("./users/" + fileid + ".p", "wb")) return cbor.dumps({'status': 'OK'})
def store_credential(cursor, user, mode, credential): """ Store credential in database :param credential: The credential to store :param user: The user the credential belongs to :param cursor: Database cursor :param mode: The FIDO2 mode :return: """ user_id = user['id'] sql_command = """ INSERT INTO credentials(credential_id, aaguid, public_key, user_id, mode) VALUES (?, ?, ?, ?, ?); """ data = (memoryview(credential.credential_id), memoryview( credential.aaguid), memoryview(dumps(credential.public_key)), memoryview(user_id), int(mode)) cursor.execute(sql_command, data) print("New credential stored")
def test_make_credential(self): ctap = CTAP2(mock.MagicMock()) ctap.device.call.return_value = b'\0' + _MC_RESP resp = ctap.make_credential(1, 2, 3, 4) ctap.device.call.assert_called_with( 0x10, b'\1' + cbor.dumps({ 1: 1, 2: 2, 3: 3, 4: 4 }), None, None) self.assertIsInstance(resp, AttestationObject) self.assertEqual(resp, _MC_RESP) self.assertEqual(resp.fmt, 'packed') self.assertEqual(resp.auth_data, _AUTH_DATA_MC) self.assertSetEqual(set(resp.att_statement.keys()), {'alg', 'sig', 'x5c'})
def autenticate_begin(): credentials = session['userobj']['credentials'] auth_data = fidoserver.authenticate_begin(rp['id'], credentials) session['challenge'] = auth_data['publicKey']['challenge'] return cbor.dumps(auth_data)
def get_config_for_bundle(self, action): if action.old_format: userid = action.user_id user = current_app.central_userdb.get_user_by_id( userid, raise_on_missing=False) else: eppn = action.eppn user = current_app.central_userdb.get_user_by_eppn( eppn, raise_on_missing=False) current_app.logger.debug('Loaded User {} from db'.format(user)) if not user: raise self.ActionError('mfa.user-not-found') credentials = _get_user_credentials(user) current_app.logger.debug('FIDO credentials for user {}:\n{}'.format( user, pprint.pformat(credentials))) # CTAP1/U2F # TODO: Only make U2F challenges for U2F tokens? challenge = None if current_app.config.get('GENERATE_U2F_CHALLENGES') is True: u2f_tokens = [v['u2f'] for v in credentials.values()] try: challenge = begin_authentication( current_app.config['U2F_APP_ID'], u2f_tokens) current_app.logger.debug('U2F challenge:\n{}'.format( pprint.pformat(challenge))) except ValueError: # there is no U2F key registered for this user pass # CTAP2/Webauthn # TODO: Only make Webauthn challenges for Webauthn tokens? webauthn_credentials = [v['webauthn'] for v in credentials.values()] fido2rp = RelyingParty(current_app.config['FIDO2_RP_ID'], 'eduID') fido2server = _get_fido2server(credentials, fido2rp) raw_fido2data, fido2state = fido2server.authenticate_begin( webauthn_credentials) current_app.logger.debug('FIDO2 authentication data:\n{}'.format( pprint.pformat(raw_fido2data))) fido2data = base64.urlsafe_b64encode( cbor.dumps(raw_fido2data)).decode('ascii') fido2data = fido2data.rstrip('=') config = {'u2fdata': '{}', 'webauthn_options': fido2data} # Save the challenge to be used when validating the signature in perform_action() below if challenge is not None: session[self.PACKAGE_NAME + '.u2f.challenge'] = challenge.json config['u2fdata'] = json.dumps(challenge.data_for_client) current_app.logger.debug( f'FIDO1/U2F challenge for user {user}: {challenge.data_for_client}' ) current_app.logger.debug( f'FIDO2/Webauthn state for user {user}: {fido2state}') session[self.PACKAGE_NAME + '.webauthn.state'] = json.dumps(fido2state) # Explicit check for boolean True if current_app.config.get('MFA_TESTING') is True: current_app.logger.info('MFA test mode is enabled') config['testing'] = True else: config['testing'] = False # Add config for external mfa auth config['eidas_url'] = current_app.config['EIDAS_URL'] config['mfa_authn_idp'] = current_app.config['MFA_AUTHN_IDP'] return config
def generate_registration_response_message(cbor_data): print(''' %s %s Got an event from some relying party! A website is asking you to register the authenticator, and it is claiming itself to be APPID with SHA256(APPID) = %s''' % (sys.argv[0], V2F_DIR, cbor_data)) if not user_says_yes('Enter yes to register'): return SW_CONDITIONS_NOT_SATISFIED, b'' print() print('Please return to the web page in 3 seconds to avoid timeout!') print() time.sleep(3) _AAGUID = b'f8a011f38c0a4d15800617111f9edc7d' _CRED_ID = b'fe3aac036d14c1e1c65518b698dd1da8f596bc33e11072813466c6bf3845691509b80fb76d59309b8d39e0a93452688f6ca3a39a76f3fc52744fb73948b15783' # noqa # private_key = ROMAN_KEY public_key = ROMAN_KEY.public_key() # print(signature) # public_key.verify(signature, data2, ec.ECDSA(hashes.SHA256())) cose_public_key = ES256.from_cryptography_key(public_key) cose_public_key_hex = b2a_hex(cbor.dumps(cose_public_key)) # cose_public_key.verify(data2, signature) attest_cred_data = b''.join([ _AAGUID, #b'F8A011F38C0A4D15800617111F9EDC7D', #AAGUID b'0040', # CRED_ID length _CRED_ID, #b'fe3aac036d14c1e1c65518b698dd1da8f596bc33e11072813466c6bf3845691509b80fb76d59309b8d39e0a93452688f6ca3a39a76f3fc52744fb73948b15783', cose_public_key_hex, #cbor2hex(pk_cose).encode(), #Credentail PubK CBORtoHEX and encoded to bytes # b'a5010203262001215820643566c206dd00227005fa5de69320616ca268043a38f08bde2e9dc45a5cafaf225820171353b2932434703726aae579fa6542432861fe591e481ea22d63997e1a5290' ]) # print(cbor_data[0].get(2)) RP_ID = cbor_data[0].get(2).get('id') RP_ID_hash = hashlib.sha256(str.encode(RP_ID)).digest() # print(b2a_hex(RP_ID_hash)) auth_data = b''.join([ b2a_hex(RP_ID_hash), #RP ID HASH #b'0021F5FC0B85CD22E60623BCD7D1CA48948909249B4776EB515154E57B66AE12' # RP ID HASH b'41', # FLAG b'00000003', # counter attest_cred_data #b'f8a011f38c0a4d15800617111f9edc7d0040fe3aac036d14c1e1c65518b698dd1da8f596bc33e11072813466c6bf3845691509b80fb76d59309b8d39e0a93452688f6ca3a39a76f3fc52744fb73948b15783a5010203262001215820643566c206dd00227005fa5de69320616ca268043a38f08bde2e9dc45a5cafaf225820171353b2932434703726aae579fa6542432861fe591e481ea22d63997e1a5290' ]) #ATT_STMT #SIGNATURE #find clientDataHash from request clientDataHash = cbor_data[0].get(1) # data_to_sign = auth_data + client_data_hash # data_to_sign = b'0021F5FC0B85CD22E60623BCD7D1CA48948909249B4776EB515154E57B66AE12010000002C' + b'7B89F12A9088B0F5EE0EF8F6718BCCC374249C31AEEBAEB79BD0450132CD536C' data_to_sign = a2b_hex(auth_data) + clientDataHash attStmt_signature_ = ROMAN_KEY.sign(data_to_sign, ec.ECDSA(hashes.SHA256())) # print("***678***") # print(b2a_hex(signature)) # print(b2a_hex(data_to_sign)) #CERTIFICATE x509 #created on init and saved in global var - see above attStmt_certificate = ROMAN_CERT statement2 = { 'alg': -7, # 'sig': a2b_hex(b'304502200D15DAF337D727AB4719B4027114A2AC43CD565D394CED62C3D9D1D90825F0B3022100989615E7394C87F4AD91F8FDAE86F7A3326DF332B3633DB088AAC76BFFB9A46B'), 'sig': attStmt_signature_, 'x5c': [attStmt_certificate.public_bytes(serialization.Encoding.DER)] # 'x5c': [a2b_hex( # b'308202B73082019FA00302010202041D31330D300D06092A864886F70D01010B0500302A3128302606035504030C1F59756269636F2050726576696577204649444F204174746573746174696F6E301E170D3138303332383036333932345A170D3139303332383036333932345A306E310B300906035504061302534531123010060355040A0C0959756269636F20414231223020060355040B0C1941757468656E74696361746F72204174746573746174696F6E3127302506035504030C1E59756269636F205532462045452053657269616C203438393736333539373059301306072A8648CE3D020106082A8648CE3D030107034200047D71E8367CAFD0EA6CF0D61E4C6A416BA5BB6D8FAD52DB2389AD07969F0F463BFDDDDDC29D39D3199163EE49575A3336C04B3309D607F6160C81E023373E0197A36C306A302206092B0601040182C40A020415312E332E362E312E342E312E34313438322E312E323013060B2B0601040182E51C0201010404030204303021060B2B0601040182E51C01010404120410F8A011F38C0A4D15800617111F9EDC7D300C0603551D130101FF04023000300D06092A864886F70D01010B050003820101009B904CEADBE1F1985486FEAD02BAEAA77E5AB4E6E52B7E6A2666A4DC06E241578169193B63DADEC5B2B78605A128B2E03F7FE2A98EAEB4219F52220995F400CE15D630CF0598BA662D7162459F1AD1FC623067376D4E4091BE65AC1A33D8561B9996C0529EC1816D1710786384D5E8783AA1F7474CB99FE8F5A63A79FF454380361C299D67CB5CC7C79F0D8C09F8849B0500F6D625408C77CBBC26DDEE11CB581BEB7947137AD4F05AAF38BD98DA10042DDCAC277604A395A5B3EAA88A5C8BB27AB59C8127D59D6BBBA5F11506BF7B75FDA7561A0837C46F025FD54DCF1014FC8D17C859507AC57D4B1DEA99485DF0BA8F34D00103C3EEF2EF3BBFEC7A6613DE')] # # noqa } attest_obj = {1: 'packed', 2: a2b_hex(auth_data), 3: statement2} att_obj = cbor2hex(attest_obj) return SW_NO_ERROR, b'\0' + a2b_hex(att_obj)
def cbor2hex(data): return b2a_hex(cbor.dumps(data)).decode()
def args(*args): """ Constructs a dict from a list of arguments for sending a CBOR command. """ if args: return dict((i, v) for i, v in enumerate(args, 1) if v is not None) return None def _parse_cbor(data): resp, rest = cbor.loads(data) if rest: raise ValueError('Extraneous data') return resp print(cbor.dumps(a2b_hex(sent))) def dict_values(d, indent=0): for key, value in d.items(): print((value), indent+1) def cbor2hex(data): return b2a_hex(cbor.dumps(data)).decode() def hex2cbor(data): return cbor.loads(a2b_hex(data)) cbor1 = {'3': 0, b'2': 0, 1: 0 }
key = CoseKey.parse(cbor.loads(_ES256_KEY)[0]) key.verify( a2b_hex( b'0021F5FC0B85CD22E60623BCD7D1CA48948909249B4776EB515154E57B66AE12010000002C' + # noqa b'7B89F12A9088B0F5EE0EF8F6718BCCC374249C31AEEBAEB79BD0450132CD536C' ), # noqa a2b_hex( b'304402202B3933FE954A2D29DE691901EB732535393D4859AAA80D58B08741598109516D0220236FBE6B52326C0A6B1CFDC6BF0A35BDA92A6C2E41E40C3A1643428D820941E0' ) # noqa ) print() print( b'a5010203262001215820643566c206dd00227005fa5de69320616ca268043a38f08bde2e9dc45a5cafaf225820171353b2932434703726aae579fa6542432861fe591e481ea22d63997e1a5290' ) print(b2a_hex(cbor.dumps(cose_public_key))) cos_key = { 1: 2, 3: -7, -1: 1, -2: a2b_hex(b'A5FD5CE1B1C458C530A54FA61B31BF6B04BE8B97AFDE54DD8CBB69275A8A1BE1' ), # noqa -3: a2b_hex(b'FA3A3231DD9DEED9D1897BE5A6228C59501E4BCD12975D3DFF730F01278EA61C' ) # noqa } print(cose_public_key)
def render(self, data, media_type=None, renderer_context=None): return cbor.dumps(data)
return b2a_hex(cbor.dumps(data)).decode() _AAGUID = a2b_hex('F8A011F38C0A4D15800617111F9EDC7D') _INFO = a2b_hex( 'a60182665532465f5632684649444f5f325f3002826375766d6b686d61632d7365637265740350f8a011f38c0a4d15800617111f9edc7d04a462726bf5627570f564706c6174f469636c69656e7450696ef4051904b0068101' ) # noqa ret1 = "ret1: " + b'\0' + _INFO + " END" print(ret1) info_data = { 'versions': ['U2F_V2', 'FIDO_2_0'], 'extensions': ['uvm', 'hmac-secret'], 'aaguid': _AAGUID, 'options': { 'clientPin': False, 'plat': False, 'rk': True, 'up': True }, 'max_msg_size': 1200, 'pin_protocols': [1] } # print(cbor.dumps(info_data)) ret2 = "ret2: " + b'\0' + cbor.dumps(info_data) + " END" # ret = b'\0' + _INFO print(ret2) # # Serialize an object as a bytestring # data = dumps(['hello', 'world']) # # # # Deserialize a bytestring # obj = loads(data)
def get_config_for_bundle(self, action): if action.old_format: userid = action.user_id user = current_app.central_userdb.get_user_by_id( userid, raise_on_missing=False) else: eppn = action.eppn user = current_app.central_userdb.get_user_by_eppn( eppn, raise_on_missing=False) current_app.logger.debug('Loaded User {} from db'.format(user)) if not user: raise self.ActionError('mfa.user-not-found') credentials = _get_user_credentials(user) current_app.logger.debug('FIDO credentials for user {}:\n{}'.format(user, pprint.pformat(credentials))) # CTAP1/U2F # TODO: Only make U2F challenges for U2F tokens? challenge = None if current_app.config.get('GENERATE_U2F_CHALLENGES') is True: u2f_tokens = [v['u2f'] for v in credentials.values()] try: challenge = begin_authentication(current_app.config['U2F_APP_ID'], u2f_tokens) current_app.logger.debug('U2F challenge:\n{}'.format(pprint.pformat(challenge))) except ValueError: # there is no U2F key registered for this user pass # CTAP2/Webauthn # TODO: Only make Webauthn challenges for Webauthn tokens? webauthn_credentials = [v['webauthn'] for v in credentials.values()] fido2rp = RelyingParty(current_app.config['FIDO2_RP_ID'], 'eduID') fido2server = _get_fido2server(credentials, fido2rp) raw_fido2data, fido2state = fido2server.authenticate_begin(webauthn_credentials) current_app.logger.debug('FIDO2 authentication data:\n{}'.format(pprint.pformat(raw_fido2data))) fido2data = base64.urlsafe_b64encode(cbor.dumps(raw_fido2data)).decode('ascii') fido2data = fido2data.rstrip('=') config = {'u2fdata': '{}', 'webauthn_options': fido2data} # Save the challenge to be used when validating the signature in perform_action() below if challenge is not None: session[self.PACKAGE_NAME + '.u2f.challenge'] = challenge.json config['u2fdata'] = json.dumps(challenge.data_for_client) current_app.logger.debug(f'FIDO1/U2F challenge for user {user}: {challenge.data_for_client}') current_app.logger.debug(f'FIDO2/Webauthn state for user {user}: {fido2state}') session[self.PACKAGE_NAME + '.webauthn.state'] = json.dumps(fido2state) # Explicit check for boolean True if current_app.config.get('MFA_TESTING') is True: current_app.logger.info('MFA test mode is enabled') config['testing'] = True else: config['testing'] = False # Add config for external mfa auth config['eidas_url'] = current_app.config['EIDAS_URL'] config['mfa_authn_idp'] = current_app.config['MFA_AUTHN_IDP'] return config