-
Notifications
You must be signed in to change notification settings - Fork 2
/
keygen.py
executable file
·107 lines (78 loc) · 3.31 KB
/
keygen.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
import argparse
from functools import reduce
from math import log
from operator import mul
from sys import version_info
from Crypto.Util.number import getRandomNBitInteger, isPrime
from Crypto.PublicKey import RSA
# 0x10001 = 65537 (hardcoded public exponent, low hamming weight = fast)
e = 0x10001 if version_info >= (3, 0) else long(0x10001) # there are no long literals in python3
def _prime_count_for_keysize(keysize):
"""Return the number of primes whose product forms the primorial M.
Taken from section 2.1 "Format of the constructed primes" of the paper.
"""
if 512 <= keysize <= 960:
return 39
elif 992 <= keysize <= 1952:
return 71
elif 1984 <= keysize <= 3936:
return 126
elif 3968 <= keysize <= 4096:
return 225
else:
raise ValueError('Keysize is not in range for ROCA attack')
def _first_n_primes(n):
"""Generate the first :param:`n` prime numbers.
Code is not optimally efficient but keygen only requires values of
n < 1000 and for that it's fast enough and easy to read."""
primes = [2]
candidate = 3
while len(primes) != n:
# test if candidate is divisible by any prime smaller than it
if not any([candidate % p == 0 for p in primes]):
primes.append(candidate)
candidate += 2 # only odds (beside 2) are prime, skip the evens
return primes
def _get_candidate_prime(keysize):
"""Generate a candidate prime based on formula (1) in section 2.1."""
prime_count = _prime_count_for_keysize(keysize)
primes = _first_n_primes(prime_count)
M = reduce(mul, primes) # the primorial M in the paper
M_bits = int(log(M, 2)) + 1
k_bits = (keysize // 2) - M_bits
a_bits = {
39: 62, 71: 134, 126: 255, 225: 434
}[prime_count] # Table 1 - Naive BF # attempts gives order of group
k = getRandomNBitInteger(k_bits)
a = getRandomNBitInteger(a_bits)
return k * M + pow(e, a, M)
def _check_bits(prime, keysize):
"""Ensure the top two bits of the prime are set."""
if prime == 0:
return False # initial values of p, q can be skipped
bits = keysize // 2
top_two_bits = prime >> (bits - 2)
return top_two_bits == 0x2 # 0x3 = 11 in binary
def generate_vulnerable_key(file, keysize=1024):
"""Generate an RSA object vulnerable to the ROCA attack"""
q, p = 0, 0 # some non-prime values ...
keysize += 2
while not (_check_bits(p, keysize) and isPrime(p)):
p = _get_candidate_prime(keysize)
while not (_check_bits(q, keysize) and isPrime(q)):
q = _get_candidate_prime(keysize)
# generate only the public key (N, e), the whole point is to recover d
rsa = RSA.construct((p*q, e))
ascii_armored_key = rsa.exportKey().decode()
print(ascii_armored_key)
# run it against the roca-detect check utility
f = open(file, 'w')
f.write(ascii_armored_key)
f.close()
return rsa
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('pubkey', help='File containing the public key')
parser.add_argument('keysize', help='Keysize of public Key in bits', type=int)
args = parser.parse_args()
generate_vulnerable_key(args.pubkey, args.keysize)