1
|
|
|
"""RSA module |
2
|
|
|
|
3
|
|
|
Module for calculating large primes, and RSA encryption, decryption, |
4
|
|
|
signing and verification. Includes generating public and private keys. |
5
|
|
|
|
6
|
|
|
WARNING: this implementation does not use random padding, compression of the |
7
|
|
|
cleartext input to prevent repetitions, or other common security improvements. |
8
|
|
|
Use with care. |
9
|
|
|
|
10
|
|
|
""" |
11
|
|
|
|
12
|
|
|
__author__ = "Sybren Stuvel, Marloes de Boer, Ivo Tamboer, and Barry Mead" |
13
|
|
|
__date__ = "2010-02-08" |
14
|
|
|
__version__ = '2.0' |
15
|
|
|
|
16
|
|
|
import math |
17
|
|
|
import os |
18
|
|
|
import random |
19
|
|
|
import sys |
20
|
|
|
import types |
21
|
|
|
|
22
|
|
|
# Display a warning that this insecure version is imported. |
23
|
|
|
import warnings |
24
|
|
|
warnings.warn('Insecure version of the RSA module is imported as %s' % __name__) |
25
|
|
|
|
26
|
|
|
|
27
|
|
|
def bit_size(number): |
28
|
|
|
"""Returns the number of bits required to hold a specific long number""" |
29
|
|
|
|
30
|
|
|
return int(math.ceil(math.log(number,2))) |
31
|
|
|
|
32
|
|
|
def gcd(p, q): |
33
|
|
|
"""Returns the greatest common divisor of p and q |
34
|
|
|
>>> gcd(48, 180) |
35
|
|
|
12 |
36
|
|
|
""" |
37
|
|
|
# Iterateive Version is faster and uses much less stack space |
38
|
|
|
while q != 0: |
39
|
|
|
if p < q: (p,q) = (q,p) |
40
|
|
|
(p,q) = (q, p % q) |
41
|
|
|
return p |
42
|
|
|
|
43
|
|
|
|
44
|
|
View Code Duplication |
def bytes2int(bytes): |
|
|
|
|
45
|
|
|
"""Converts a list of bytes or a string to an integer |
46
|
|
|
|
47
|
|
|
>>> (((128 * 256) + 64) * 256) + 15 |
48
|
|
|
8405007 |
49
|
|
|
>>> l = [128, 64, 15] |
50
|
|
|
>>> bytes2int(l) #same as bytes2int('\x80@\x0f') |
51
|
|
|
8405007 |
52
|
|
|
""" |
53
|
|
|
|
54
|
|
|
if not (type(bytes) is types.ListType or type(bytes) is types.StringType): |
55
|
|
|
raise TypeError("You must pass a string or a list") |
56
|
|
|
|
57
|
|
|
# Convert byte stream to integer |
58
|
|
|
integer = 0 |
59
|
|
|
for byte in bytes: |
60
|
|
|
integer *= 256 |
61
|
|
|
if type(byte) is types.StringType: byte = ord(byte) |
62
|
|
|
integer += byte |
63
|
|
|
|
64
|
|
|
return integer |
65
|
|
|
|
66
|
|
View Code Duplication |
def int2bytes(number): |
|
|
|
|
67
|
|
|
"""Converts a number to a string of bytes |
68
|
|
|
|
69
|
|
|
>>>int2bytes(123456789) |
70
|
|
|
'\x07[\xcd\x15' |
71
|
|
|
>>> bytes2int(int2bytes(123456789)) |
72
|
|
|
123456789 |
73
|
|
|
""" |
74
|
|
|
|
75
|
|
|
if not (type(number) is types.LongType or type(number) is types.IntType): |
76
|
|
|
raise TypeError("You must pass a long or an int") |
77
|
|
|
|
78
|
|
|
string = "" |
79
|
|
|
|
80
|
|
|
while number > 0: |
81
|
|
|
string = "%s%s" % (chr(number & 0xFF), string) |
82
|
|
|
number /= 256 |
83
|
|
|
|
84
|
|
|
return string |
85
|
|
|
|
86
|
|
|
def to64(number): |
87
|
|
|
"""Converts a number in the range of 0 to 63 into base 64 digit |
88
|
|
|
character in the range of '0'-'9', 'A'-'Z', 'a'-'z','-','_'. |
89
|
|
|
|
90
|
|
|
>>> to64(10) |
91
|
|
|
'A' |
92
|
|
|
""" |
93
|
|
|
|
94
|
|
|
if not (type(number) is types.LongType or type(number) is types.IntType): |
95
|
|
|
raise TypeError("You must pass a long or an int") |
96
|
|
|
|
97
|
|
|
if 0 <= number <= 9: #00-09 translates to '0' - '9' |
98
|
|
|
return chr(number + 48) |
99
|
|
|
|
100
|
|
|
if 10 <= number <= 35: |
101
|
|
|
return chr(number + 55) #10-35 translates to 'A' - 'Z' |
102
|
|
|
|
103
|
|
|
if 36 <= number <= 61: |
104
|
|
|
return chr(number + 61) #36-61 translates to 'a' - 'z' |
105
|
|
|
|
106
|
|
|
if number == 62: # 62 translates to '-' (minus) |
107
|
|
|
return chr(45) |
108
|
|
|
|
109
|
|
|
if number == 63: # 63 translates to '_' (underscore) |
110
|
|
|
return chr(95) |
111
|
|
|
|
112
|
|
|
raise ValueError(u'Invalid Base64 value: %i' % number) |
113
|
|
|
|
114
|
|
|
|
115
|
|
|
def from64(number): |
116
|
|
|
"""Converts an ordinal character value in the range of |
117
|
|
|
0-9,A-Z,a-z,-,_ to a number in the range of 0-63. |
118
|
|
|
|
119
|
|
|
>>> from64(49) |
120
|
|
|
1 |
121
|
|
|
""" |
122
|
|
|
|
123
|
|
|
if not (type(number) is types.LongType or type(number) is types.IntType): |
124
|
|
|
raise TypeError("You must pass a long or an int") |
125
|
|
|
|
126
|
|
|
if 48 <= number <= 57: #ord('0') - ord('9') translates to 0-9 |
127
|
|
|
return(number - 48) |
128
|
|
|
|
129
|
|
|
if 65 <= number <= 90: #ord('A') - ord('Z') translates to 10-35 |
130
|
|
|
return(number - 55) |
131
|
|
|
|
132
|
|
|
if 97 <= number <= 122: #ord('a') - ord('z') translates to 36-61 |
133
|
|
|
return(number - 61) |
134
|
|
|
|
135
|
|
|
if number == 45: #ord('-') translates to 62 |
136
|
|
|
return(62) |
137
|
|
|
|
138
|
|
|
if number == 95: #ord('_') translates to 63 |
139
|
|
|
return(63) |
140
|
|
|
|
141
|
|
|
raise ValueError(u'Invalid Base64 value: %i' % number) |
142
|
|
|
|
143
|
|
|
|
144
|
|
|
def int2str64(number): |
145
|
|
|
"""Converts a number to a string of base64 encoded characters in |
146
|
|
|
the range of '0'-'9','A'-'Z,'a'-'z','-','_'. |
147
|
|
|
|
148
|
|
|
>>> int2str64(123456789) |
149
|
|
|
'7MyqL' |
150
|
|
|
""" |
151
|
|
|
|
152
|
|
|
if not (type(number) is types.LongType or type(number) is types.IntType): |
153
|
|
|
raise TypeError("You must pass a long or an int") |
154
|
|
|
|
155
|
|
|
string = "" |
156
|
|
|
|
157
|
|
|
while number > 0: |
158
|
|
|
string = "%s%s" % (to64(number & 0x3F), string) |
159
|
|
|
number /= 64 |
160
|
|
|
|
161
|
|
|
return string |
162
|
|
|
|
163
|
|
|
|
164
|
|
|
def str642int(string): |
165
|
|
|
"""Converts a base64 encoded string into an integer. |
166
|
|
|
The chars of this string in in the range '0'-'9','A'-'Z','a'-'z','-','_' |
167
|
|
|
|
168
|
|
|
>>> str642int('7MyqL') |
169
|
|
|
123456789 |
170
|
|
|
""" |
171
|
|
|
|
172
|
|
|
if not (type(string) is types.ListType or type(string) is types.StringType): |
173
|
|
|
raise TypeError("You must pass a string or a list") |
174
|
|
|
|
175
|
|
|
integer = 0 |
176
|
|
|
for byte in string: |
177
|
|
|
integer *= 64 |
178
|
|
|
if type(byte) is types.StringType: byte = ord(byte) |
179
|
|
|
integer += from64(byte) |
180
|
|
|
|
181
|
|
|
return integer |
182
|
|
|
|
183
|
|
|
def read_random_int(nbits): |
184
|
|
|
"""Reads a random integer of approximately nbits bits rounded up |
185
|
|
|
to whole bytes""" |
186
|
|
|
|
187
|
|
|
nbytes = int(math.ceil(nbits/8.)) |
188
|
|
|
randomdata = os.urandom(nbytes) |
189
|
|
|
return bytes2int(randomdata) |
190
|
|
|
|
191
|
|
View Code Duplication |
def randint(minvalue, maxvalue): |
|
|
|
|
192
|
|
|
"""Returns a random integer x with minvalue <= x <= maxvalue""" |
193
|
|
|
|
194
|
|
|
# Safety - get a lot of random data even if the range is fairly |
195
|
|
|
# small |
196
|
|
|
min_nbits = 32 |
197
|
|
|
|
198
|
|
|
# The range of the random numbers we need to generate |
199
|
|
|
range = (maxvalue - minvalue) + 1 |
200
|
|
|
|
201
|
|
|
# Which is this number of bytes |
202
|
|
|
rangebytes = ((bit_size(range) + 7) / 8) |
203
|
|
|
|
204
|
|
|
# Convert to bits, but make sure it's always at least min_nbits*2 |
205
|
|
|
rangebits = max(rangebytes * 8, min_nbits * 2) |
206
|
|
|
|
207
|
|
|
# Take a random number of bits between min_nbits and rangebits |
208
|
|
|
nbits = random.randint(min_nbits, rangebits) |
209
|
|
|
|
210
|
|
|
return (read_random_int(nbits) % range) + minvalue |
211
|
|
|
|
212
|
|
View Code Duplication |
def jacobi(a, b): |
|
|
|
|
213
|
|
|
"""Calculates the value of the Jacobi symbol (a/b) |
214
|
|
|
where both a and b are positive integers, and b is odd |
215
|
|
|
""" |
216
|
|
|
|
217
|
|
|
if a == 0: return 0 |
218
|
|
|
result = 1 |
219
|
|
|
while a > 1: |
220
|
|
|
if a & 1: |
221
|
|
|
if ((a-1)*(b-1) >> 2) & 1: |
222
|
|
|
result = -result |
223
|
|
|
a, b = b % a, a |
224
|
|
|
else: |
225
|
|
|
if (((b * b) - 1) >> 3) & 1: |
226
|
|
|
result = -result |
227
|
|
|
a >>= 1 |
228
|
|
|
if a == 0: return 0 |
229
|
|
|
return result |
230
|
|
|
|
231
|
|
View Code Duplication |
def jacobi_witness(x, n): |
|
|
|
|
232
|
|
|
"""Returns False if n is an Euler pseudo-prime with base x, and |
233
|
|
|
True otherwise. |
234
|
|
|
""" |
235
|
|
|
|
236
|
|
|
j = jacobi(x, n) % n |
237
|
|
|
f = pow(x, (n-1)/2, n) |
238
|
|
|
|
239
|
|
|
if j == f: return False |
240
|
|
|
return True |
241
|
|
|
|
242
|
|
|
def randomized_primality_testing(n, k): |
243
|
|
|
"""Calculates whether n is composite (which is always correct) or |
244
|
|
|
prime (which is incorrect with error probability 2**-k) |
245
|
|
|
|
246
|
|
|
Returns False if the number is composite, and True if it's |
247
|
|
|
probably prime. |
248
|
|
|
""" |
249
|
|
|
|
250
|
|
|
# 50% of Jacobi-witnesses can report compositness of non-prime numbers |
251
|
|
|
|
252
|
|
|
for i in range(k): |
253
|
|
|
x = randint(1, n-1) |
254
|
|
|
if jacobi_witness(x, n): return False |
255
|
|
|
|
256
|
|
|
return True |
257
|
|
|
|
258
|
|
|
def is_prime(number): |
259
|
|
|
"""Returns True if the number is prime, and False otherwise. |
260
|
|
|
|
261
|
|
|
>>> is_prime(42) |
262
|
|
|
0 |
263
|
|
|
>>> is_prime(41) |
264
|
|
|
1 |
265
|
|
|
""" |
266
|
|
|
|
267
|
|
|
if randomized_primality_testing(number, 6): |
268
|
|
|
# Prime, according to Jacobi |
269
|
|
|
return True |
270
|
|
|
|
271
|
|
|
# Not prime |
272
|
|
|
return False |
273
|
|
|
|
274
|
|
|
|
275
|
|
|
def getprime(nbits): |
276
|
|
|
"""Returns a prime number of max. 'math.ceil(nbits/8)*8' bits. In |
277
|
|
|
other words: nbits is rounded up to whole bytes. |
278
|
|
|
|
279
|
|
|
>>> p = getprime(8) |
280
|
|
|
>>> is_prime(p-1) |
281
|
|
|
0 |
282
|
|
|
>>> is_prime(p) |
283
|
|
|
1 |
284
|
|
|
>>> is_prime(p+1) |
285
|
|
|
0 |
286
|
|
|
""" |
287
|
|
|
|
288
|
|
|
while True: |
289
|
|
|
integer = read_random_int(nbits) |
290
|
|
|
|
291
|
|
|
# Make sure it's odd |
292
|
|
|
integer |= 1 |
293
|
|
|
|
294
|
|
|
# Test for primeness |
295
|
|
|
if is_prime(integer): break |
296
|
|
|
|
297
|
|
|
# Retry if not prime |
298
|
|
|
|
299
|
|
|
return integer |
300
|
|
|
|
301
|
|
|
def are_relatively_prime(a, b): |
302
|
|
|
"""Returns True if a and b are relatively prime, and False if they |
303
|
|
|
are not. |
304
|
|
|
|
305
|
|
|
>>> are_relatively_prime(2, 3) |
306
|
|
|
1 |
307
|
|
|
>>> are_relatively_prime(2, 4) |
308
|
|
|
0 |
309
|
|
|
""" |
310
|
|
|
|
311
|
|
|
d = gcd(a, b) |
312
|
|
|
return (d == 1) |
313
|
|
|
|
314
|
|
|
def find_p_q(nbits): |
315
|
|
|
"""Returns a tuple of two different primes of nbits bits""" |
316
|
|
|
pbits = nbits + (nbits/16) #Make sure that p and q aren't too close |
317
|
|
|
qbits = nbits - (nbits/16) #or the factoring programs can factor n |
318
|
|
|
p = getprime(pbits) |
319
|
|
|
while True: |
320
|
|
|
q = getprime(qbits) |
321
|
|
|
#Make sure p and q are different. |
322
|
|
|
if not q == p: break |
323
|
|
|
return (p, q) |
324
|
|
|
|
325
|
|
View Code Duplication |
def extended_gcd(a, b): |
|
|
|
|
326
|
|
|
"""Returns a tuple (r, i, j) such that r = gcd(a, b) = ia + jb |
327
|
|
|
""" |
328
|
|
|
# r = gcd(a,b) i = multiplicitive inverse of a mod b |
329
|
|
|
# or j = multiplicitive inverse of b mod a |
330
|
|
|
# Neg return values for i or j are made positive mod b or a respectively |
331
|
|
|
# Iterateive Version is faster and uses much less stack space |
332
|
|
|
x = 0 |
333
|
|
|
y = 1 |
334
|
|
|
lx = 1 |
335
|
|
|
ly = 0 |
336
|
|
|
oa = a #Remember original a/b to remove |
337
|
|
|
ob = b #negative values from return results |
338
|
|
|
while b != 0: |
339
|
|
|
q = long(a/b) |
|
|
|
|
340
|
|
|
(a, b) = (b, a % b) |
341
|
|
|
(x, lx) = ((lx - (q * x)),x) |
342
|
|
|
(y, ly) = ((ly - (q * y)),y) |
343
|
|
|
if (lx < 0): lx += ob #If neg wrap modulo orignal b |
344
|
|
|
if (ly < 0): ly += oa #If neg wrap modulo orignal a |
345
|
|
|
return (a, lx, ly) #Return only positive values |
346
|
|
|
|
347
|
|
|
# Main function: calculate encryption and decryption keys |
348
|
|
View Code Duplication |
def calculate_keys(p, q, nbits): |
|
|
|
|
349
|
|
|
"""Calculates an encryption and a decryption key for p and q, and |
350
|
|
|
returns them as a tuple (e, d)""" |
351
|
|
|
|
352
|
|
|
n = p * q |
353
|
|
|
phi_n = (p-1) * (q-1) |
354
|
|
|
|
355
|
|
|
while True: |
356
|
|
|
# Make sure e has enough bits so we ensure "wrapping" through |
357
|
|
|
# modulo n |
358
|
|
|
e = max(65537,getprime(nbits/4)) |
359
|
|
|
if are_relatively_prime(e, n) and are_relatively_prime(e, phi_n): break |
360
|
|
|
|
361
|
|
|
(d, i, j) = extended_gcd(e, phi_n) |
362
|
|
|
|
363
|
|
|
if not d == 1: |
364
|
|
|
raise Exception("e (%d) and phi_n (%d) are not relatively prime" % (e, phi_n)) |
365
|
|
|
if (i < 0): |
366
|
|
|
raise Exception("New extended_gcd shouldn't return negative values") |
367
|
|
|
if not (e * i) % phi_n == 1: |
368
|
|
|
raise Exception("e (%d) and i (%d) are not mult. inv. modulo phi_n (%d)" % (e, i, phi_n)) |
369
|
|
|
|
370
|
|
|
return (e, i) |
371
|
|
|
|
372
|
|
|
|
373
|
|
|
def gen_keys(nbits): |
374
|
|
|
"""Generate RSA keys of nbits bits. Returns (p, q, e, d). |
375
|
|
|
|
376
|
|
|
Note: this can take a long time, depending on the key size. |
377
|
|
|
""" |
378
|
|
|
|
379
|
|
|
(p, q) = find_p_q(nbits) |
380
|
|
|
(e, d) = calculate_keys(p, q, nbits) |
381
|
|
|
|
382
|
|
|
return (p, q, e, d) |
383
|
|
|
|
384
|
|
|
def newkeys(nbits): |
385
|
|
|
"""Generates public and private keys, and returns them as (pub, |
386
|
|
|
priv). |
387
|
|
|
|
388
|
|
|
The public key consists of a dict {e: ..., , n: ....). The private |
389
|
|
|
key consists of a dict {d: ...., p: ...., q: ....). |
390
|
|
|
""" |
391
|
|
|
nbits = max(9,nbits) # Don't let nbits go below 9 bits |
392
|
|
|
(p, q, e, d) = gen_keys(nbits) |
393
|
|
|
|
394
|
|
|
return ( {'e': e, 'n': p*q}, {'d': d, 'p': p, 'q': q} ) |
395
|
|
|
|
396
|
|
|
def encrypt_int(message, ekey, n): |
397
|
|
|
"""Encrypts a message using encryption key 'ekey', working modulo n""" |
398
|
|
|
|
399
|
|
|
if type(message) is types.IntType: |
400
|
|
|
message = long(message) |
|
|
|
|
401
|
|
|
|
402
|
|
|
if not type(message) is types.LongType: |
403
|
|
|
raise TypeError("You must pass a long or int") |
404
|
|
|
|
405
|
|
|
if message < 0 or message > n: |
406
|
|
|
raise OverflowError("The message is too long") |
407
|
|
|
|
408
|
|
|
#Note: Bit exponents start at zero (bit counts start at 1) this is correct |
409
|
|
|
safebit = bit_size(n) - 2 #compute safe bit (MSB - 1) |
410
|
|
|
message += (1 << safebit) #add safebit to ensure folding |
411
|
|
|
|
412
|
|
|
return pow(message, ekey, n) |
413
|
|
|
|
414
|
|
|
def decrypt_int(cyphertext, dkey, n): |
415
|
|
|
"""Decrypts a cypher text using the decryption key 'dkey', working |
416
|
|
|
modulo n""" |
417
|
|
|
|
418
|
|
|
message = pow(cyphertext, dkey, n) |
419
|
|
|
|
420
|
|
|
safebit = bit_size(n) - 2 #compute safe bit (MSB - 1) |
421
|
|
|
message -= (1 << safebit) #remove safebit before decode |
422
|
|
|
|
423
|
|
|
return message |
424
|
|
|
|
425
|
|
|
def encode64chops(chops): |
426
|
|
|
"""base64encodes chops and combines them into a ',' delimited string""" |
427
|
|
|
|
428
|
|
|
chips = [] #chips are character chops |
429
|
|
|
|
430
|
|
|
for value in chops: |
431
|
|
|
chips.append(int2str64(value)) |
432
|
|
|
|
433
|
|
|
#delimit chops with comma |
434
|
|
|
encoded = ','.join(chips) |
435
|
|
|
|
436
|
|
|
return encoded |
437
|
|
|
|
438
|
|
|
def decode64chops(string): |
439
|
|
|
"""base64decodes and makes a ',' delimited string into chops""" |
440
|
|
|
|
441
|
|
|
chips = string.split(',') #split chops at commas |
442
|
|
|
|
443
|
|
|
chops = [] |
444
|
|
|
|
445
|
|
|
for string in chips: #make char chops (chips) into chops |
446
|
|
|
chops.append(str642int(string)) |
447
|
|
|
|
448
|
|
|
return chops |
449
|
|
|
|
450
|
|
|
def chopstring(message, key, n, funcref): |
451
|
|
|
"""Chops the 'message' into integers that fit into n, |
452
|
|
|
leaving room for a safebit to be added to ensure that all |
453
|
|
|
messages fold during exponentiation. The MSB of the number n |
454
|
|
|
is not independant modulo n (setting it could cause overflow), so |
455
|
|
|
use the next lower bit for the safebit. Therefore reserve 2-bits |
456
|
|
|
in the number n for non-data bits. Calls specified encryption |
457
|
|
|
function for each chop. |
458
|
|
|
|
459
|
|
|
Used by 'encrypt' and 'sign'. |
460
|
|
|
""" |
461
|
|
|
|
462
|
|
|
msglen = len(message) |
463
|
|
|
mbits = msglen * 8 |
464
|
|
|
#Set aside 2-bits so setting of safebit won't overflow modulo n. |
465
|
|
|
nbits = bit_size(n) - 2 # leave room for safebit |
466
|
|
|
nbytes = nbits / 8 |
467
|
|
|
blocks = msglen / nbytes |
468
|
|
|
|
469
|
|
|
if msglen % nbytes > 0: |
470
|
|
|
blocks += 1 |
471
|
|
|
|
472
|
|
|
cypher = [] |
473
|
|
|
|
474
|
|
|
for bindex in range(blocks): |
475
|
|
|
offset = bindex * nbytes |
476
|
|
|
block = message[offset:offset+nbytes] |
477
|
|
|
value = bytes2int(block) |
478
|
|
|
cypher.append(funcref(value, key, n)) |
479
|
|
|
|
480
|
|
|
return encode64chops(cypher) #Encode encrypted ints to base64 strings |
481
|
|
|
|
482
|
|
|
def gluechops(string, key, n, funcref): |
483
|
|
|
"""Glues chops back together into a string. calls |
484
|
|
|
funcref(integer, key, n) for each chop. |
485
|
|
|
|
486
|
|
|
Used by 'decrypt' and 'verify'. |
487
|
|
|
""" |
488
|
|
|
message = "" |
489
|
|
|
|
490
|
|
|
chops = decode64chops(string) #Decode base64 strings into integer chops |
491
|
|
|
|
492
|
|
|
for cpart in chops: |
493
|
|
|
mpart = funcref(cpart, key, n) #Decrypt each chop |
494
|
|
|
message += int2bytes(mpart) #Combine decrypted strings into a msg |
495
|
|
|
|
496
|
|
|
return message |
497
|
|
|
|
498
|
|
|
def encrypt(message, key): |
499
|
|
|
"""Encrypts a string 'message' with the public key 'key'""" |
500
|
|
|
if 'n' not in key: |
501
|
|
|
raise Exception("You must use the public key with encrypt") |
502
|
|
|
|
503
|
|
|
return chopstring(message, key['e'], key['n'], encrypt_int) |
504
|
|
|
|
505
|
|
|
def sign(message, key): |
506
|
|
|
"""Signs a string 'message' with the private key 'key'""" |
507
|
|
|
if 'p' not in key: |
508
|
|
|
raise Exception("You must use the private key with sign") |
509
|
|
|
|
510
|
|
|
return chopstring(message, key['d'], key['p']*key['q'], encrypt_int) |
511
|
|
|
|
512
|
|
|
def decrypt(cypher, key): |
513
|
|
|
"""Decrypts a string 'cypher' with the private key 'key'""" |
514
|
|
|
if 'p' not in key: |
515
|
|
|
raise Exception("You must use the private key with decrypt") |
516
|
|
|
|
517
|
|
|
return gluechops(cypher, key['d'], key['p']*key['q'], decrypt_int) |
518
|
|
|
|
519
|
|
|
def verify(cypher, key): |
520
|
|
|
"""Verifies a string 'cypher' with the public key 'key'""" |
521
|
|
|
if 'n' not in key: |
522
|
|
|
raise Exception("You must use the public key with verify") |
523
|
|
|
|
524
|
|
|
return gluechops(cypher, key['e'], key['n'], decrypt_int) |
525
|
|
|
|
526
|
|
|
# Do doctest if we're not imported |
527
|
|
|
if __name__ == "__main__": |
528
|
|
|
import doctest |
529
|
|
|
doctest.testmod() |
530
|
|
|
|
531
|
|
|
__all__ = ["newkeys", "encrypt", "decrypt", "sign", "verify"] |
532
|
|
|
|
533
|
|
|
|