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