Cryptopals zestaw 5 ćwiczenie 36

przez | 17 sierpnia 2021

W tym zadaniu zapoznajemy się z protokołem Secure Remote Password. Protokół dział wykorzystując ideę podobną do tej zgodnie z którą działa protokół Diffielgo-Hellmana i pozwala w bezpieczny sposób uwierzytelniać się np. na serwerze.

W związku z tym, że samo zadanie polega na implementacji poszczególnych kroków podanych przez autorów, wklejam od razu swoją implementację poniżej:

def server36(config):
    # Generate salt as random integer
    # Generate string xH=SHA256(salt|password)
    # Convert xH to integer x somehow (put 0x on hexdigest)
    # Generate v=g**x % N
    # Save everything but x, xH
    SALT = random.randrange(1, config['N'])
    data_to_hash = str(SALT)+config['P']
    xH = hashlib.sha256(data_to_hash.encode())
    x = int(xH.hexdigest(), 16)
    v = power_mod(config['g'], x, config['N'])
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as socket_ma:
        socket_ma.bind((config['host'], config['port']))
        socket_ma.listen()
        conn, addr = socket_ma.accept()
        with conn:
            print("Connected: ", addr)
            data = wait_and_recv_data(conn, 4*1024)
            print("Received: ", data)
            b = random.randrange(1, config['N'])
            B = config['k']*v + power_mod(config['g'], b, config['N'])
            json_data = json.loads(data)
            A = json_data['A']
            # Send salt, B=kv + g**b % N
            resp = json.dumps({
                'salt': SALT,
                'B': B
            }).encode()
            conn.sendall(resp)
            sleep(1)
            # Compute string uH = SHA256(A|B), u = integer of uH
            # Generate S = (A * v**u) ** b % N
            # Generate K = SHA256(S)
            uH = hashlib.sha256((str(A)+str(B)).encode())
            u = int(uH.hexdigest(),16)
            S = power_mod(A*power_mod(v, u, config['N']), b, config['N'])
            K = hashlib.sha256(str(S).encode())
            # print('SRV::S: ', S)
            # print('SRV::K: ', K.hexdigest())
            data = wait_and_recv_data(conn, 4*1024)
            hmac_obj = hmac.new(K.digest(), str(SALT).encode(), hashlib.sha256)
            valid_hmac = hmac_obj.digest()
            if (valid_hmac == data):
                conn.sendall(b'OK')
            else:
                conn.sendall(b'FAIL')



def s5challenge36():
    print("Challenge 36")
    config = {
        'N': 0xffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c08ca237327ffffffffffffffff,
        'g': 2,
        'k': 3,
        'I': '[email protected]',
        'P': 'superpassword',
        'host': '127.0.0.1',
        'port': 40038
    }
    server_handle = threading.Thread(target=server36, args=(config,))
    server_handle.start()
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as socket_a:
        socket_a.connect((config["host"], config["port"]))
        # Send I, A=g**a % N (a la Diffie Hellman)
        a = random.randrange(1, config["N"])
        A = power_mod(config["g"], a, config["N"])
        session_init_data = json.dumps(
            {
                "I": config['I'],
                "A": A
            }
        ).encode()
        socket_a.sendall(session_init_data)
        resp = wait_and_recv_data(socket_a, 4096)
        print("Client received: ", resp)
        json_data = json.loads(resp)
        # Compute string uH = SHA256(A|B), u = integer of uH
        B = json_data['B']
        SALT = json_data['salt']
        uH = hashlib.sha256((str(A)+str(B)).encode())
        u = int(uH.hexdigest(),16)
        # Generate string xH=SHA256(salt|password)
        # Convert xH to integer x somehow (put 0x on hexdigest)
        # Generate S = (B - k * g**x)**(a + u * x) % N
        # Generate K = SHA256(S)
        data_to_hash = str(SALT)+config['P']
        xH = hashlib.sha256(data_to_hash.encode())
        x = int(xH.hexdigest(), 16)
        S = power_mod(B - config['k'] * power_mod(config['g'], x, config['N']), a+u*x, config['N'])
        K = hashlib.sha256(str(S).encode())
        # print("CLIENT::S: ", S)
        # print("CLIENT::K: ", K.hexdigest())
        hmac_obj = hmac.new(K.digest(), str(SALT).encode(), hashlib.sha256)
        socket_a.sendall(hmac_obj.digest())
        resp = wait_and_recv_data(socket_a, 4096)
        print("CLIENT::Srv response: ", resp)

    server_handle.join()

Powyższa implementacja prezentuje ideę działania protokołu SRP natomiast zawiera pewną istotną zmianę. W tym wypadku zarówno serwer jak i klient znają zarówno hasło jak i „sól” (losowa wartość dodawana do hasła podczas obliczania funkcji skrótu). W normalnych implementacjach klient podczas tworzenia konta na serwerze przesyła na serwer swój login, sól oraz wartość v (weryfikator). Następnym razem kiedy użytkownik chce się zalogować, na serwer przesyłany jest login a serwer znajduje przypisane do danego loginu sól i weryfikator i dalej proces przebiega już podobnie jak w powyższej implementacji. Polecam zapoznać się z ciekawą prezentacją na ten temat:

Kod dostępny w repozytorium: https://gitlab.com/akoltys/cryptopals.

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *