def sign(self, msg: bytes, k: int = None) -> bytes: """Sign a message `msg` with DSA and return the signature tuple `(r, s)` """ r = s = 0 while r == 0 or s == 0: k = k or randint(1, self.q - 1) r = modpow(self.g, k, self.p) % self.q hm = int.from_bytes(sha1(msg), 'big') s = (modinv(k, self.q) * (hm + self.x * r)) % self.q return r, s
def verify(self, msg: bytes, r: int, s: int) -> bool: """Verify a signature `(r, s)` is for a given `msg`. """ valid = 0 < r < self.q and 0 < s < self.q w = modinv(s, self.q) hm = int.from_bytes(sha1(msg), 'big') u1 = (hm * w) % self.q u2 = (r * w) % self.q v = ((modpow(self.g, u1, self.p) * modpow(self.y, u2, self.p)) % self.p) % self.q print(f'W: {w}') print(f'V: {v}') print(f'R: {r}') return valid and v == r
return plaintext else: raise ValueError('Ciphertext already seen') """ return plaintext if __name__ == '__main__': print('Challenge #41 - Implement unpadded message recovery oracle') server = RSAServer() # With a generated, good plaintext plaintext = int.from_bytes(bytes(json.dumps({ 'time': timegm(datetime.now(tz=timezone.utc).timetuple()), 'social': '555-55-5555' }).encode('UTF-8')), 'big') e, n = server.public_key c = modpow(plaintext, e, n) s = randint(2, 1000) c_prime = (modpow(s, e, n) * c) % n p_prime = server.decrypt(c_prime) recovered_p = p_prime * modinv(s, n) print(plaintext) print(recovered_p)
logger.debug(f'Calculating intervals from:') logger.debug(f' a: {a}') logger.debug(f' b: {b}') r_start = divceil(a * s[i] - 3 * B + 1, rsa.n) r_end = (b * s[i] - 2 * B) // rsa.n logger.debug(f' {r_start}, {r_end}') for r in range(r_start, r_end + 1): # r_start <= r <= r_end interval_start = max(a, divceil(2 * B + r * rsa.n, s[i])) interval_end = min(b, (3 * B - 1 + r * rsa.n) // s[i]) intervals.add((interval_start, interval_end)) M[i] = list(intervals) logger.debug(f'Built {len(M[i])} intervals:') for a, b in M[i]: logger.debug(f' {hex(a)}') logger.debug(f' {hex(b)}') logger.debug(f' m is in interval? {a <= m <= b}') logger.debug(f' {b - a}') # Step 4: Computing the solution if len(M[i]) == 1 and M[i][0][0] == M[i][0][1]: a = M[i][0][0] discovered_m = a * modinv(s[0], rsa.n) % rsa.n break i += 1 assert discovered_m == m logger.info(f'original m: {hex(m)}') logger.info(f'dicovered m: {hex(discovered_m)}')
#!/usr/bin/env python from cryptopals_39 import modinv from cryptopals_43 import DSA if __name__ == '__main__': print('Challenge #45 - DSA parameter tampering') # When we sign a message with g=0 dsa = DSA(g=0) msg = b'The oldest and strongest emotion of mankind is fear, and the oldest and strongest kind of fear is fear of the unknown' # Infinitely loops, because 0 to any positive power is 0 (r = ), failing the # non-zero requirements for r and s # r, s = dsa.sign(msg) # Try g=p+1 dsa = DSA(g=DSA.DEFAULT_P + 1) r1, s1 = dsa.sign(b'Hello, world') r2, s2 = dsa.sign(b'Goodbye, world') # Build magic signature z = 14 r = (dsa.y**12 % dsa.p) % dsa.q s = (r * modinv(z, dsa.q)) % dsa.q assert dsa.verify(b'Hello, world', r, s) assert dsa.verify(b'Goodbye, world', r, s)
rsa_1 = RSA() rsa_2 = RSA() assert len(set([rsa_0.n, rsa_1.n, rsa_2.n])) == 3 # Given some random input number inp = randint(1, 10000) c_0 = rsa_0.encrypt(inp) % rsa_0.n c_1 = rsa_1.encrypt(inp) % rsa_1.n c_2 = rsa_2.encrypt(inp) % rsa_2.n # m_s_n (for n in 0, 1, 2) are the product of the moduli EXCEPT n_n --- # ie, m_s_1 is n_0 * n_2 m_s_0 = rsa_1.n * rsa_2.n m_s_1 = rsa_0.n * rsa_2.n m_s_2 = rsa_0.n * rsa_1.n N_012 = rsa_0.n * rsa_1.n * rsa_2.n # Chinese Remainder Theorem result = ( c_0 * m_s_0 * modinv(m_s_0, rsa_0.n) + c_1 * m_s_1 * modinv(m_s_1, rsa_1.n) + c_2 * m_s_2 * modinv(m_s_2, rsa_2.n) ) % N_012 # Take the cube root, round and convert to an integer decrypted = int(round(result ** (1 / 3), 0)) assert inp == decrypted
for signatures in ks.values(): # Need multiple messages signed using the same k if len(signatures) == 1: continue msg1, s1, r1, m1 = signatures[0] msg2, s2, r2, m2 = signatures[1] # Convert to bytes for consistency msg1 = bytes(msg1.encode('UTF-8')) msg2 = bytes(msg2.encode('UTF-8')) # TODO: Unable to determine the modular multiplicative inverse for two # of these pairs of messages, need to look into this try: k = (((m1 - m2) % dsa.q) * modinv(s1 - s2, dsa.q)) % dsa.q except Exception as e: print(e) continue print(f'K: {k}') # Now, back to #43, can determine x from k x = dsa_recover_x(msg1, k, dsa.q, r1, s1) print(f'X: {x}') # When we sign a message again, using the x and k we found, the (r, s) # signature should be identical dsa = DSA(x=x, y=y) test_r1, test_s1 = dsa.sign(msg1, k=k)