def u2f_authenticate(control_byte, req): # https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-raw-message-formats-v1.2-ps-20170411.html#authentication-messages global ks_u2f L = req[64] if L != KEY_HANDLE_LENGTH: return SW_WRONG_DATA if len(req) != 64 + 1 + L: return SW_CONDITIONS_NOT_SATISFIED if control_byte not in (0x03, 0x07, 0x08): return SW_CONDITIONS_NOT_SATISFIED key_handle = dec_key_handle(req[65:], req[32:64]) if key_handle == b'': return SW_WRONG_DATA if control_byte == 0x07: # check-only return SW_CONDITIONS_NOT_SATISFIED user_presemce = b'\x00' if control_byte == 0x03: # enforce-user-presence-and-sign if (up_check() is False): return SW_CONDITIONS_NOT_SATISFIED user_presemce = b'\x01' private_key = int.from_bytes(key_handle[:32], 'big', False) ks_u2f.COUNTER = (ks_u2f.COUNTER + 1) % 0x0100000000 cb = ks_u2f.COUNTER.to_bytes(4, 'big') s = sha256(req[32:64] + user_presemce + cb + req[:32]) h = int.from_bytes(s.digest(), 'big', False) signature = ecdsa_sign(secp256r1, private_key, h) return user_presemce + cb + signature + SW_NO_ERROR
def getNextAssertion(): global ks_ctap2 global NEXT_CREDENTIAL_TIMER, REM_GETASSERTION_PARAMETERS global CREDENTIALCOUNTER, NUMBEROFCREDENTIALS global REM_GETASSERTION_PARAMETERS_COMMON, REM_LAST_CMD if not REM_GETASSERTION_PARAMETERS: return CTAP2_ERR_NOT_ALLOWED if REM_LAST_CMD not in(authenticatorGetAssertion, authenticatorGetNextAssertion): return CTAP2_ERR_NOT_ALLOWED if CREDENTIALCOUNTER >= NUMBEROFCREDENTIALS: return CTAP2_ERR_NOT_ALLOWED if ticks_diff(ticks_ms(), NEXT_CREDENTIAL_TIMER) > 30000: return CTAP2_ERR_NOT_ALLOWED d, user_description, credentialID = REM_GETASSERTION_PARAMETERS.pop() rp_id_hash, clientDataHash, FLAGS, useRK = REM_GETASSERTION_PARAMETERS_COMMON # increase signature counter ks_ctap2.COUNTER = (ks_ctap2.COUNTER + 1) % 0x0100000000 cb = ks_ctap2.COUNTER.to_bytes(4, 'big') # authenticator data: https://www.w3.org/TR/webauthn/#table-authData auth_data = rp_id_hash + FLAGS + cb # compute signature s = sha256(auth_data + clientDataHash) # auth_data + client_data_hash h = int.from_bytes(s.digest(), 'big', False) signature = ecdsa_sign(secp256r1, d, h) CREDENTIALCOUNTER += 1 NEXT_CREDENTIAL_TIMER = ticks_ms() ret = {1: {'id': credentialID, 'type': 'public-key'}, 2: auth_data, 3: signature} if useRK is True: ret[4] = user_description return CTAP2_OK + encode(ret)
def test_break(self): curve_obj = asymmetric.ECC_NISTP256() priv = util.be2int(self.TEST_VECTORS['key1w']['private']) pub = tuple(map(util.be2int, self.TEST_VECTORS['key1w']['public'])) hash_func = lambda m: util.be2int(hashlib.sha256(m).digest()) hash_bits = 256 msg1 = self.TEST_VECTORS['msg1']['msg'] msg2 = self.TEST_VECTORS['msg2']['msg'] k = util.be2int(self.TEST_VECTORS['msg1']['k']) sig1 = ecdsa.ecdsa_sign(curve_obj, hash_func, hash_bits, priv, msg1, k) sig2 = ecdsa.ecdsa_sign(curve_obj, hash_func, hash_bits, priv, msg2, k) recovered = ecdsa.break_ecdsa(curve_obj, hash_func, hash_bits, sig1, sig2, msg1, msg2) self.assertEquals((k, priv), recovered)
def _test_ecdsa(self, curve_obj, priv, pub, hash_func, hash_bits, msg, k, reference_sig): self.assertEquals(pub, curve_obj.derive_public_key(priv)) sig = ecdsa.ecdsa_sign(curve_obj, hash_func, hash_bits, priv, msg, k) self.assertEquals(reference_sig, sig) # verify self.assertTrue( ecdsa.ecdsa_verify(curve_obj, hash_func, hash_bits, pub, msg, reference_sig)) self.assertTrue(not ecdsa.ecdsa_verify(curve_obj, hash_func, hash_bits, pub, 'abcdef', reference_sig))
def u2f_register(req): # https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-raw-message-formats-v1.2-ps-20170411.html#registration-messages if len(req) != 64: return SW_WRONG_LENGTH # ec key genaration d, Q = secp256r1.keyGen() user_public_key = b'\x04' + Q.x.to_bytes(32, 'big') \ + Q.y.to_bytes(32, 'big') key_handle = enc_key_handle(d.to_bytes(32, 'big'), req[32:]) s = sha256(b'\x00' + req[32:] + req[:32] + key_handle + user_public_key) h = int.from_bytes(s.digest(), 'big', False) signature = ecdsa_sign(secp256r1, PRIVATE_EC_KEY, h) return b'\x05' \ + user_public_key \ + KEY_HANDLE_LENGTH.to_bytes(1, 'big') \ + key_handle \ + CERTIFICATE_DER \ + signature \ + SW_NO_ERROR
def makeCredential(data): # https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#authenticatorMakeCredential global ks_ctap2 try: data = decode(data) except ValueError: return CTAP2_ERR_INVALID_CBOR # verify structure of parameter for authenticatorMakeCredential ret = ccp.authenticatorMakeCredential.verify(data) if ret != CTAP2_OK: return ret # user description user_description = {'id': data[3]['id']} for key in ('displayName', 'name', 'icon'): if key in data[3]: user_description[key] = data[3][key] rpId_user_description = encode([data[2]['id'], user_description]) if not {'alg': -7, 'type': 'public-key'} in data[4]: # ES256 return CTAP2_ERR_UNSUPPORTED_ALGORITHM if 5 in data: # excludeList for x in data[5]: if x['type'] != 'public-key': continue if dec_key_handle(x['id']) != b'': return CTAP2_ERR_CREDENTIAL_EXCLUDED rk, uv, up = False, False, True # default options if 7 in data: # Map of authenticator options rk = data[7].get('rk', False) uv = data[7].get('uv', False) up = data[7].get('up', True) if uv is True: return CTAP2_ERR_UNSUPPORTED_OPTION if up is False: return CTAP2_ERR_INVALID_OPTION FLAGS = 0x40 # ED | AT | 0 | 0 | 0 | uv | 0 | up if isPINset(): if 8 not in data or 9 not in data: # pinAuth return CTAP2_ERR_PIN_REQUIRED if verifyPIN(data[8], data[1]) is False: return CTAP2_ERR_PIN_AUTH_INVALID else: FLAGS |= 0x04 # make credential # user presence check if (up_check() is False): return CTAP2_ERR_OPERATION_DENIED FLAGS = int(FLAGS | 0x01).to_bytes(1, 'big') # hash rpid s = sha256(bytes(data[2]['id'], 'utf8')) rp_id_hash = s.digest() # ec key genaration d, Q = secp256r1.keyGen() if secp256r1.verify_point(Q) is False: return CTAP1_ERR_OTHER cose_key = encode({1: 2, # kty: EC2 key type 3: -7, # alg: ES256 signature algorithm -1: 1, # crv: P-256 curve -2: Q.x.to_bytes(32, 'big'), # x-coordinate -3: Q.y.to_bytes(32, 'big') # y-coordinate }) # generate key handle key_handle = enc_key_handle(d.to_bytes(32, 'big') + rpId_user_description) Lb = len(key_handle).to_bytes(2, 'big') # increase signature counter ks_ctap2.COUNTER = (ks_ctap2.COUNTER + 1) % 0x0100000000 cb = ks_ctap2.COUNTER.to_bytes(4, 'big') # authenticator data: https://www.w3.org/TR/webauthn/#fig-attStructs auth_data = rp_id_hash + FLAGS + cb + ks_ctap2.AAGUID \ + Lb + key_handle + cose_key # compute signature s = sha256(auth_data + data[1]) # auth_data + client_data_hash h = int.from_bytes(s.digest(), 'big', False) signature = ecdsa_sign(secp256r1, PRIVATE_EC_KEY, h) if rk is True: if ks_ctap2.save_rk(data[2]['id'], data[3]['id'], key_handle) is False: return CTAP2_ERR_KEY_STORE_FULL # https://www.w3.org/TR/webauthn/#sctn-attestation return CTAP2_OK + encode({1: 'packed', 2: auth_data, 3: {'alg': -7, 'sig': signature, 'x5c': [CERTIFICATE_DER]} })
def getAssertion(data): global ks_ctap2 global NUMBEROFCREDENTIALS, CREDENTIALCOUNTER global REM_GETASSERTION_PARAMETERS, NEXT_CREDENTIAL_TIMER global REM_GETASSERTION_PARAMETERS_COMMON try: data = decode(data) except ValueError: return CTAP2_ERR_INVALID_CBOR # verify structure of parameter for authenticatorGetAssertion ret = ccp.authenticatorGetAssertion.verify(data) if ret != CTAP2_OK: return ret allowList = [] len_allowList = 0 useRK = False if 3 in data: # allowList len_allowList = len(data[3]) for pkc_descriptor in data[3]: if pkc_descriptor['type'] != 'public-key': continue allowList.append(pkc_descriptor['id']) if (3 not in data) or ((3 in data) and (len_allowList == 0)): # search for residential keys for key_handle in ks_ctap2.load_rk(data[1]): useRK = True allowList.append(key_handle) if not allowList: return CTAP2_ERR_NO_CREDENTIALS # get options uv, up = False, True # default options if 5 in data: # Map of authenticator options if 'rk' in data[5]: return CTAP2_ERR_UNSUPPORTED_OPTION uv = data[5].get('uv', False) up = data[5].get('up', True) if uv is True: return CTAP2_ERR_UNSUPPORTED_OPTION FLAGS = 0 # ED | AT | 0 | 0 | 0 | uv | 0 | up if isPINset(): if 6 not in data or 7 not in data: # pinAuth return CTAP2_ERR_PIN_REQUIRED if verifyPIN(data[6], data[2]) is False: return CTAP2_ERR_PIN_AUTH_INVALID else: FLAGS |= 0x04 # make assertion REM_GETASSERTION_PARAMETERS.clear() for credId in allowList: key_data = dec_key_handle(credId) if key_data == b'': continue try: rpId, user_description = decode(key_data[32:]) except ValueError: continue if rpId != data[1]: continue # rpId does not match d = int.from_bytes(key_data[:32], 'big', False) if FLAGS & 0x04 == 0: # uv=PIN not done: remove all optional user informations user_description = {'id': user_description['id']} REM_GETASSERTION_PARAMETERS.append([d, user_description, credId]) NUMBEROFCREDENTIALS = len(REM_GETASSERTION_PARAMETERS) if not REM_GETASSERTION_PARAMETERS: return CTAP2_ERR_NO_CREDENTIALS d, user_description, credentialID = REM_GETASSERTION_PARAMETERS.pop() # user presence check if up is True: if (up_check() is False): return CTAP2_ERR_OPERATION_DENIED FLAGS |= 1 # rpIdHash s = sha256(bytes(data[1], 'utf8')) rp_id_hash = s.digest() # flags FLAGS = int(FLAGS).to_bytes(1, 'big') # increase signature counter ks_ctap2.COUNTER = (ks_ctap2.COUNTER + 1) % 0x0100000000 cb = ks_ctap2.COUNTER.to_bytes(4, 'big') # authenticator data: https://www.w3.org/TR/webauthn/#table-authData auth_data = rp_id_hash + FLAGS + cb # compute signature s = sha256(auth_data + data[2]) # auth_data + client_data_hash h = int.from_bytes(s.digest(), 'big', False) signature = ecdsa_sign(secp256r1, d, h) if not REM_GETASSERTION_PARAMETERS: REM_GETASSERTION_PARAMETERS_COMMON = rp_id_hash, data[2], FLAGS, useRK CREDENTIALCOUNTER = 1 NEXT_CREDENTIAL_TIMER = ticks_ms() # https://www.w3.org/TR/webauthn/#sctn-attestation ret = {1: {'id': credentialID, 'type': 'public-key'}, 2: auth_data, 3: signature} if useRK is True: ret[4] = user_description if NUMBEROFCREDENTIALS > 1: ret[5] = NUMBEROFCREDENTIALS return CTAP2_OK + encode(ret)
r_list.append(get_r(ecdsa.decode_sig(sig))) for i in range(len(ordered_sigs)): for j in range(i+1,len(ordered_sigs)): if(r_list[i] == r_list[j]): #find k from common r k = find_k(msgs[i],ecdsa.decode_sig(ordered_sigs[i]),msgs[j],ecdsa.decode_sig(ordered_sigs[j])) # find private key from reused nonce k pk1 = get_private_key(msgs[i],ecdsa.decode_sig(ordered_sigs[i]),k) # sign1 = ecdsa.ecdsa_sign(msgs[4], pk1,ecdsa.secp256k1, hashlib.sha256,k) # print msgs[4],ecdsa.encode_sig(sign1) # sign2 = ecdsa.ecdsa_sign(msgs[5], pk1,ecdsa.secp256k1, hashlib.sha256,k) # print msgs[5],ecdsa.encode_sig(sign2) new_msg = "Hello from aaku8856 and mifr0750" new_sign = ecdsa.ecdsa_sign(new_msg, pk1) print new_msg, ecdsa.encode_sig(new_sign) if ecdsa_verify(new_msg, pk, new_sign) == True: print 'Signature Verified!' else: print 'Error: Signature is Invalid!!!' if ecdsa_verify(new_msg + 'y', pk, new_sign) == False: print 'Correctly rejected invalid signature' else: print 'Error: verify did not reject incorrect signature'