Skip to content

Commit 1d6910a

Browse files
committed
Initial commit
0 parents  commit 1d6910a

File tree

8 files changed

+253
-0
lines changed

8 files changed

+253
-0
lines changed

RsaAttack.py

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
from Crypto.PublicKey import RSA
2+
from attacks import *
3+
import argparse
4+
5+
if __name__ == '__main__':
6+
parser = argparse.ArgumentParser(description='This tool is for attacks on RSA multiple keys')
7+
8+
parser.add_argument('--key-files', nargs='*', help='Specify RSA public keys in standart form')
9+
parser.add_argument('--enc-files', nargs='*', help='Specify encrypted files')
10+
parser.add_argument('--chosen-plaintext', action='store_true', help='Specify to start chosen plaintext attack')
11+
parser.add_argument('--exponents', nargs='*', help='Specify exponents of keys')
12+
parser.add_argument('--modules', nargs='*', help='Specify modules of keys')
13+
parser.add_argument('--ciphertexts', nargs='*', help='Specify ciphertexts')
14+
parser.add_argument('--exponents-list', nargs=1, help='Specify file with exponents (line by line)')
15+
parser.add_argument('--modules-list', nargs=1, help='Specify file with modules (line by line)')
16+
parser.add_argument('--ciphertexts-list', nargs=1, help='Specify file with ciphertexts (line by line)')
17+
18+
args = parser.parse_args()
19+
20+
exponents, modules, ciphertexts = list(), list(), list()
21+
22+
if args.exponents_list is not None:
23+
for file_name in args.exponents_list:
24+
lines = open(file_name, 'r').readlines()
25+
exponents = [transform_to_int(l) for l in lines]
26+
27+
if args.modules_list is not None:
28+
for file_name in args.modules_list:
29+
lines = open(file_name, 'r').readlines()
30+
modules = [transform_to_int(l) for l in lines]
31+
32+
if args.ciphertexts_list is not None:
33+
for file_name in args.ciphertexts_list:
34+
lines = open(file_name, 'r').readlines()
35+
ciphertexts = [transform_to_int(l) for l in lines]
36+
37+
if args.exponents is not None:
38+
for e in args.exponents:
39+
exponents.append(transform_to_int(e))
40+
41+
if args.modules is not None:
42+
for n in args.modules:
43+
modules.append(transform_to_int(n))
44+
45+
if args.ciphertexts is not None:
46+
for c in args.ciphertexts:
47+
ciphertexts.append(transform_to_int(c))
48+
49+
if args.key_files is not None:
50+
for file_name in args.key_files:
51+
key = open(file_name, 'r').read()
52+
key_handle = RSA.importKey(key)
53+
54+
exponents.append(key_handle.e)
55+
modules.append(key_handle.n)
56+
57+
if args.enc_files is not None:
58+
for file_name in args.enc_files:
59+
buffer = open(file_name, 'rb').read()
60+
61+
try:
62+
if match(r'^[\w+/=\r\n]+$', buffer.decode()):
63+
ciphertexts.append(transform_to_int(buffer.decode()))
64+
else:
65+
ciphertexts.append(bytes_as_int(buffer))
66+
except:
67+
ciphertexts.append(bytes_as_int(buffer))
68+
69+
if len(exponents) != len(modules) or len(modules) != len(ciphertexts):
70+
print('[-] You should provide the same number of ciphertexts and keys')
71+
exit(0)
72+
73+
if args.chosen_plaintext:
74+
chosen_plaintext(modules[0], exponents[0], ciphertexts[0])
75+
exit(0)
76+
77+
# First of all, trying to find e keys with exponent e
78+
common_e_dict = dict()
79+
80+
for index, e in enumerate(exponents):
81+
temp = common_e_dict.get(e, (list(), list()))
82+
83+
common_e_dict[e] = (
84+
temp[0] + [modules[index]],
85+
temp[1] + [ciphertexts[index]]
86+
)
87+
88+
if len(temp[0]) == e - 1:
89+
print(common_exponent(e, *common_e_dict[e]))
90+
exit(0)
91+
92+
# Trying to find keys with common modulus
93+
for i in range(len(modules)):
94+
for j in range(i + 1, len(modules)):
95+
if modules[i] == modules[j] and gcd(exponents[i], exponents[j]) == 1:
96+
print(common_modulus(
97+
modules[i],
98+
exponents[i], exponents[j],
99+
ciphertexts[i], ciphertexts[j]
100+
))
101+
exit(0)
102+
103+
if gcd(modules[i], modules[j]) > 1:
104+
print(common_divisor(
105+
modules[i], modules[j],
106+
exponents[i], ciphertexts[i]
107+
))
108+
exit(0)

