def _get_user_credentials(self, user_id): """ Get all credentials associated with a user in the current mode from the database :param user_id: The id of the user :return: List of AttestedCredentialData """ if not user_id: return [] cursor = self._db_connection.cursor() sql_command = """ SELECT credential_id, aaguid, public_key FROM credentials WHERE user_id=(?) AND mode=(?) """ cursor.execute(sql_command, (user_id, int(self.mode))) results = cursor.fetchall() cursor.close() credentials = [] if results and len(results) > 0: for result in results: credential_id = result[0] aaguid = result[1] public_key = CoseKey.parse(loads(result[2])[0]) credential = AttestedCredentialData.create( aaguid, credential_id, public_key) credentials.append(credential) return credentials
def complete_reg(request): try: data = cbor.loads(request.body)[0] client_data = ClientData(data['clientDataJSON']) att_obj = AttestationObject((data['attestationObject'])) server = getServer() auth_data = server.register_complete(request.session['fido_state'], client_data, att_obj) encoded = websafe_encode(auth_data.credential_data) uk = User_Keys() uk.username = request.user.username uk.properties = { "device": encoded, "type": att_obj.fmt, } uk.key_type = "FIDO2" uk.save() return HttpResponse(simplejson.dumps({'status': 'OK'})) except Exception as exp: from raven.contrib.django.raven_compat.models import client import traceback client.captureException() return HttpResponse( simplejson.dumps({ 'status': 'ERR', "message": "Error on server, please try again later" }))
def authenticate_complete(request): credentials = [] username=request.session.get("base_username",request.user.username) server=getServer() credentials=getUserCredentials(username) data = cbor.loads(request.body)[0] credential_id = data['credentialId'] client_data = ClientData(data['clientDataJSON']) auth_data = AuthenticatorData(data['authenticatorData']) signature = data['signature'] cred = server.authenticate_complete( request.session.pop('fido_state'), credentials, credential_id, client_data, auth_data, signature ) keys = User_Keys.objects.filter(username=username, key_type="FIDO2",enabled=1) import random for k in keys: if AttestedCredentialData(websafe_decode(k.properties["device"])).credential_id == cred.credential_id: k.last_used = timezone.now() k.save() mfa = {"verified": True, "method": "FIDO2"} if getattr(settings, "MFA_RECHECK", False): mfa["next_check"] = int((datetime.datetime.now()+ datetime.timedelta( seconds=random.randint(settings.MFA_RECHECK_MIN, settings.MFA_RECHECK_MAX))).strftime("%s")) request.session["mfa"] = mfa res=login(request) return HttpResponse(simplejson.dumps({'status':"OK","redirect":res["location"]}),content_type="application/json") return HttpResponse(simplejson.dumps({'status': "err"}))
def test_vectors(self): for (data, value) in _TEST_VECTORS: try: self.assertEqual(cbor.loads(a2b_hex(data)), (value, b'')) self.assertEqual(cbor2hex(value), data) except Exception: print('\nERROR in test vector, %s' % data) raise
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_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 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_info(self, ): with Test("Get info"): info = self.ctap.get_info() print(bytes(info)) print(cbor.loads(bytes(info))) with Test("Check FIDO2 string is in VERSIONS field"): assert "FIDO_2_0" in info.versions with Test("Check pin protocols field"): if len(info.pin_protocols): assert sum(info.pin_protocols) > 0 with Test("Check options field"): for x in info.options: assert info.options[x] in [True, False] if "uv" in info.options: if info.options["uv"]: self.testMC( "Send MC request with uv set to true, expect SUCCESS", cdh, rp, user, key_params, other={"options": { "uv": True }}, expectedError=CtapError.ERR.SUCCESS, ) if "up" in info.options: if info.options["up"]: self.testMC( "Send MC request with up set to true, expect INVALID_OPTION", cdh, rp, user, key_params, other={"options": { "up": True }}, expectedError=CtapError.ERR.INVALID_OPTION, )
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 _add_token_to_user(self, registration_data, state): data = registration_data + (b'=' * (len(registration_data) % 4)) data = base64.urlsafe_b64decode(data) data = cbor.loads(data)[0] client_data = ClientData(data['clientDataJSON']) attestation = data['attestationObject'] att_obj = AttestationObject(attestation) server = get_webauthn_server(self.app.config.get('FIDO2_RP_ID')) auth_data = server.register_complete(state, client_data, att_obj) cred_data = auth_data.credential_data cred_id = cred_data.credential_id credential = Webauthn( keyhandle = cred_id.hex(), credential_data = base64.urlsafe_b64encode(cred_data).decode('ascii'), app_id = self.app.config['FIDO2_RP_ID'], attest_obj = base64.b64encode(attestation).decode('ascii'), description = 'ctap1 token', application = 'test_security' ) self.test_user.credentials.add(credential) self.app.central_userdb.save(self.test_user, check_sync=False) return credential
def TestCborKeysSorted(cbor_obj): # Cbor canonical ordering of keys. # https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#ctap2-canonical-cbor-encoding-form if isinstance(cbor_obj, bytes): cbor_obj = cbor.loads(cbor_obj)[0] if isinstance(cbor_obj, dict): l = [x for x in cbor_obj] else: l = cbor_obj l_sorted = sorted(l[:], key=cmp_to_key(cmp_cbor_keys)) for i in range(len(l)): if not isinstance(l[i], (str, int)): raise ValueError(f"Cbor map key {l[i]} must be int or str for CTAP2") if l[i] != l_sorted[i]: raise ValueError(f"Cbor map item {i}: {l[i]} is out of order") return l
# data = b'this is some data Id like to sign' # print(b2a_hex(data)) data2 = b'0021F5FC0B85CD22E60623BCD7D1CA48948909249B4776EB515154E57B66AE12010000002C' + b'7B89F12A9088B0F5EE0EF8F6718BCCC374249C31AEEBAEB79BD0450132CD536C' signature = private_key.sign(data2, ec.ECDSA(hashes.SHA256())) print(signature) public_key.verify(signature, data2, ec.ECDSA(hashes.SHA256())) cose_public_key = ES256.from_cryptography_key(public_key) cose_public_key.verify(data2, signature) #compre to test pubK from fido2 import cbor _ES256_KEY = a2b_hex( b'A5010203262001215820A5FD5CE1B1C458C530A54FA61B31BF6B04BE8B97AFDE54DD8CBB69275A8A1BE1225820FA3A3231DD9DEED9D1897BE5A6228C59501E4BCD12975D3DFF730F01278EA61C' ) # noqa 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)))
def process_u2fraw_request(raw_request): print('process_u2fraw_request - raw_request: ', raw_request) try: apducmd = decode_apdu_commandROMAN(raw_request) print( 'apducmd = decode_apdu_command(raw_request) passed and apducmd.ins is ' + str(apducmd.ins) + " with apducmd.cla " + str(apducmd.cla)) if (apducmd.cla == U2F_REGISTER): print("ROMAN apducmd.CLA or .INS == FIDO2_REGISTER") print(b2a_hex(apducmd.data)) cbor_data = cbor.loads(apducmd.data) print((cbor_data)) #print("apducmd.data: " + cbor.loads(apducmd.data.decode())) #assert len(apducmd.data) == 64 # application_parameter = apducmd.data[32:] # parse reg # challenge_parameter = apducmd.data[:32] #sw, resp = generate_registration_response_message(application_parameter, challenge_parameter) #create response sw, resp = generate_registration_response_message(cbor_data) elif (apducmd.ins == U2F_REGISTER): print("ROMAN_U2F apducmd.INS == U2F_REGISTER") print(b2a_hex(apducmd.data)) # cbor_data = cbor.loads(apducmd.data) # print((cbor_data)) #print("apducmd.data: " + cbor.loads(apducmd.data.decode())) #assert len(apducmd.data) == 64 # application_parameter = apducmd.data[32:] # parse reg # challenge_parameter = apducmd.data[:32] #sw, resp = generate_registration_response_message(application_parameter, challenge_parameter) #create response sw, resp = generate_registration_u2f_response_message(apducmd.data) elif apducmd.cla == U2F_AUTHENTICATE: # and apducmd.p1 == 0x07: print("ROMAN apducmd.INS == FIDO2_AUTHENTICATE") cbor_data = cbor.loads(apducmd.data) print(cbor_data) # assert len(apducmd.data) >= 65 # assert len(apducmd.data[65:]) == apducmd.data[64] # print("*************666*************") #There are two Auth mode; for check OR full auth; # sw, resp = generate_key_handle_checking_response(apducmd.data[32:64], apducmd.data[65:]) sw, resp = generate_authentication_response_message(cbor_data) elif apducmd.cla == U2F_VERSION: print("ROMAN apducmd.CLA == FIDO2_CANCEL") # assert len(apducmd.data) == 0 sw, resp = generate_get_version_response_message() #sw, resp = SW_INS_NOT_SUPPORTED, b'' elif apducmd.ins == U2F_VERSION: print("apducmd.ins == U2F_VERSION") assert len(apducmd.data) == 0 sw, resp = generate_get_version_response_message() elif apducmd.ins == U2F_REGISTER: print("apducmd.ins == U2F_REGISTER") assert len(apducmd.data) == 64 application_parameter = apducmd.data[32:] # parse reg challenge_parameter = apducmd.data[:32] sw, resp = generate_registration_response_message( application_parameter, challenge_parameter) #create response to reg elif apducmd.ins == U2F_AUTHENTICATE and apducmd.p1 == 0x07: print("apducmd.ins == U2F_AUTHENTICATE") assert len(apducmd.data) >= 65 assert len(apducmd.data[65:]) == apducmd.data[64] sw, resp = generate_key_handle_checking_response( apducmd.data[32:64], apducmd.data[65:]) elif apducmd.ins == U2F_AUTHENTICATE and apducmd.p1 == 0x03: print("apducmd.ins == U2F_AUTHENTICATE") assert len(apducmd.data) >= 65 assert len(apducmd.data[65:]) == apducmd.data[64] sw, resp = generate_authentication_response_message( apducmd.data[32:64], apducmd.data[0:32], apducmd.data[65:]) elif apducmd.ins == U2F_GETINFO: print("apducmd.ins == U2F_GETINFO") #assert len(apducmd.data) == 0 sw, resp = generate_get_info_response_message() else: print("apducmd.ins == ELSE") sw, resp = SW_INS_NOT_SUPPORTED, b'' except AssertionError: sw, resp = SW_WRONG_DATA, b'' # ~ print('***Response from process_u2fraw_request + sw.to_bytes(2, \'big\')***: ', resp + sw.to_bytes(2, 'big') ) return resp #+ sw.to_bytes(2, 'big')
def hex2cbor(data): return cbor.loads(a2b_hex(data))
def public_key(self): return cose.CoseKey.parse(cbor.loads(bytes(self._public_key))[0])
def parse(self, stream, media_type=None, parser_context=None): return cbor.loads(stream.read())
def _parse_cbor(data): resp, rest = cbor.loads(data) if rest: raise ValueError('Extraneous data') return resp