diff options
author | Legrandin <gooksankoo@hoiptorrow.mailexpire.com> | 2011-10-18 23:20:26 +0200 |
---|---|---|
committer | Legrandin <gooksankoo@hoiptorrow.mailexpire.com> | 2011-10-18 23:20:26 +0200 |
commit | c22fa18c0dedb43a8b19dcb9b29512ba59e1764b (patch) | |
tree | e7864a848ed2c37d4a2c0d65bcae0f0cbdc6ea27 /lib/Crypto/Util | |
parent | 897b75983c31a9e2630af92161e6206c2480685e (diff) | |
parent | b9658a26003ebfcfce1804a2363a29354799b47e (diff) | |
download | pycrypto-c22fa18c0dedb43a8b19dcb9b29512ba59e1764b.tar.gz |
Merged from upstream (py3k support) and modified so that all unit tests pass.
Diffstat (limited to 'lib/Crypto/Util')
-rw-r--r-- | lib/Crypto/Util/Counter.py | 18 | ||||
-rw-r--r-- | lib/Crypto/Util/RFC1751.py | 20 | ||||
-rw-r--r-- | lib/Crypto/Util/_number_new.py | 4 | ||||
-rw-r--r-- | lib/Crypto/Util/asn1.py | 61 | ||||
-rw-r--r-- | lib/Crypto/Util/number.py | 56 | ||||
-rw-r--r-- | lib/Crypto/Util/py21compat.py (renamed from lib/Crypto/Util/python_compat.py) | 4 | ||||
-rw-r--r-- | lib/Crypto/Util/py3compat.py | 70 | ||||
-rw-r--r-- | lib/Crypto/Util/wrapper.py | 41 |
8 files changed, 192 insertions, 82 deletions
diff --git a/lib/Crypto/Util/Counter.py b/lib/Crypto/Util/Counter.py index 42dab42..f00099b 100644 --- a/lib/Crypto/Util/Counter.py +++ b/lib/Crypto/Util/Counter.py @@ -22,13 +22,16 @@ # SOFTWARE. # =================================================================== -from Crypto.Util.python_compat import * +import sys +if sys.version_info[0] == 2 and sys.version_info[1] == 1: + from Crypto.Util.py21compat import * +from Crypto.Util.py3compat import * from Crypto.Util import _counter import struct # Factory function -def new(nbits, prefix="", suffix="", initial_value=1, overflow=0, little_endian=False, allow_wraparound=False, disable_shortcut=False): +def new(nbits, prefix=b(""), suffix=b(""), initial_value=1, overflow=0, little_endian=False, allow_wraparound=False, disable_shortcut=False): # TODO: Document this # Sanity-check the message size @@ -42,20 +45,21 @@ def new(nbits, prefix="", suffix="", initial_value=1, overflow=0, little_endian= raise ValueError("nbits too large") initval = _encode(initial_value, nbytes, little_endian) + if little_endian: - return _counter._newLE(str(prefix), str(suffix), initval, allow_wraparound=allow_wraparound, disable_shortcut=disable_shortcut) + return _counter._newLE(bstr(prefix), bstr(suffix), initval, allow_wraparound=allow_wraparound, disable_shortcut=disable_shortcut) else: - return _counter._newBE(str(prefix), str(suffix), initval, allow_wraparound=allow_wraparound, disable_shortcut=disable_shortcut) + return _counter._newBE(bstr(prefix), bstr(suffix), initval, allow_wraparound=allow_wraparound, disable_shortcut=disable_shortcut) def _encode(n, nbytes, little_endian=False): retval = [] n = long(n) for i in range(nbytes): if little_endian: - retval.append(chr(n & 0xff)) + retval.append(bchr(n & 0xff)) else: - retval.insert(0, chr(n & 0xff)) + retval.insert(0, bchr(n & 0xff)) n >>= 8 - return "".join(retval) + return b("").join(retval) # vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/lib/Crypto/Util/RFC1751.py b/lib/Crypto/Util/RFC1751.py index 85e0e99..9786e6f 100644 --- a/lib/Crypto/Util/RFC1751.py +++ b/lib/Crypto/Util/RFC1751.py @@ -1,4 +1,3 @@ -#!/usr/local/bin/python # rfc1751.py : Converts between 128-bit strings and a human-readable # sequence of words, as defined in RFC1751: "A Convention for # Human-Readable 128-bit Keys", by Daniel L. McDonald. @@ -28,7 +27,8 @@ __revision__ = "$Id$" -import string, binascii +import binascii +from Crypto.Util.py3compat import * binary={0:'0000', 1:'0001', 2:'0010', 3:'0011', 4:'0100', 5:'0101', 6:'0110', 7:'0111', 8:'1000', 9:'1001', 10:'1010', 11:'1011', @@ -36,18 +36,18 @@ binary={0:'0000', 1:'0001', 2:'0010', 3:'0011', 4:'0100', 5:'0101', def _key2bin(s): "Convert a key into a string of binary digits" - kl=map(lambda x: ord(x), s) + kl=map(lambda x: bord(x), s) kl=map(lambda x: binary[x>>4]+binary[x&15], kl) return ''.join(kl) def _extract(key, start, length): - """Extract a bitstring from a string of binary digits, and return its + """Extract a bitstring(2.x)/bytestring(2.x) from a string of binary digits, and return its numeric value.""" k=key[start:start+length] return reduce(lambda x,y: x*2+ord(y)-48, k, 0) def key_to_english (key): - """key_to_english(key:string) : string + """key_to_english(key:string(2.x)/bytes(3.x)) : string Transform an arbitrary key into a string containing English words. The key length must be a multiple of 8. """ @@ -58,20 +58,20 @@ def key_to_english (key): skbin=_key2bin(subkey) ; p=0 for i in range(0, 64, 2): p=p+_extract(skbin, i, 2) # Append parity bits to the subkey - skbin=_key2bin(subkey+chr((p<<6) & 255)) + skbin=_key2bin(subkey+bchr((p<<6) & 255)) for i in range(0, 64, 11): english=english+wordlist[_extract(skbin, i, 11)]+' ' return english[:-1] # Remove the trailing space -def english_to_key (str): - """english_to_key(string):string +def english_to_key (s): + """english_to_key(string):string(2.x)/bytes(2.x) Transform a string into a corresponding key. The string must contain words separated by whitespace; the number of words must be a multiple of 6. """ - L=string.split(string.upper(str)) ; key='' + L=s.upper().split() ; key=b('') for index in range(0, len(L), 6): sublist=L[index:index+6] ; char=9*[0] ; bits=0 for i in sublist: @@ -88,7 +88,7 @@ def english_to_key (str): char[(bits>>3)+1] = char[(bits>>3)+1] | cr else: char[bits>>3] = char[bits>>3] | cr bits=bits+11 - subkey=reduce(lambda x,y:x+chr(y), char, '') + subkey=reduce(lambda x,y:x+bchr(y), char, b('')) # Check the parity of the resulting key skbin=_key2bin(subkey) diff --git a/lib/Crypto/Util/_number_new.py b/lib/Crypto/Util/_number_new.py index 2640392..b040025 100644 --- a/lib/Crypto/Util/_number_new.py +++ b/lib/Crypto/Util/_number_new.py @@ -27,7 +27,9 @@ __revision__ = "$Id$" __all__ = ['ceil_shift', 'ceil_div', 'floor_div', 'exact_log2', 'exact_div'] -from Crypto.Util.python_compat import * +import sys +if sys.version_info[0] == 2 and sys.version_info[1] == 1: + from Crypto.Util.py21compat import * def ceil_shift(n, b): """Return ceil(n / 2**b) without performing any floating-point or division operations. diff --git a/lib/Crypto/Util/asn1.py b/lib/Crypto/Util/asn1.py index f7658b4..e09b3ff 100644 --- a/lib/Crypto/Util/asn1.py +++ b/lib/Crypto/Util/asn1.py @@ -21,8 +21,10 @@ # =================================================================== from Crypto.Util.number import long_to_bytes, bytes_to_long +import sys +from Crypto.Util.py3compat import * -__all__ = [ 'DerObject', 'DerInteger', 'DerOctetString', 'DerNull', 'DerSequence' ] +__all__ = [ 'DerObject', 'DerInteger', 'DerOctetString', 'DerNull', 'DerSequence', 'DerObjectId' ] class DerObject: """Base class for defining a single DER object. @@ -31,35 +33,41 @@ class DerObject: """ # Known TAG types - typeTags = { 'SEQUENCE':'\x30', 'BIT STRING':'\x03', 'INTEGER':'\x02', - 'OCTET STRING':'\x04', 'NULL':'\x05', 'OBJECT IDENTIFIER':'\x06'} + typeTags = { 'SEQUENCE': 0x30, 'BIT STRING': 0x03, 'INTEGER': 0x02, + 'OCTET STRING': 0x04, 'NULL': 0x05, 'OBJECT IDENTIFIER': 0x06 } - def __init__(self, ASN1Type=None, payload=''): + def __init__(self, ASN1Type=None, payload=b('')): """Initialize the DER object according to a specific type. The ASN.1 type is either specified as the ASN.1 string (e.g. 'SEQUENCE'), directly with its numerical tag or with no tag - atl all (None).""" - self.typeTag = self.typeTags.get(ASN1Type, ASN1Type) + at all (None).""" + if isInt(ASN1Type) or ASN1Type is None: + self.typeTag = ASN1Type + else: + if len(ASN1Type)==1: + self.typeTag = ord(ASN1Type) + else: + self.typeTag = self.typeTags.get(ASN1Type) self.payload = payload def isType(self, ASN1Type): return self.typeTags[ASN1Type]==self.typeTag def _lengthOctets(self, payloadLen): - """Return a string that encodes the given payload length (in + """Return a byte string that encodes the given payload length (in bytes) in a format suitable for a DER length tag (L). """ if payloadLen>127: encoding = long_to_bytes(payloadLen) - return chr(len(encoding)+128) + encoding - return chr(payloadLen) + return bchr(len(encoding)+128) + encoding + return bchr(payloadLen) def encode(self): """Return a complete DER element, fully encoded as a TLV.""" - return self.typeTag + self._lengthOctets(len(self.payload)) + self.payload + return bchr(self.typeTag) + self._lengthOctets(len(self.payload)) + self.payload - def _decodeLen(self, idx, str): + def _decodeLen(self, idx, der): """Given a (part of a) DER element, and an index to the first byte of a DER length tag (L), return a tuple with the payload size, and the index of the first byte of the such payload (V). @@ -67,10 +75,10 @@ class DerObject: Raises a ValueError exception if the DER length is invalid. Raises an IndexError exception if the DER element is too short. """ - length = ord(str[idx]) + length = bord(der[idx]) if length<=127: return (length,idx+1) - payloadLength = bytes_to_long(str[idx+1:idx+1+(length & 0x7F)]) + payloadLength = bytes_to_long(der[idx+1:idx+1+(length & 0x7F)]) if payloadLength<=127: raise ValueError("Not a DER length tag.") return (payloadLength, idx+1+(length & 0x7F)) @@ -90,8 +98,8 @@ class DerObject: Raises an IndexError exception if the DER element is too short. """ try: - self.typeTag = derEle[0] - if (ord(self.typeTag) & 0x1F)==0x1F: + self.typeTag = bord(derEle[0]) + if (self.typeTag & 0x1F)==0x1F: raise ValueError("Unsupported DER tag") (length,idx) = self._decodeLen(1, derEle) if noLeftOvers and len(derEle) != (idx+length): @@ -113,8 +121,8 @@ class DerInteger(DerObject): def encode(self): """Return a complete INTEGER DER element, fully encoded as a TLV.""" self.payload = long_to_bytes(self.value) - if ord(self.payload[0])>127: - self.payload = '\x00' + self.payload + if bord(self.payload[0])>127: + self.payload = bchr(0x00) + self.payload return DerObject.encode(self) def decode(self, derEle, noLeftOvers=0): @@ -135,7 +143,7 @@ class DerInteger(DerObject): tlvLength = DerObject.decode(self, derEle, noLeftOvers) if self.typeTag!=self.typeTags['INTEGER']: raise ValueError ("Not a DER INTEGER.") - if ord(self.payload[0])>127: + if bord(self.payload[0])>127: raise ValueError ("Negative INTEGER.") self.value = bytes_to_long(self.payload) return tlvLength @@ -179,13 +187,6 @@ class DerSequence(DerObject): def hasInts(self): """Return the number of items in this sequence that are numbers.""" - def isInt(x): - test = 0 - try: - test += x - except TypeError: - return 0 - return 1 return len(filter(isInt, self._seq)) def hasOnlyInts(self): @@ -199,7 +200,7 @@ class DerSequence(DerObject): Limitation: Raises a ValueError exception if it some elements in the sequence are neither Python integers nor complete DER INTEGERs. """ - self.payload = '' + self.payload = b('') for item in self._seq: try: self.payload += item @@ -275,3 +276,11 @@ class DerObjectId(DerObject): raise ValueError("Not a valid OBJECT IDENTIFIER.") return p +def isInt(x): + test = 0 + try: + test += x + except TypeError: + return 0 + return 1 + diff --git a/lib/Crypto/Util/number.py b/lib/Crypto/Util/number.py index 7be595b..2b5beb6 100644 --- a/lib/Crypto/Util/number.py +++ b/lib/Crypto/Util/number.py @@ -26,15 +26,36 @@ __revision__ = "$Id$" -from Crypto.pct_warnings import GetRandomNumber_DeprecationWarning +from Crypto.pct_warnings import GetRandomNumber_DeprecationWarning, PowmInsecureWarning +from warnings import warn as _warn import math +import sys +from Crypto.Util.py3compat import * bignum = long try: from Crypto.PublicKey import _fastmath except ImportError: + # For production, we are going to let import issues due to gmp/mpir shared + # libraries not loading slide silently and use slowmath. If you'd rather + # see an exception raised if _fastmath exists but cannot be imported, + # uncomment the below + # + # from distutils.sysconfig import get_config_var + # import inspect, os + # _fm_path = os.path.normpath(os.path.dirname(os.path.abspath( + # inspect.getfile(inspect.currentframe()))) + # +"/../../PublicKey/_fastmath"+get_config_var("SO")) + # if os.path.exists(_fm_path): + # raise ImportError("While the _fastmath module exists, importing "+ + # "it failed. This may point to the gmp or mpir shared library "+ + # "not being in the path. _fastmath was found at "+_fm_path) _fastmath = None +# You need libgmp v5 or later to get mpz_powm_sec. Warn if it's not available. +if _fastmath is not None and not _fastmath.HAVE_DECL_MPZ_POWM_SEC: + _warn("Not using mpz_powm_sec. You should rebuild using libgmp >= 5 to avoid timing attack vulnerability.", PowmInsecureWarning) + # New functions from _number_new import * @@ -62,7 +83,8 @@ def size (N): def getRandomNumber(N, randfunc=None): """Deprecated. Use getRandomInteger or getRandomNBitInteger instead.""" - warnings.warn("Crypto.Util.number.getRandomNumber has confusing semantics and has been deprecated. Use getRandomInteger or getRandomNBitInteger instead.", + warnings.warn("Crypto.Util.number.getRandomNumber has confusing semantics"+ + "and has been deprecated. Use getRandomInteger or getRandomNBitInteger instead.", GetRandomNumber_DeprecationWarning) return getRandomNBitInteger(N, randfunc) @@ -83,7 +105,7 @@ def getRandomInteger(N, randfunc=None): odd_bits = N % 8 if odd_bits != 0: char = ord(randfunc(1)) >> (8-odd_bits) - S = chr(char) + S + S = bchr(char) + S value = bytes_to_long(S) return value @@ -221,7 +243,7 @@ def getStrongPrime(N, e=0, false_positive_prob=1e-6, randfunc=None): The optional false_positive_prob is the statistical probability that true is returned even though it is not (pseudo-prime). It defaults to 1e-6 (less than 1:1000000). - Note that the real probability of a false-positiv is far less. This is + Note that the real probability of a false-positive is far less. This is just the mathematically provable limit. randfunc should take a single int parameter and return that @@ -239,7 +261,8 @@ def getStrongPrime(N, e=0, false_positive_prob=1e-6, randfunc=None): # Use the accelerator if available if _fastmath is not None: - return _fastmath.getStrongPrime(long(N), long(e), false_positive_prob, randfunc) + return _fastmath.getStrongPrime(long(N), long(e), false_positive_prob, + randfunc) if (N < 512) or ((N % 128) != 0): raise ValueError ("bits must be multiple of 128 and > 512") @@ -263,7 +286,6 @@ def getStrongPrime(N, e=0, false_positive_prob=1e-6, randfunc=None): for i in (0, 1): # randomly choose 101-bit y y = getRandomNBitInteger (101, randfunc) - # initialize the field for sieving field = [0] * 5 * len (sieve_base) # sieve the field @@ -300,13 +322,13 @@ def getStrongPrime(N, e=0, false_positive_prob=1e-6, randfunc=None): X = X + (R - (X % increment)) while 1: is_possible_prime = 1 - # first check canidate against sieve_base + # first check candidate against sieve_base for prime in sieve_base: if (X % prime) == 0: is_possible_prime = 0 break # if e is given make sure that e and X-1 are coprime - # this is not necessarily a strong prime criterion but usefull when + # this is not necessarily a strong prime criterion but useful when # creating them for RSA where the p-1 and q-1 should be coprime to # the public exponent e if e and is_possible_prime: @@ -314,8 +336,9 @@ def getStrongPrime(N, e=0, false_positive_prob=1e-6, randfunc=None): if GCD (e, X-1) != 1: is_possible_prime = 0 else: - if GCD (e, (X-1)/2) != 1: + if GCD (e, divmod((X-1),2)[0]) != 1: is_possible_prime = 0 + # do some Rabin-Miller-Tests if is_possible_prime: result = _rabinMillerTest (X, rabin_miller_rounds) @@ -336,7 +359,7 @@ def isPrime(N, false_positive_prob=1e-6, randfunc=None): The optional false_positive_prob is the statistical probability that true is returned even though it is not (pseudo-prime). It defaults to 1e-6 (less than 1:1000000). - Note that the real probability of a false-positiv is far less. This is + Note that the real probability of a false-positive is far less. This is just the mathematically provable limit. If randfunc is omitted, then Random.new().read is used. @@ -370,7 +393,7 @@ def long_to_bytes(n, blocksize=0): blocksize. """ # after much testing, this algorithm was deemed to be the fastest - s = '' + s = b('') n = long(n) pack = struct.pack while n > 0: @@ -378,17 +401,17 @@ def long_to_bytes(n, blocksize=0): n = n >> 32 # strip off leading zeros for i in range(len(s)): - if s[i] != '\000': + if s[i] != b('\000')[0]: break else: # only happens when n == 0 - s = '\000' + s = b('\000') i = 0 s = s[i:] # add back some pad bytes. this could be done more efficiently w.r.t. the # de-padding being done above, but sigh... if blocksize > 0 and len(s) % blocksize: - s = (blocksize - len(s) % blocksize) * '\000' + s + s = (blocksize - len(s) % blocksize) * b('\000') + s return s def bytes_to_long(s): @@ -402,7 +425,7 @@ def bytes_to_long(s): length = len(s) if length % 4: extra = (4 - length % 4) - s = '\000' * extra + s + s = b('\000') * extra + s length = length + extra for i in range(0, length, 4): acc = (acc << 32) + unpack('>I', s[i:i+4])[0] @@ -418,7 +441,8 @@ def str2long(s): return bytes_to_long(s) def _import_Random(): - # This is called in a function instead of at the module level in order to avoid problems with recursive imports + # This is called in a function instead of at the module level in order to + # avoid problems with recursive imports global Random, StrongRandom from Crypto import Random from Crypto.Random.random import StrongRandom diff --git a/lib/Crypto/Util/python_compat.py b/lib/Crypto/Util/py21compat.py index 7eb2803..624408b 100644 --- a/lib/Crypto/Util/python_compat.py +++ b/lib/Crypto/Util/py21compat.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Util/python_compat.py : Compatibility code for old versions of Python +# Util/py21compat.py : Compatibility code for Python 2.1 # # Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net> # @@ -22,7 +22,7 @@ # SOFTWARE. # =================================================================== -"""Compatibility code for old versions of Python +"""Compatibility code for Python 2.1 Currently, this just defines: - True and False diff --git a/lib/Crypto/Util/py3compat.py b/lib/Crypto/Util/py3compat.py new file mode 100644 index 0000000..76d168b --- /dev/null +++ b/lib/Crypto/Util/py3compat.py @@ -0,0 +1,70 @@ +# -*- coding: utf-8 -*- +# +# Util/py3compat.py : Compatibility code for handling Py3k / Python 2.x +# +# Written in 2010 by Thorsten Behrens +# +# =================================================================== +# The contents of this file are dedicated to the public domain. To +# the extent that dedication to the public domain is not available, +# everyone is granted a worldwide, perpetual, royalty-free, +# non-exclusive license to exercise all rights associated with the +# contents of this file for any purpose whatsoever. +# No rights are reserved. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# =================================================================== + +"""Compatibility code for handling string/bytes changes from Python 2.x to Py3k +""" + +__revision__ = "$Id$" + +import sys + +if sys.version_info[0] == 2: + def b(s): + return s + def bchr(s): + return chr(s) + def bstr(s): + return str(s) + def bord(s): + return ord(s) + def tobytes(s): + if isinstance(s,str): + return s + else: + if isinstance(s, unicode): + return s.encode("latin-1") + else: + ''.join(s) +else: + def b(s): + return s.encode("latin-1") # utf-8 would cause some side-effects we don't want + def bchr(s): + return bytes([s]) + def bstr(s): + if isinstance(s,str): + return bytes(s,"latin-1") + else: + return bytes(s) + def bord(s): + return s + def tobytes(s): + if isinstance(s,bytes): + return s + else: + if isinstance(s,str): + return s.encode("latin-1") + else: + return bytes(s) + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/lib/Crypto/Util/wrapper.py b/lib/Crypto/Util/wrapper.py index 479ae26..1090fc7 100644 --- a/lib/Crypto/Util/wrapper.py +++ b/lib/Crypto/Util/wrapper.py @@ -1,6 +1,6 @@ # # wrapper.py: Small class to wrap an object, instantiated from a class -# or generated by a module. +# or generated by a module. # # =================================================================== # The contents of this file are dedicated to the public domain. To @@ -24,23 +24,24 @@ __all__ = [ 'Wrapper' ] class Wrapper: - ''' - Wrapper for an object, instantiated from a class - or from a call to a new() function in a module. - ''' - def __init__(self, wrapped, *args): - """ - wrapped is either a class or a module with a new() function. - """ - if hasattr(wrapped, 'new'): - self._wrapped = wrapped.new(*args) - else: - self._wrapped = wrapped(*args) - def __getattr__(self, name): - try: - return getattr(getattr(self,'_wrapped'),name) - except AttributeError: - if hasattr(self, name): - return getattr(self,name) - raise + ''' + Wrapper for an object, instantiated from a class + or from a call to a new() function in a module. + ''' + def __init__(self, wrapped, *args): + """ + wrapped is either a class or a module with a new() function. + """ + if hasattr(wrapped, 'new'): + self._wrapped = wrapped.new(*args) + else: + self._wrapped = wrapped(*args) + + def __getattr__(self, name): + try: + return getattr(getattr(self,'_wrapped'),name) + except AttributeError: + if hasattr(self, name): + return getattr(self,name) + raise |