def recover_private_key(m: bytes, signature: DSASignature, k: int, q: int) -> int: """Recover private key from signature and subkey k""" r, s = signature h_m = m39.to_int(m28.SHA1(m).digest()) r_inv = m39.invmod(r, q) return r_inv * (s * k - h_m) % q
def magic_signature_generator(y: int, p: int, q: int) -> m43.DSASignature: """Generate a magic signature, assuming verification with g = 1 mod p""" # See https://infoscience.epfl.ch/record/99390/files/Vau96c.ps §4. # # The trick is to realise at what point we assume g = 1 mod p. Here, it's # after key generation but before validation, i,e. the value of g changes # mid-way. This means we have to work with some given public key y != 1. # # The last validation calculation is v = g^u_1 y^u_2 mod p mod q. At this # point, g = 1 mod p so this reduces to # # v = g^u_1 y^u_2 mod p mod q # = y^u_2 mod p mod q # = y^(rw) mod p mod q # = y^(r / s) mod p mod q. # # Since validation requires r = v, we solve # # r = y^(r / s) mod p mod q. # # Note that r necessarily depends on y. r = y^z mod p mod q and # s = r / z mod q is a solution, for arbitrary z != 0 mod q. z = randint(1, q - 1) z_inv = m39.invmod(z, q) r = pow(y, z, p) % q s = z_inv * r % q return m43.DSASignature(r, s)
def recover_k(message_1: dict[str, Any], message_2: dict[str, Any], q: int) -> int: m_1 = message_1["m"] m_2 = message_2["m"] s_1 = message_1["s"] s_2 = message_2["s"] return m39.invmod((s_1 - s_2) % q, q) * ((m_1 - m_2) % q) % q
def sign(m: bytes, x: int, p: int, q: int, g: int) -> DSASignature: """Sign message with DSA private key""" h_m = m39.to_int(m28.SHA1(m).digest()) r, s = 0, 0 while r == 0 or s == 0: k = randint(1, q - 1) r = pow(g, k, p) % q k_inv = m39.invmod(k, q) s = (k_inv * (h_m + x * r)) % q return DSASignature(r, s)
def verify_relaxed(m: bytes, signature: m43.DSASignature, y: int, p: int, q: int, g: int) -> bool: """Verify DSA signature without checking constraints on r, s""" r, s = signature w = m39.invmod(s, q) h_m = m39.to_int(m28.SHA1(m).digest()) u_1 = h_m * w % q u_2 = r * w % q v = pow(g, u_1, p) * pow(y, u_2, p) % p % q return v == r
def sign_relaxed(m: bytes, x: int, p: int, q: int, g: int) -> m43.DSASignature: """Sign message without checking constraints on r, s""" # The original implementation checks this and falls into # an infinite loop. h_m = m39.to_int(m28.SHA1(m).digest()) k = randint(1, q - 1) k_inv = m39.invmod(k, q) r = pow(g, k, p) % q s = (k_inv * (h_m + x * r)) % q return m43.DSASignature(r, s)
def recover_message(c: int, server: DecryptionServer) -> bytes: """Recover plaintext via homeomorphic transformation""" e, n = server.public_key # We use a random number so we can perform repeated decryptions s = randint(2, 4096) c_prime = pow(s, e, n) * c % n p_prime = m39.to_int(server.decrypt(c_prime)) s_inverse = m39.invmod(s, n) p = p_prime * s_inverse % n return m39.to_bytes(p)
def crt(a: list[int], n: list[int]) -> int: """ Given lists a_1, ..., a_k and n_1, ..., n_k, solve the system x = a_i mod n_i for all i such that 0 <= x < prod(n_i), assuming n_i are pairwise coprime. """ r = 0 N = math.prod(n) for a_i, n_i in zip(a, n, strict=True): m_s = N // n_i r += a_i * m_s * m39.invmod(m_s, n_i) return r % N
def verify(m: bytes, signature: DSASignature, y: int, p: int, q: int, g: int) -> bool: """Verify DSA signature""" r, s = signature if not 0 < r < q or not 0 < s < q: return False w = m39.invmod(s, q) h_m = m39.to_int(m28.SHA1(m).digest()) u_1 = h_m * w % q u_2 = r * w % q v = pow(g, u_1, p) * pow(y, u_2, p) % p % q return v == r