def generate_key(key_size: int) -> tuple[tuple[int, int], tuple[int, int]]:
    print("Generating prime p...")
    p = rabinMiller.generate_large_prime(key_size)
    print("Generating prime q...")
    q = rabinMiller.generate_large_prime(key_size)
    n = p * q

    print("Generating e that is relatively prime to (p - 1) * (q - 1)...")
    while True:
        e = random.randrange(2 ** (key_size - 1), 2 ** (key_size))
        if cryptoMath.gcd(e, (p - 1) * (q - 1)) == 1:
            break

    print("Calculating d that is mod inverse of e...")
    d = cryptoMath.find_mod_inverse(e, (p - 1) * (q - 1))

    public_key = (n, e)
    private_key = (n, d)
    return (public_key, private_key)