-
Notifications
You must be signed in to change notification settings - Fork 81
/
python_rsakey.py
151 lines (134 loc) · 4.58 KB
/
python_rsakey.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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# Author: Trevor Perrin
# See the LICENSE file for legal information regarding use of this file.
"""Pure-Python RSA implementation."""
import threading
from .cryptomath import *
from .rsakey import *
from .pem import *
from .deprecations import deprecated_params
if GMPY2_LOADED:
from gmpy2 import mpz
elif gmpyLoaded:
from gmpy import mpz
class Python_RSAKey(RSAKey):
def __init__(self, n=0, e=0, d=0, p=0, q=0, dP=0, dQ=0, qInv=0,
key_type="rsa"):
"""Initialise key directly from integers.
see also generate() and parsePEM()."""
if (n and not e) or (e and not n):
raise AssertionError()
if gmpyLoaded or GMPY2_LOADED:
n = mpz(n)
e = mpz(e)
d = mpz(d)
p = mpz(p)
q = mpz(q)
dP = mpz(dP)
dQ = mpz(dQ)
qInv = mpz(qInv)
self.n = n
self.e = e
if p and not q or not p and q:
raise ValueError("p and q must be set or left unset together")
if not d and p and q:
t = lcm(p - 1, q - 1)
d = invMod(e, t)
self.d = d
self.p = p
self.q = q
if not dP and p:
dP = d % (p - 1)
self.dP = dP
if not dQ and q:
dQ = d % (q - 1)
self.dQ = dQ
if not qInv:
qInv = invMod(q, p)
self.qInv = qInv
self.blinder = 0
self.unblinder = 0
self._lock = threading.Lock()
self.key_type = key_type
def hasPrivateKey(self):
"""
Does the key has the associated private key (True) or is it only
the public part (False).
"""
return self.d != 0
def _rawPrivateKeyOp(self, message):
n = self.n
with self._lock:
# Create blinding values, on the first pass:
if not self.blinder:
self.unblinder = getRandomNumber(2, n)
self.blinder = powMod(invMod(self.unblinder, n), self.e,
n)
unblinder = self.unblinder
blinder = self.blinder
# Update blinding values
self.blinder = (blinder * blinder) % n
self.unblinder = (unblinder * unblinder) % n
# Blind the input
message = (message * blinder) % n
# Perform the RSA operation
cipher = self._rawPrivateKeyOpHelper(message)
# Unblind the output
cipher = (cipher * unblinder) % n
# Return the output
return cipher
def _rawPrivateKeyOpHelper(self, m):
#Non-CRT version
#c = pow(m, self.d, self.n)
#CRT version (~3x faster).
p, q = self.p, self.q
s1 = pow(m, self.dP, p)
s2 = pow(m, self.dQ, q)
h = ((s1 - s2) * self.qInv) % p
c = s2 + q * h
return c
def _rawPublicKeyOp(self, ciphertext):
msg = pow(ciphertext, self.e, self.n)
return msg
def acceptsPassword(self):
"""Does it support encrypted key files."""
return False
@staticmethod
def generate(bits, key_type="rsa"):
"""Generate a private key with modulus 'bits' bit big.
key_type can be "rsa" for a universal rsaEncryption key or
"rsa-pss" for a key that can be used only for RSASSA-PSS."""
# p, q, and t are standard names for the variables in RSA, so
# ignore the fact those are one character long variable names
# pylint: disable=invalid-name
key = Python_RSAKey()
while True:
p = getRandomPrime(bits//2, False)
q = getRandomPrime(bits//2, False)
if gmpyLoaded or GMPY2_LOADED:
p = mpz(p)
q = mpz(q)
t = lcm(p-1, q-1)
# since we need to calculate inverse of 65537 mod t, they
# must be relatively prime (coprime)
if gcd(t, 65537) == 1:
break
key.n = p * q
if gmpyLoaded or GMPY2_LOADED:
key.e = mpz(65537)
else:
key.e = 65537
key.d = invMod(key.e, t)
key.p = p
key.q = q
key.dP = key.d % (p-1)
key.dQ = key.d % (q-1)
key.qInv = invMod(q, p)
key.key_type = key_type
# pylint: enable=invalid-name
return key
@staticmethod
@deprecated_params({"data": "s", "password_callback": "passwordCallback"})
def parsePEM(data, password_callback=None):
"""Parse a string containing a PEM-encoded <privateKey>."""
from .python_key import Python_Key
return Python_Key.parsePEM(data, password_callback)