attacks.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
from utils import *
2+
from random import randint
3+
4+
def chosen_plaintext(n: int, e: int, c: int) -> bytes:
5+
p = randint(2, 1000)
6+
q = mod_inv(p, n)
7+
8+
to_decrypt = c * pow(p, e, n) % n
9+
b = int_as_bytes(to_decrypt)
10+
11+
print('Send data for decrypting in one of the following forms:')
12+
print('As decimal integer:', to_decrypt)
13+
print('As hexidecimal integer:', hex(to_decrypt))
14+
print('As Base64 encoded bytes:', b64encode(b).decode('ascii'))
15+
16+
result = input('Provide decrypted data: ')
17+
result = transform_to_int(result)
18+
19+
m = result * q % n
20+
print('Result is:', int_as_bytes(m))
21+
22+
def common_divisor(n1: int, n2: int, e1: int, c1: int) -> bytes:
23+
assert gcd(n1, n2) > 1
24+
25+
p = gcd(n1, n2)
26+
q = n1 // p
27+
28+
d = mod_inv(e1, (p - 1) * (q - 1))
29+
m = pow(c1, d, n1)
30+
31+
return int_as_bytes(m)
32+
33+
def common_modulus(n: int, e1: int, e2: int, c1: int, c2: int) -> bytes:
34+
assert gcd(e1, e2) == 1
35+
36+
p1, p2, _ = egcd(e1, e2)
37+
38+
if p1 < 0:
39+
c1 = mod_inv(c1, n)
40+
p1 *= -1
41+
else:
42+
c2 = mod_inv(c2, n)
43+
p2 *= -1
44+
45+
m = pow(c1, p1, n) * pow(c2, p2, n) % n
46+
47+
return int_as_bytes(m)
48+
49+
def common_exponent(e: int, modules: list, ciphertexts: list) -> bytes:
50+
assert len(modules) == e
51+
52+
solution = solve_chinese_problem(modules, ciphertexts)
53+
54+
left, right = 1, min(modules)
55+
56+
while right - left > 1:
57+
middle = (left + right) // 2
58+
59+
if middle ** e == solution:
60+
return int_as_bytes(middle)
61+
62+
if middle ** e < solution:
63+
left = middle
64+
else:
65+
right = middle
66+
67+
return b''

examples/common-divisor/enc1.bin

128 Bytes
Binary file not shown.

examples/common-divisor/enc2.bin

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
@�E�c�?[w�;��U�/^>�q(�^Jv!nu�Bˬ?���ﭹl �P|V�Ly4��a���{�<��A�l�͡�WD���z�W�K|�9�z�
2+
/�u��`]t�E(�hC?w{��`^)��'�

examples/common-divisor/key1.pem

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
-----BEGIN PUBLIC KEY-----
2+
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCmsaTRyPhf5Kt0NbaUW8K+ZynZ
3+
poVfMHrhSH9so/pul3f4/HKMUTfDn2ili+oRvXScvngrE8l7bE+ADA6HbLxzIdQV
4+
GhqhMEJTmo0808tpsYsMJ1sR13I3vcYpZ76bphEGhLxae9ssARuvn37Tx/f+RDXa
5+
JHWDuJfeupD9EjJqGwIDAQAB
6+
-----END PUBLIC KEY-----

examples/common-divisor/key2.pem

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
-----BEGIN PUBLIC KEY-----
2+
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDgs4hS/4DYe9xjCP0ELXGdWLbR
3+
gYewDLHRewpb1ASH5Zi+LL777FpadvTvpuiht1D97WoUBjwQaa4xFj4lmNVZyoK7
4+
I9zMPxrU+rpjjZh2Gqa5Jy+PmYuUUN0FAEVU6xO6sb6oZWJx+mqyEAhMQ9K8JRAb
5+
jaNoBx8fywENyd7+ZQIDAQAB
6+
-----END PUBLIC KEY-----

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pycryptodome==3.8.2

utils.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
from base64 import b64encode, b64decode
2+
from re import match
3+
4+
# Mathematical algorithms
5+
def egcd(a: int, b: int) -> (int, int, int):
6+
prelast_u, prelast_v = 1, 0
7+
last_u, last_v = 0, 1
8+
9+
while True:
10+
q, r = a // b, a % b
11+
12+
if r == 0:
13+
return (last_u, last_v, b)
14+
15+
prelast_u, last_u = last_u, prelast_u - q * last_u
16+
prelast_v, last_v = last_v, prelast_v - q * last_v
17+
18+
a, b = b, r
19+
20+
def gcd(a: int, b: int) -> int:
21+
while True:
22+
if b == 0:
23+
return a
24+
a, b = b, a % b
25+
26+
def mod_inv(a: int, b: int) -> int:
27+
return egcd(a, b)[0] % b
28+
29+
def solve_chinese_problem(modules: list, remainders: list) -> int:
30+
mult = 1
31+
32+
for i in modules:
33+
mult *= i
34+
35+
result = sum(
36+
r * mult * mod_inv(mult // m, m) // m
37+
for r, m in zip(remainders, modules)
38+
)
39+
40+
return result % mult
41+
42+
# Conversion algorithms
43+
def int_as_bytes(n: int) -> bytes:
44+
bit_length = n.bit_length()
45+
46+
if bit_length == 0:
47+
length = 1
48+
else:
49+
length = (bit_length + 7) // 8
50+
51+
return n.to_bytes(length, 'big')
52+
53+
def bytes_as_int(b: bytes) -> int:
54+
return int.from_bytes(b, 'big')
55+
56+
def transform_to_int(s: str) -> int:
57+
if match(r'^\d+$', s):
58+
return int(s)
59+
elif s.startswith('0x'):
60+
return int(s, 16)
61+
elif match(r'^[\w+/=\r\n]+$', s):
62+
b = b64decode(s.encode('utf-8'))
63+
return bytes_as_int(b)

0 commit comments

Comments
 (0)