def secret_key_from_nonce(self, message, nonce, r, s): """ Given a signature, nonce and message, calculates the secret key""" numerator = (s*nonce - int(self.hash(message).hexdigest(), 16)) % self.q r_inv = nt.invmod(r, self.q) return (numerator * r_inv) % self.q
def step4(self): if len(self.M) == 1: if self.M[0][0] == self.M[0][1]: return (self.M[0][0] * nt.invmod(1, self.mod)) % self.mod self.i += 1 return 0
def nonce_from_double_signing(self, message1, s1, message2, s2): """Computes the nonce from two message signed with the same nonce""" msg_hash1 = int(self.hash(message1).hexdigest(), 16) msg_hash2 = int(self.hash(message2).hexdigest(), 16) numerator = (msg_hash1 - msg_hash2) % self.q denominator = nt.invmod((s1 - s2) % self.q, self.q) return (numerator * denominator) % self.q
def verify(self, message, public_key, r, s): s_inv = nt.invmod(s, self.q) msg_hash = int(self.hash(message).hexdigest(), 16) exp1 = msg_hash * s_inv % self.q exp2 = r * s_inv % self.q v = ((nt.modexp(self.g, exp1, self.p) * nt.modexp(public_key, exp2, self.p)) % self.p) % self.q return v == r
def sign_message(self, message): r = s = 0 k = secrets.randbelow(self.q) r = nt.modexp(self.g, k, self.p) % self.q k_inv = nt.invmod(k, self.q) msg_hash = int(self.hash(message).hexdigest(), 16) s = (k_inv * (msg_hash + self.secret_key * r)) % self.q return self.public_key, r, s
def __init__(self, e=3, prime_size=1024): p = number.getPrime(prime_size) q = number.getPrime(prime_size) #gcd(e, (p-1)*(q-1)) needs to be 1 for the algorithm to work while nt.gcd(e, p - 1) > 1: p = number.getPrime(prime_size) while nt.gcd(e, q - 1) > 1: q = number.getPrime(prime_size) self.e = e self.n = p * q totient = (p - 1) * (q - 1) self.d = nt.invmod(e, totient)
def main(): oracle = RSAOracle() client = rsa.RSAClient() client.recv_public_key(*oracle.send_public_key()) message = b'A secret message' #Client encrypts the message message_int = int(bso.bytes_to_hex(message), 16) ciphertext = client.encrypt(message_int) #Client sends ciphertext which gets decrypted assert message == bso.hex_to_bytes(hex(oracle.decrypt(ciphertext))[2:]) #Attacker intercepts the ciphertext and tries to get the plaintext from #the oracle. This fails because the oracle only decrypts each plaintext once try: successfully_decrypted = message == bso.hex_to_bytes( hex(oracle.decrypt(ciphertext))[2:]) except: successfully_decrypted = False assert successfully_decrypted == False #instead the attacker can get the decryption of an alternate ciphertext and # convert it to the original message. This attack uses the fat that exponentiation # is a homomorphism e, n = oracle.send_public_key() #S can be any value S = 2 altered_ciphertext = nt.modexp(S, e, n) * ciphertext % n altered_message = oracle.decrypt(altered_ciphertext) #altered message = S * message new_message = altered_message * nt.invmod(S, n) % n assert message == bso.hex_to_bytes(hex(new_message)[2:])
def main(): server = ChallengeDSAUser(SHA1) client = ChallengeDSAUser(SHA1) #malicious g parameter server.g = client.g = 0 server.key_generation() client.key_generation() message = b'henlo' signature = server.sign_message(message) assert client.verify(message, *signature) assert signature[0] == 0 assert signature[1] == 0 #since g = 0, any signature will have r = 0 and secret key = 0 # but worse still, any signature with r = 0 will verify any message new_message = secrets.token_bytes(21) assert client.verify(new_message, *signature) #new malicious g value server.g = client.g = server.p + 1 server.key_generation() client.key_generation() signature = server.sign_message(message) assert signature[0] == signature[1] == 1 assert client.verify(message, *signature) #we can construct a magic signature for a given public key and any message #choose a random public key public_key = secrets.randbelow(server.p) z = 3 #a magic number (not actually magic in this case, any z will do) r = nt.modexp(public_key, z, server.p) % server.q s = (r * nt.invmod(z, server.q)) % server.q assert client.verify(new_message, public_key, r, s)
def test_nonce(self, nonce, message, r, s): """Tests is a nonce gives the desired signature for a message""" message_int = int(self.hash(message).hexdigest(), 16) test_r = nt.modexp(self.g, nonce, self.p) % self.q #Don't test s if r does not match if test_r == r: secret_key = self.secret_key_from_nonce(message, nonce, r, s) nonce_inv = nt.invmod(nonce, self.q) test_s = (nonce_inv * (message_int + secret_key * r)) % self.q if test_s == s: return secret_key return 0