def user(): start = yield [] assert start is Start a = dh_secret(P) A = mod(G, a) print("user: sending email") salt, B = yield [b"*****@*****.**", A] u = hash_to_int(int_to_bytes(A) + int_to_bytes(B)) print("user: A:", A) print("user: B:", B) print("user: salt:", bytes_to_int(salt)) print("user: u", u) x = hash_to_int(salt + b"pass") print("user: x:", x) t1 = B - K * mod(G, x) assert t1 > 0 S = mod(t1, a + u * x) print("user: S:", S) key = hashlib.sha256(int_to_bytes(S)).digest() print("user: key:", binascii.hexlify(key)) response, *_ = yield [hmac_sha256(key, salt)] print("server says password was", response) assert response == "OK"
def user(): start = yield [] assert start is actors.Start a = dh_secret(P) A = mod(G, a) salt, B, u = yield [b"*****@*****.**", A] x = hash_to_int(salt + b"pass") S = mod(B, a + u * x) key = hashlib.sha256(int_to_bytes(S)).digest() response, *_ = yield [hmac_sha256(key, salt)] print("=" * 80) print("user: A:", A) print("user: B:", B) print("user: salt:", bytes_to_int(salt)) print("user: u", u) print("user: x:", x) print("user: S:", S) print("user: key:", binascii.hexlify(key)) print("user: server says password was", response) assert response == "OK"
def host(): email, A = yield [] password = USERS[email] salt = random_bytes(16) x = hash_to_int(salt + password) print("s: x:", x) v = mod(G, x) b = dh_secret(P) B = K * v + mod(G, b) print("{} trying to log in".format(email.decode())) u = hash_to_int(int_to_bytes(A) + int_to_bytes(B)) print("host: A:", A) print("host: B:", B) print("host: salt:", bytes_to_int(salt)) print("host: u", u) print("host: x:", x) user_mac, *_ = yield [salt, B] t0 = A * mod(v, u) S = mod(t0, b) print("host: S:", S) key = hashlib.sha256(int_to_bytes(S)).digest() print("host: key:", binascii.hexlify(key)) host_mac = hmac_sha256(key, salt) yield ["OK" if user_mac == host_mac else "NO"]
def host(): email, A = yield [] password = USERS[email] salt = random_bytes(16) x = hash_to_int(salt + password) v = mod(G, x) b = dh_secret(P) B = mod(G, b) u = random_int_from_n_bytes(128 // 8) user_mac, *_ = yield [salt, B, u] S = mod(A * mod(v, u), b) key = hashlib.sha256(int_to_bytes(S)).digest() host_mac = hmac_sha256(key, salt) print("=" * 80) print("{} trying to log in".format(email.decode())) print("host: A:", A) print("host: B:", B) print("host: salt:", bytes_to_int(salt)) print("host: u", u) print("host: x:", x) print("host: S:", S) print("host: key:", binascii.hexlify(key)) yield ["OK" if user_mac == host_mac else "NO"]
def dec_key_handle(data, application_parameter): global ks_u2f if len(data) != KEY_HANDLE_LENGTH: return b'' if data[-32:] != hmac_sha256(ks_u2f.KEY_5C, ks_u2f.KEY_36, data[:-32] + application_parameter): return b'' dec = aes(ks_u2f.AES_KEY, MODE_CBC, ks_u2f.AES_IV) return dec.decrypt(data[:32])
def test_s4c31(self): eq = self.assertEqual eq( hmac.hmac_md5(b"", b""), "74e6f7298a9c2d168935f58c001bad88", ) eq( hmac.hmac_sha1(b"", b""), "fbdb1d1b18aa6c08324b7d64b71fb76370690e1d", ) eq( hmac.hmac_sha256(b"", b""), "b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad", ) eq( hmac.hmac_md5(b"key", b"The quick brown fox jumps over the lazy dog"), "80070713463e7749b90c2dc24911e275", ) eq( hmac.hmac_sha1(b"key", b"The quick brown fox jumps over the lazy dog"), "de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9", ) eq( hmac.hmac_sha256(b"key", b"The quick brown fox jumps over the lazy dog"), "f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8", ) self.assertTrue(insecure_compare(0.0, "abcd", "abcd")) self.assertFalse(insecure_compare(0.0, "abcd", "abce")) self.assertFalse(insecure_compare(0.0, "aBcd", "abcd")) self.assertFalse(insecure_compare(0.0, "abc", "abcd")) self.assertFalse(insecure_compare(0.0, "abcd", "abc"))
def dec_key_handle(data): if len(data) < 64 or len(data) % 16 > 0: return b'' if data[-32:] != hmac_sha256(ks_ctap2.KEY_5C, ks_ctap2.KEY_36, data[:-32]): return b'' dec = aes(ks_ctap2.AES_KEY, MODE_CBC, ks_ctap2.AES_IV) m = dec.decrypt(data[:-32]) # remove padding 80 00 00 ... for i in range(len(m) - 1, 31, -1): if m[i] == 0x80: return m[:i] elif m[i] == 0x00: continue else: return b'' # wrong padding return b''
def user(): start = yield [] assert start is actors.Start A = 0 print("user: sending email") salt, B = yield [b"*****@*****.**", A] S = 0 print("user: S:", S) key = hashlib.sha256(int_to_bytes(S)).digest() print("user: key:", binascii.hexlify(key)) response, *_ = yield [hmac_sha256(key, salt)] print("server says password was", response) assert response == "OK"
def verifyPIN(pinAuth, clientDataHash): n = -(len(ks_pin.PIN_TOKEN)) % 64 k5c = bytes((c ^ 0x5c for c in ks_pin.PIN_TOKEN)) + b'\x5c' * n k36 = bytes((c ^ 0x36 for c in ks_pin.PIN_TOKEN)) + b'\x36' * n return hmac_sha256(k5c, k36, clientDataHash)[:16] == pinAuth
def clientPIN(data): global ks_pin, PIN_CONSECUTIVE_RETRIES # https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#authenticatorClientPIN try: data = decode(data) except ValueError: return CTAP2_ERR_INVALID_CBOR ret = ccp.authenticatorClientPIN.verify(data) if ret != CTAP2_OK: return ret if data[2] == 0x01: # getRetries return CTAP2_OK + encode({3: ks_pin.PIN_RETRIES}) elif data[2] == 0x02: # getKeyAgreement return CTAP2_OK + encode({1: {1: 2, # kty: EC2 key type 3: -25, # alg: ECDH-ES+HKDF-256 -1: 1, # crv: P-256 curve # x-coordinate -2: ks_pin.DH_PK_x, # y-coordinate -3: ks_pin.DH_PK_y } }) elif data[2] in (0x03, 0x04, 0x05): # verify parameters for setPIN, changePIN, getPINToken if 3 not in data: # platformKeyAgreementKey return CTAP2_ERR_MISSING_PARAMETER if (data[2] in (0x03, 0x04)): if 4 not in data or 5 not in data: # pinAuth, newPinEnc return CTAP2_ERR_MISSING_PARAMETER if (data[2] in (0x04, 0x05)): if 6 not in data: # pinHashEnc return CTAP2_ERR_MISSING_PARAMETER if (data[2] == 0x03 and ks_pin.PIN != b'') \ or (data[2] in (0x04, 0x05) and ks_pin.PIN == b''): # either setPIN command and PIN already set # or changePIN/getPINToken command and PIN not yet set return CTAP2_ERR_PIN_NOT_SET Q = point(int.from_bytes(data[3][-2], 'big', False), int.from_bytes(data[3][-3], 'big', False)) if secp256r1.verify_point(Q) is False: return CTAP1_ERR_OTHER # compute shared secret as SHA-256(Q.x) d = int.from_bytes(ks_pin.DH_SK, 'big', False) shared_secret = sha256( secp256r1.kP(d, Q).x.to_bytes(32, 'big')).digest() k5c = bytes((c ^ 0x5c for c in shared_secret)) + b'\x5c' * 32 k36 = bytes((c ^ 0x36 for c in shared_secret)) + b'\x36' * 32 if data[2] == 0x03: # setPIN # Authenticator verifies pinAuth by generating # LEFT(HMAC-SHA-256(sharedSecret, newPinEnc), 16) # and matching against input pinAuth parameter. if hmac_sha256(k5c, k36, data[5])[:16] != data[4]: return CTAP2_ERR_PIN_AUTH_INVALID # Authenticator decrypts newPinEnc using above "sharedSecret" # producing newPin and checks newPin length against minimum # PIN length of 4 bytes. return set_new_pin(shared_secret, data[5]) elif data[2] == 0x04: # changePIN # If the retries counter is 0, return CTAP2_ERR_PIN_BLOCKED error. if ks_pin.PIN_RETRIES == 0: return CTAP2_ERR_PIN_BLOCKED if PIN_CONSECUTIVE_RETRIES == 3: return CTAP2_ERR_PIN_AUTH_BLOCKED # Authenticator verifies pinAuth by generating # LEFT(HMAC-SHA-256(sharedSecret, newPinEnc || pinHashEnc), 16) # and matching against input pinAuth parameter. if hmac_sha256(k5c, k36, data[5] + data[6])[:16] != data[4]: return CTAP2_ERR_PIN_AUTH_INVALID # Authenticator decrements the retries counter by 1. ks_pin.PIN_RETRIES -= 1 PIN_CONSECUTIVE_RETRIES += 1 ks_pin.save_keystore() # Authenticator decrypts pinHashEnc and verifies against its # internal stored LEFT(SHA-256(curPin), 16). if len(data[6]) != 16: return CTAP1_ERR_OTHER dec = aes(shared_secret, MODE_CBC, bytes(16)) if dec.decrypt(data[6]) != ks_pin.PIN_DIGEST: if ks_pin.PIN_RETRIES == 0: return CTAP2_ERR_PIN_BLOCKED elif PIN_CONSECUTIVE_RETRIES == 3: return CTAP2_ERR_PIN_AUTH_BLOCKED else: return CTAP2_ERR_PIN_INVALID # Authenticator sets the retries counter to 8. ks_pin.PIN_RETRIES = ks_pin.PIN_MAX_RETRIES ks_pin.save_keystore() PIN_CONSECUTIVE_RETRIES = 0 # Authenticator decrypts newPinEnc using above "sharedSecret" # producing newPin and checks newPin length against minimum # PIN length of 4 bytes. return set_new_pin(shared_secret, data[5]) elif data[2] == 0x05: # getPINToken # If the retries counter is 0, return CTAP2_ERR_PIN_BLOCKED error. if ks_pin.PIN_RETRIES == 0: return CTAP2_ERR_PIN_BLOCKED if PIN_CONSECUTIVE_RETRIES == 3: return CTAP2_ERR_PIN_AUTH_BLOCKED # Authenticator decrements the retries counter by 1. ks_pin.PIN_RETRIES -= 1 ks_pin.save_keystore() PIN_CONSECUTIVE_RETRIES += 1 # Authenticator decrypts pinHashEnc and verifies against its # internal stored LEFT(SHA-256(curPin), 16). if len(data[6]) != 16: return CTAP1_ERR_OTHER dec = aes(shared_secret, MODE_CBC, bytes(16)) if dec.decrypt(data[6]) != ks_pin.PIN_DIGEST: if ks_pin.PIN_RETRIES == 0: return CTAP2_ERR_PIN_BLOCKED elif PIN_CONSECUTIVE_RETRIES == 3: return CTAP2_ERR_PIN_AUTH_BLOCKED else: return CTAP2_ERR_PIN_INVALID # Authenticator sets the retries counter to 8. ks_pin.PIN_RETRIES = ks_pin.PIN_MAX_RETRIES ks_pin.save_keystore() PIN_CONSECUTIVE_RETRIES = 0 # Authenticator returns encrypted pinToken using # "sharedSecret": AES256-CBC(sharedSecret, IV=0, pinToken). ks_pin.PIN_TOKEN = urandom(16) ks_pin.save_keystore() enc = aes(shared_secret, MODE_CBC, bytes(16)) return CTAP2_OK + encode({2: enc.encrypt(ks_pin.PIN_TOKEN)})
def enc_key_handle(data): # add padding data 80 00 00 ... enc = aes(ks_ctap2.AES_KEY, MODE_CBC, ks_ctap2.AES_IV) cipher = enc.encrypt(data + b'\x80' + bytes(-(1 + len(data)) % 16)) return cipher + hmac_sha256(ks_ctap2.KEY_5C, ks_ctap2.KEY_36, cipher)
def enc_key_handle(data, application_parameter): global ks_u2f enc = aes(ks_u2f.AES_KEY, MODE_CBC, ks_u2f.AES_IV) cipher = enc.encrypt(data) return cipher + hmac_sha256(ks_u2f.KEY_5C, ks_u2f.KEY_36, cipher + application_parameter)