def test_dsa_attack(): x = randint(1, q) y = pow(g, x, p) test_keypair = DSAKeypair(y, x, g, p, q) k = 710184121617032319901844589822607487992346783541 sig = dsa_sign(SIGN_MSG, test_keypair, k=k) attack_keypair = get_attack_keypair(SIGN_MSG, sig, y, k) assert attack_keypair.private == x dsa_sign(SIGN_MSG, attack_keypair, k=k)
def BreakDSA(m1, m2, publicKey, dsa_params): # m1 and m2 are interchangeable, but if you compute m1['s'] - m2['s'] # (and the rest accordingly), you get a negative K, which is not valid # according to the spec, and our implementation hangs trying to sign # a message. k = rsa.invmod((m2['s'] - m1['s']), dsa_params['Q']) k *= (int(m2['m'], 16) - int(m1['m'], 16)) top = (m1['s'] * k) - int(m1['m'], 16) bottom = rsa.invmod(m1['r'], dsa_params['Q']) privateKey = (top * bottom) % dsa_params['Q'] # Derive the public key from the private key and compare it # with the one we know. testPub = dsa.modexp(dsa_params['G'], privateKey, dsa_params['P']) if testPub == publicKey: print('[**] Success!') print('K:', k) print('X:', privateKey) # Sign one of the two messages with the private key and compare # the signature with the one we got from the file. sig = dsa.dsa_sign(dsa_params['Q'], dsa_params['P'], dsa_params['G'], privateKey, int('0x' + m1['m'], 16), k=k) if sig == (m1['r'], m1['s']): print('[**] Broken private key passes validation') return True return False
def test_find_repeated_nonce(): # This is just a brute force attack for sig1, sig2 in combinations(map(itemgetter(0), SIGNATURES), 2): assert sig1 != sig2 # r = g^k % p % q so if k is repeated so is r if sig1.r == sig2.r: break s_inv = mod_inverse(sig1.s - sig2.s, q) k = ((hash_msg(sig1.msg) - hash_msg(sig2.msg)) * s_inv) % q attack_keypair = get_attack_keypair(sig1.msg, sig1, y, k) assert dsa_sign(sig1.msg, attack_keypair, k) == sig1 assert dsa_sign(sig2.msg, attack_keypair, k) == sig2 secret_str = '{:02x}'.format(attack_keypair.private) hash_obj = sha1(secret_str.encode('utf-8')) digest = hash_obj.hexdigest() assert digest == 'ca8f6f7c66fa362d40760d135b763eb8527d3d52'
def Generator0(): dsa_params = { 'Q': int("0xf4f47f05794b256174bba6e9b396a7707e563c5b", 16), 'P': int("0x800000000000000089e1855218a0e7dac38136ffafa72eda7859f2171e25e65eac698c1702578b07dc2a1076da241c76c62d374d8389ea5aeffd3226a0530cc565f3bf6b50929139ebeac04f48c3c84afb796d61e5a4f9a8fda812ab59494232c7d2b4deb50aa18ee9e132bfa85ac4374d7f9091abc3d015efc871a584471bb1", 16), 'G': 0, } privateKey, publicKey = dsa.generate_pair(dsa_params['P'], dsa_params['G'], dsa_params['Q']) sig = dsa.dsa_sign(dsa_params['Q'], dsa_params['P'], dsa_params['G'], privateKey, dsa.HashMessage(b'And now for something completely different')) print(sig) res = dsa.dsa_verify(sig[0], sig[1], dsa_params['G'], dsa_params['P'], dsa_params['Q'], publicKey, dsa.HashMessage(b'And now for something completely different')) print(res)
def test_recover_keypair(): y = 0x84ad4719d044495496a3201c8ff484feb45b962e7302e56a392aee4abab3e4bdebf2955b4736012f21a08084056b19bcd7fee56048e004e44984e2f411788efdc837a0d2e5abb7b555039fd243ac01f0fb2ed1dec568280ce678e931868d23eb095fde9d3779191b8c0299d6e07bbb283e6633451e535c45513b2d33c99ea17 sig = DSASignature(SIGN_MSG, r=548099063082341131477253921760299949438196259240, s=857042759984254168557880549501802188789837994940) # We know nonce is between 0 and 2^16 for k in range(0, 2**16): attack_keypair = get_attack_keypair(SIGN_MSG, sig, y, k) test_sig = dsa_sign(SIGN_MSG, attack_keypair, k=k) if sig.r == test_sig.r and sig.s == test_sig.s: secret_str = '{:02x}'.format(attack_keypair.private) hash_obj = sha1(secret_str.encode('utf-8')) digest = hash_obj.hexdigest() assert digest == '0954edd5e0afe5542a4adf012611a91912a3ec16' break
def GeneratorPPlus1(): dsa_params = { 'Q': int("0xf4f47f05794b256174bba6e9b396a7707e563c5b", 16), 'P': int("0x800000000000000089e1855218a0e7dac38136ffafa72eda7859f2171e25e65eac698c1702578b07dc2a1076da241c76c62d374d8389ea5aeffd3226a0530cc565f3bf6b50929139ebeac04f48c3c84afb796d61e5a4f9a8fda812ab59494232c7d2b4deb50aa18ee9e132bfa85ac4374d7f9091abc3d015efc871a584471bb1", 16), } dsa_params['G'] = dsa_params['P'] + 1 privateKey, publicKey = dsa.generate_pair(dsa_params['P'], dsa_params['G'], dsa_params['Q']) # The generated signature is verified against any string, provided the public # key is correct. sig = dsa.dsa_sign(dsa_params['Q'], dsa_params['P'], dsa_params['G'], privateKey, dsa.HashMessage(b'Hello world!')) res = dsa.dsa_verify(sig[0], sig[1], dsa_params['G'], dsa_params['P'], dsa_params['Q'], publicKey, dsa.HashMessage(b'Goodbye world!')) if res: print('[**] Attack with G=P+1 successful.') else: print('[!!] Attack with G=P+1 failed.')
return print('[**] Success!') print('K:', k) print('X:', brokenKey) return brokenKey, k if __name__ == '__main__': message = b'For those that envy a MC it can be hazardous to your health\nSo be friendly, a matter of life and death, just like a etch-a-sketch\n' r = 548099063082341131477253921760299949438196259240 s = 857042759984254168557880549501802188789837994940 dsa_params = { 'Q': int("0xf4f47f05794b256174bba6e9b396a7707e563c5b", 16), 'P': int("0x800000000000000089e1855218a0e7dac38136ffafa72eda7859f2171e25e65eac698c1702578b07dc2a1076da241c76c62d374d8389ea5aeffd3226a0530cc565f3bf6b50929139ebeac04f48c3c84afb796d61e5a4f9a8fda812ab59494232c7d2b4deb50aa18ee9e132bfa85ac4374d7f9091abc3d015efc871a584471bb1", 16), 'G': int("5958c9d3898b224b12672c0b98e06c60df923cb8bc999d119458fef538b8fa4046c8db53039db620c094c9fa077ef389b5322a559946a71903f990f1f7e0e025e2d7f7cf494aff1a0470f5b64c36b625a097f1651fe775323556fe00b3608c887892878480e99041be601a62166ca6894bdd41a7054ec89f756ba9fc95302291", 16), } # To verify we have found the right k and private key, we sign the same message # using both of them. If the signatures match with the one we got from the # challenge, it is correct. brokenKey, brokenK = BreakDSA(dsa_params['P'], dsa_params['G'], dsa_params['Q'], r, s) sig = dsa.dsa_sign(dsa_params['Q'], dsa_params['P'], dsa_params['G'], brokenKey, dsa.HashMessage(message), k=brokenK) if sig == (r, s): print('[**] The signatures (with real and broken private key) match!') else: print('[!!] The signatures (with real and broken private key) do not match!')
'Q': int("0xf4f47f05794b256174bba6e9b396a7707e563c5b", 16), 'P': int( "0x800000000000000089e1855218a0e7dac38136ffafa72eda7859f2171e25e65eac698c1702578b07dc2a1076da241c76c62d374d8389ea5aeffd3226a0530cc565f3bf6b50929139ebeac04f48c3c84afb796d61e5a4f9a8fda812ab59494232c7d2b4deb50aa18ee9e132bfa85ac4374d7f9091abc3d015efc871a584471bb1", 16), 'G': int( "5958c9d3898b224b12672c0b98e06c60df923cb8bc999d119458fef538b8fa4046c8db53039db620c094c9fa077ef389b5322a559946a71903f990f1f7e0e025e2d7f7cf494aff1a0470f5b64c36b625a097f1651fe775323556fe00b3608c887892878480e99041be601a62166ca6894bdd41a7054ec89f756ba9fc95302291", 16), } # To verify we have found the right k and private key, we sign the same message # using both of them. If the signatures match with the one we got from the # challenge, it is correct. brokenKey, brokenK = BreakDSA(dsa_params['P'], dsa_params['G'], dsa_params['Q'], r, s) sig = dsa.dsa_sign(dsa_params['Q'], dsa_params['P'], dsa_params['G'], brokenKey, dsa.HashMessage(message), k=brokenK) if sig == (r, s): print('[**] The signatures (with real and broken private key) match!') else: print( '[!!] The signatures (with real and broken private key) do not match!' )