Total Complexity | 97 |
Total Lines | 532 |
Duplicated Lines | 24.81 % |
Changes | 0 |
Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like rsa._version200 often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
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 |