def attack_rsa(message): """ Performs the e=3 broadcast attack as described above. Args: message (bytes): The message to send 3 times. Returns: The message that was sent in, but decrypted from the crazy math. """ server = BadRSAServer() c_0, (_, n_0) = server.encrypt_message(message) c_1, (_, n_1) = server.encrypt_message(message) c_2, (_, n_2) = server.encrypt_message(message) m_0 = n_1 * n_2 m_1 = n_0 * n_2 m_2 = n_0 * n_1 c_0 = int.from_bytes(c_0, 'big') c_1 = int.from_bytes(c_1, 'big') c_2 = int.from_bytes(c_2, 'big') result = c_0 * m_0 * c39.invmod(m_0, n_0) result += c_1 * m_1 * c39.invmod(m_1, n_1) result += c_2 * m_2 * c39.invmod(m_2, n_2) result = result % (n_0 * n_1 * n_2) result = find_invpow(result, 3)[0] return c36.int_to_bytes(result)
def recover_nonce(msg1, msg2, params): """ Attempts to recover the nonce from the two messages, assuming both messages used the same nonce. If they, don't it returns None. Args: msg1: The first message msg2: The second message params: The DSA domain parameters Returns: The (k,x) pair from the two messages if they used the same nonce. If not, returns None. """ p, q, g = params m1 = msg1.h m2 = msg2.h s1 = msg1.s s2 = msg2.s sig1 = msg1.r, msg1.s sig2 = msg2.r, msg2.s subm = (m1 - m2) % q subs = (s1 - s2) % q k = (c39.invmod(subs, q) * subm) % q x1 = c43.get_x_from_k(msg1.msg, sig1, params, k) x2 = c43.get_x_from_k(msg2.msg, sig2, params, k) if x1 == x2: return k, x1 return None
def dsa_sign_with_k(message, params, key, k=0): """ DSA signing operation which captures and returns k. Args: message: The message to sign for. params: DSA domain parameters as a triplet (p,q,g) key: The DSA private key (x) k: An optional k value for testing Returns: The DSA signature pair (r,s,k) """ p, q, g = params n = len(number.long_to_bytes(q)) x = key if k == 0: k = random.randrange(1, q) r = pow(g, k, p) % q #if r == 0: # dsa_sign(message, params, key) h = number.bytes_to_long(sha1(message).digest()[:n]) xr = x * r s = (c39.invmod(k, q) * (h + xr)) % q #if s == 0: # dsa_sign(message, params, key) return r, s, k
def dsa_verify(message, signature, params, key): """ DSA signature verification. Args: message: The message. signature: The signature for the message. params: The DSA domain parameters as a triplet (p,q,g) key: The DSA public key (y) Returns: True if signature validates """ p, q, g = params y = key r, s = signature if r >= q or s >= q: print('F**k up') return False w = c39.invmod(s, q) % q H = number.bytes_to_long(sha1(message).digest()) u1 = (H * w) % q u2 = (r * w) % q v = ((pow(g, u1, p) * pow(y, u2, p)) % p) % q return v == r
def x_recovery(self, m, r, s, k, dgst=hashlib.sha1): # recovering x knowing k # m - message # r,s - public signature # k - this should be secret (nounce) top = (s*k)-s2i(dgst(m).digest()) return (top * invmod(r, self.q)) % self.q
def sign(self, msg, x=None): x = x if x else self.x r = 0 while r == 0: k = random.randint(1, self.q - 1) r = pow(self.g, k, self.p) % self.q s = invmod(k, self.q) * (self.H(msg) + x * r) % self.q return r, s
def verifySignatureHash(H, signature, pub): (r, s) = signature (p, q, g, y) = pub if r <= 0 or r >= q or s <= 0 or s >= q: return False w = c39.invmod(s, q) u1 = (H * w) % q u2 = (r * w) % q v = ((pow(g, u1, p) * pow(y, u2, p)) % p) % q return v == r
def verify(self, msg, sig, y=None): y = y if y else self.y r, s = sig assert r in range(1, self.q) and s in range( 1, self.q), "Invalid signature" w = invmod(s, self.q) u1 = self.H(msg) * w % self.q u2 = r * w % self.q v = pow(self.g, u1, self.p) * pow(y, u2, self.p) % self.p % self.q return v == r
def verify(self, m, r, s, y, dgst=hashlib.sha1): # signature verification # https://en.wikipedia.org/wiki/Digital_Signature_Algorithm#Verifying assert (r > 0 and r < self.q), "wrong r: 0 < r < q" assert (s > 0 and s < self.q), "wrong s: 0 < s < q" w = invmod(s, self.q) u1 = (s2i(dgst(m).digest())*w) % self.q u2 = (r*w) % self.q v = ((modexp(self.g, u1, self.p)*modexp(y, u2, self.p)) % self.p )% self.q assert (v == r), "verification failed, v != r" return True
def signHashWithK(H, pub, priv, k): (p, q, g, y) = pub x = priv r = pow(g, k, p) % q if r == 0: return None kInv = c39.invmod(k, q) s = (kInv * (H + x * r)) % q if s == 0: return None return (r, s)
def signk(self, m, x, dgst=hashlib.sha1): # signing with a leak of the k value # m - message # x - private key # https://en.wikipedia.org/wiki/Digital_Signature_Algorithm#Signing r = 0 k = 0 while r == 0: k = randint(0, self.q) r = modexp(self.g, k, self.p) % self.q s = (invmod(k, self.q) * (s2i(dgst(m).digest())+(x*r))) % self.q return (r, s, k)
def checkForCommonK(pub, msg1, msg2): (p, q, g, y) = pub (_, s1, r1, m1) = msg1 (_, s2, r2, m2) = msg2 ds = (s1 - s2) % q dm = (m1 - m2) % q dsInv = c39.invmod(ds, q) k = (dm * dsInv) % q priv1 = c43.extractKey(m1, r1, s1, k, pub) priv2 = c43.extractKey(m2, r2, s2, k, pub) if priv1 == priv2 and c43.areValidKeys(pub, priv1) and c43.areValidKeys( pub, priv2): return (k, priv1) return (None, None)
def get_x_from_k(message, signature, params, k): """ Extracts the private key from the k value. Args: message: The message signature: The signature pair (r,s) params: DSA domain params (p,q,g) k: The k value used for the signature """ r, s = signature p, q, g = params sk = (s * k) h = number.bytes_to_long(sha1(message).digest()) x = ((sk - h) * c39.invmod(r, q)) % q return x
def attack_server(server, msg): """ Attacks the RSA server using the attack described in the problem. Args: server: The server to attack msg: The message to use to attack the server Returns: The decrypted plaintext (even though it aleady has it) """ ctxt, (E, N) = server.encrypt_msg(msg) ptxt = server.decrypt_msg(ctxt) assert (ptxt == msg) S = random.randint(2, N - 1) c_prime = (pow(S, E, N) * number.bytes_to_long(ctxt)) % N p_prime = server.decrypt_msg(number.long_to_bytes(c_prime)) p = (number.bytes_to_long(p_prime) * c39.invmod(S, N)) % N return number.long_to_bytes(p)
def k_dup(self, m1, s1, m2, s2): return ((m1-m2) * invmod((s1-s2) % self.q, self.q)) % self.q
def priv_from_k(hashint, sig, k, q=dsa.q): r, s = sig return invmod(r, q) * (s * k - hashint) % q
def priv_from_sig_pair(m1, sig1, m2, sig2, q=dsa.q): r1, s1, r2, s2 = sig1 + sig2 k = invmod((s1 - s2) % q, q) * ((m1 - m2) % q) % q x = priv_from_k(m1, sig1, k, q) # Return x=None if sigs were not generated with same k return k, x if x == priv_from_k(m2, sig2, k, q) else None
ct1 = RSA().encrypt(cleartext,pub1) print "2. key generation" pub2,priv2 = RSA().keygen(l=2048,s=False) # s=False gives e=3 print "2. encrypting using pub key" ct2 = RSA().encrypt(cleartext,pub2) assert RSA().decrypt(ct0,priv0) == cleartext, "error on key0" assert RSA().decrypt(ct1,priv1) == cleartext, "error on key1" assert RSA().decrypt(ct2,priv2) == cleartext, "error on key2" # https://en.wikipedia.org/wiki/Coppersmith%27s_Attack#H.C3.A5stad.27s_Broadcast_Attack # https://en.wikipedia.org/wiki/Chinese_remainder_theorem c_0 = s2i(ct0[0]) c_1 = s2i(ct1[0]) c_2 = s2i(ct2[0]) n_0 = pub0[1] n_1 = pub1[1] n_2 = pub2[1] m_s_0 = n_1 * n_2 m_s_1 = n_0 * n_2 m_s_2 = n_0 * n_1 result = (c_0 * m_s_0 * invmod(m_s_0, n_0)) result += (c_1 * m_s_1 * invmod(m_s_1, n_1)) result += (c_2 * m_s_2 * invmod(m_s_2, n_2)) result %= (n_0 * n_1 * n_2) print i2s(int(pow(Decimal(result),Decimal(1)/Decimal(3)))+1)
def decrypt_unpadded(ct, oracle): e, n = oracle.publickey() ct_new = (pow(2, e, n) * ct) % n # s = 2 pt_new = oracle.decrypt(ct_new) pt = pt_new * invmod(2, n) % n return int2bytes(pt)
def magic_sign(self, y, z = 20): # only if g == p+1 assert (self.g == self.p+1), "only if g == p+1" r = modexp(y, z, self.p) % self.q s = r * invmod(z, self.q) return (r, s)
def forge_signature(msg, params, y): p, q, g = params z = number.bytes_to_long(msg) r = pow(y, z, p) % q s = (r * c39.invmod(z, q)) % q return r, s
def extractKey(H, r, s, k, pub): (p, q, g, y) = pub rInv = c39.invmod(r, q) return (rInv * (s * k - H)) % q