diff --git a/modules/Crypto/Cipher/AES.py b/modules/Crypto/Cipher/AES.py index b18b7d0..14f68d8 100644 --- a/modules/Crypto/Cipher/AES.py +++ b/modules/Crypto/Cipher/AES.py @@ -31,87 +31,32 @@ encryption. As an example, encryption can be done as follows: >>> from Crypto.Cipher import AES - >>> from Crypto.Random import get_random_bytes + >>> from Crypto import Random >>> >>> key = b'Sixteen byte key' - >>> iv = get_random_bytes(16) + >>> iv = Random.new().read(AES.block_size) >>> cipher = AES.new(key, AES.MODE_CFB, iv) >>> msg = iv + cipher.encrypt(b'Attack at dawn') -A more complicated example is based on CCM, (see `MODE_CCM`) an `AEAD`_ mode -that provides both confidentiality and authentication for a message. -It also allows message for the header to remain in the clear, whilst still -being authenticated. The encryption is done as follows: - - >>> from Crypto.Cipher import AES - >>> from Crypto.Random import get_random_bytes - >>> - >>> - >>> hdr = b'To your eyes only' - >>> plaintext = b'Attack at dawn' - >>> key = b'Sixteen byte key' - >>> nonce = get_random_bytes(11) - >>> cipher = AES.new(key, AES.MODE_CCM, nonce) - >>> cipher.update(hdr) - >>> msg = nonce, hdr, cipher.encrypt(plaintext), cipher.digest() - -We assume that the tuple ``msg`` is transmitted to the receiver: - - >>> nonce, hdr, ciphertext, mac = msg - >>> key = b'Sixteen byte key' - >>> cipher = AES.new(key, AES.MODE_CCM, nonce) - >>> cipher.update(hdr) - >>> plaintext = cipher.decrypt(ciphertext) - >>> try: - >>> cipher.verify(mac) - >>> print "The message is authentic: hdr=%s, pt=%s" % (hdr, plaintext) - >>> except ValueError: - >>> print "Key incorrect or message corrupted" - .. __: http://en.wikipedia.org/wiki/Advanced_Encryption_Standard .. _NIST: http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf -.. _AEAD: http://blog.cryptographyengineering.com/2012/05/how-to-choose-authenticated-encryption.html :undocumented: __revision__, __package__ """ __revision__ = "$Id$" -import sys -if sys.version_info[0] == 2 and sys.version_info[1] == 1: - from Crypto.Util.py21compat import * from Crypto.Cipher import blockalgo from Crypto.Cipher import _AES -from Crypto.Util import cpuid -# Import _AESNI. If AES-NI is not available or _AESNI has not been built, set -# _AESNI to None. -try: - if cpuid.have_aes_ni(): - from Crypto.Cipher import _AESNI - else: - _AESNI = None -except ImportError: - _AESNI = None class AESCipher (blockalgo.BlockAlgo): """AES cipher object""" def __init__(self, key, *args, **kwargs): """Initialize an AES cipher object - + See also `new()` at the module level.""" - - # Check if the use_aesni was specified. - use_aesni = True - if kwargs.has_key('use_aesni'): - use_aesni = kwargs['use_aesni'] - del kwargs['use_aesni'] - - # Use _AESNI if the user requested AES-NI and it's available - if _AESNI is not None and use_aesni: - blockalgo.BlockAlgo.__init__(self, _AESNI, key, *args, **kwargs) - else: - blockalgo.BlockAlgo.__init__(self, _AES, key, *args, **kwargs) + blockalgo.BlockAlgo.__init__(self, _AES, key, *args, **kwargs) def new(key, *args, **kwargs): """Create a new AES cipher @@ -120,15 +65,11 @@ def new(key, *args, **kwargs): key : byte string The secret key to use in the symmetric cipher. It must be 16 (*AES-128*), 24 (*AES-192*), or 32 (*AES-256*) bytes long. - - Only in `MODE_SIV`, it needs to be 32, 48, or 64 bytes long. :Keywords: mode : a *MODE_** constant The chaining mode to use for encryption or decryption. Default is `MODE_ECB`. IV : byte string - (*Only* `MODE_CBC`, `MODE_CFB`, `MODE_OFB`, `MODE_OPENPGP`). - The initialization vector to use for encryption or decryption. It is ignored for `MODE_ECB` and `MODE_CTR`. @@ -137,20 +78,9 @@ def new(key, *args, **kwargs): and `block_size` +2 bytes for decryption (in the latter case, it is actually the *encrypted* IV which was prefixed to the ciphertext). It is mandatory. - - For all other modes, it must be 16 bytes long. - nonce : byte string - (*Only* `MODE_CCM`, `MODE_EAX`, `MODE_GCM`, `MODE_SIV`). - - A mandatory value that must never be reused for any other encryption. - - For `MODE_CCM`, its length must be in the range ``[7..13]``. - 11 or 12 bytes are reasonable values in general. Bear in - mind that with CCM there is a trade-off between nonce length and - maximum message size. - - For the other modes, there are no restrictions on its length, - but it is recommended to use at least 16 bytes. + + For all other modes, it must be `block_size` bytes longs. It is optional and + when not present it will be given a default value of all zeroes. counter : callable (*Only* `MODE_CTR`). A stateful function that returns the next *counter block*, which is a byte string of `block_size` bytes. @@ -159,20 +89,6 @@ def new(key, *args, **kwargs): (*Only* `MODE_CFB`).The number of bits the plaintext and ciphertext are segmented in. It must be a multiple of 8. If 0 or not specified, it will be assumed to be 8. - mac_len : integer - (*Only* `MODE_CCM`). Length of the MAC, in bytes. It must be even and in - the range ``[4..16]``. The default is 16. - - (*Only* `MODE_EAX` and `MODE_GCM`). Length of the MAC, in bytes. It must be no - larger than 16 bytes (which is the default). - msg_len : integer - (*Only* `MODE_CCM`). Length of the message to (de)cipher. - If not specified, ``encrypt`` or ``decrypt`` may only be called once. - assoc_len : integer - (*Only* `MODE_CCM`). Length of the associated data. - If not specified, all data is internally buffered. - use_aesni : boolean - Use AES-NI if available. :Return: an `AESCipher` object """ @@ -192,14 +108,6 @@ MODE_OFB = 5 MODE_CTR = 6 #: OpenPGP Mode. See `blockalgo.MODE_OPENPGP`. MODE_OPENPGP = 7 -#: Counter with CBC-MAC (CCM) Mode. See `blockalgo.MODE_CCM`. -MODE_CCM = 8 -#: EAX Mode. See `blockalgo.MODE_EAX`. -MODE_EAX = 9 -#: Syntethic Initialization Vector (SIV). See `blockalgo.MODE_SIV`. -MODE_SIV = 10 -#: Galois Counter Mode (GCM). See `blockalgo.MODE_GCM`. -MODE_GCM = 11 #: Size of a data block (in bytes) block_size = 16 #: Size of a key (in bytes) diff --git a/modules/Crypto/Cipher/ARC2.py b/modules/Crypto/Cipher/ARC2.py index 1b3fa4e..7b5f43a 100644 --- a/modules/Crypto/Cipher/ARC2.py +++ b/modules/Crypto/Cipher/ARC2.py @@ -32,7 +32,7 @@ The company eventually published its full specification in RFC2268_. RC2 has a fixed data block size of 8 bytes. Length of its keys can vary from 8 to 128 bits. One particular property of RC2 is that the actual -cryptographic strength of the key (*effective key length*) can be reduced +cryptographic strength of the key (*effective key length*) can be reduced via a parameter. Even though RC2 is not cryptographically broken, it has not been analyzed as @@ -66,7 +66,7 @@ class RC2Cipher (blockalgo.BlockAlgo): def __init__(self, key, *args, **kwargs): """Initialize an ARC2 cipher object - + See also `new()` at the module level.""" blockalgo.BlockAlgo.__init__(self, _ARC2, key, *args, **kwargs) @@ -82,8 +82,6 @@ def new(key, *args, **kwargs): The chaining mode to use for encryption or decryption. Default is `MODE_ECB`. IV : byte string - (*Only* `MODE_CBC`, `MODE_CFB`, `MODE_OFB`, `MODE_OPENPGP`). - The initialization vector to use for encryption or decryption. It is ignored for `MODE_ECB` and `MODE_CTR`. @@ -92,20 +90,13 @@ def new(key, *args, **kwargs): and `block_size` +2 bytes for decryption (in the latter case, it is actually the *encrypted* IV which was prefixed to the ciphertext). It is mandatory. - - For all other modes, it must be 8 bytes long. - nonce : byte string - (*Only* `MODE_EAX`). - A mandatory value that must never be reused for any other encryption. - There are no restrictions on its length, but it is recommended to - use at least 16 bytes. + + For all other modes, it must be `block_size` bytes longs. It is optional and + when not present it will be given a default value of all zeroes. counter : callable (*Only* `MODE_CTR`). A stateful function that returns the next *counter block*, which is a byte string of `block_size` bytes. For better performance, use `Crypto.Util.Counter`. - mac_len : integer - (*Only* `MODE_EAX`). Length of the MAC, in bytes. - It must be no larger than 8 (which is the default). segment_size : integer (*Only* `MODE_CFB`).The number of bits the plaintext and ciphertext are segmented in. @@ -132,8 +123,6 @@ MODE_OFB = 5 MODE_CTR = 6 #: OpenPGP Mode. See `blockalgo.MODE_OPENPGP`. MODE_OPENPGP = 7 -#: EAX Mode. See `blockalgo.MODE_EAX`. -MODE_EAX = 9 #: Size of a data block (in bytes) block_size = 8 #: Size of a key (in bytes) diff --git a/modules/Crypto/Cipher/ARC4.py b/modules/Crypto/Cipher/ARC4.py index 48d39f1..b745e7c 100644 --- a/modules/Crypto/Cipher/ARC4.py +++ b/modules/Crypto/Cipher/ARC4.py @@ -63,7 +63,6 @@ As an example, encryption can be done as follows: __revision__ = "$Id$" -from Crypto.Util.py3compat import * from Crypto.Cipher import _ARC4 class ARC4Cipher: @@ -75,18 +74,7 @@ class ARC4Cipher: See also `new()` at the module level.""" - if len(args)>0: - ndrop = args[0] - args = args[1:] - else: - ndrop = kwargs.get('drop', 0) - if ndrop: del kwargs['drop'] self._cipher = _ARC4.new(key, *args, **kwargs) - if ndrop: - # This is OK even if the cipher is used for decryption, since encrypt - # and decrypt are actually the same thing with ARC4. - self._cipher.encrypt(b('\x00')*ndrop) - self.block_size = self._cipher.block_size self.key_size = self._cipher.key_size @@ -120,17 +108,8 @@ def new(key, *args, **kwargs): The secret key to use in the symmetric cipher. It can have any length, with a minimum of 40 bytes. Its cryptograpic strength is always capped to 2048 bits (256 bytes). - :Keywords: - drop : integer - The amount of bytes to discard from the initial part of the keystream. - In fact, such part has been found to be distinguishable from random - data (while it shouldn't) and also correlated to key. - - The recommended value is 3072_ bytes. The default value is 0. :Return: an `ARC4Cipher` object - - .. _3072: http://eprint.iacr.org/2002/067.pdf """ return ARC4Cipher(key, *args, **kwargs) diff --git a/modules/Crypto/Cipher/Blowfish.py b/modules/Crypto/Cipher/Blowfish.py index 92fad7f..3f12bea 100644 --- a/modules/Crypto/Cipher/Blowfish.py +++ b/modules/Crypto/Cipher/Blowfish.py @@ -60,7 +60,7 @@ class BlowfishCipher (blockalgo.BlockAlgo): def __init__(self, key, *args, **kwargs): """Initialize a Blowfish cipher object - + See also `new()` at the module level.""" blockalgo.BlockAlgo.__init__(self, _Blowfish, key, *args, **kwargs) @@ -76,8 +76,6 @@ def new(key, *args, **kwargs): The chaining mode to use for encryption or decryption. Default is `MODE_ECB`. IV : byte string - (*Only* `MODE_CBC`, `MODE_CFB`, `MODE_OFB`, `MODE_OPENPGP`). - The initialization vector to use for encryption or decryption. It is ignored for `MODE_ECB` and `MODE_CTR`. @@ -86,20 +84,13 @@ def new(key, *args, **kwargs): and `block_size` +2 bytes for decryption (in the latter case, it is actually the *encrypted* IV which was prefixed to the ciphertext). It is mandatory. - - For all other modes, it must be 8 bytes long. - nonce : byte string - (*Only* `MODE_EAX`). - A mandatory value that must never be reused for any other encryption. - There are no restrictions on its length, but it is recommended to - use at least 16 bytes. + + For all other modes, it must be `block_size` bytes longs. It is optional and + when not present it will be given a default value of all zeroes. counter : callable (*Only* `MODE_CTR`). A stateful function that returns the next *counter block*, which is a byte string of `block_size` bytes. For better performance, use `Crypto.Util.Counter`. - mac_len : integer - (*Only* `MODE_EAX`). Length of the MAC, in bytes. - It must be no larger than 8 (which is the default). segment_size : integer (*Only* `MODE_CFB`).The number of bits the plaintext and ciphertext are segmented in. @@ -123,8 +114,6 @@ MODE_OFB = 5 MODE_CTR = 6 #: OpenPGP Mode. See `blockalgo.MODE_OPENPGP`. MODE_OPENPGP = 7 -#: EAX Mode. See `blockalgo.MODE_EAX`. -MODE_EAX = 9 #: Size of a data block (in bytes) block_size = 8 #: Size of a key (in bytes) diff --git a/modules/Crypto/Cipher/CAST.py b/modules/Crypto/Cipher/CAST.py index 6e136a3..f08dab3 100644 --- a/modules/Crypto/Cipher/CAST.py +++ b/modules/Crypto/Cipher/CAST.py @@ -63,7 +63,7 @@ class CAST128Cipher(blockalgo.BlockAlgo): def __init__(self, key, *args, **kwargs): """Initialize a CAST-128 cipher object - + See also `new()` at the module level.""" blockalgo.BlockAlgo.__init__(self, _CAST, key, *args, **kwargs) @@ -79,8 +79,6 @@ def new(key, *args, **kwargs): The chaining mode to use for encryption or decryption. Default is `MODE_ECB`. IV : byte string - (*Only* `MODE_CBC`, `MODE_CFB`, `MODE_OFB`, `MODE_OPENPGP`). - The initialization vector to use for encryption or decryption. It is ignored for `MODE_ECB` and `MODE_CTR`. @@ -89,20 +87,13 @@ def new(key, *args, **kwargs): and `block_size` +2 bytes for decryption (in the latter case, it is actually the *encrypted* IV which was prefixed to the ciphertext). It is mandatory. - - For all other modes, it must be 8 bytes long. - nonce : byte string - (*Only* `MODE_EAX`). - A mandatory value that must never be reused for any other encryption. - There are no restrictions on its length, but it is recommended to - use at least 16 bytes. + + For all other modes, it must be `block_size` bytes longs. It is optional and + when not present it will be given a default value of all zeroes. counter : callable (*Only* `MODE_CTR`). A stateful function that returns the next *counter block*, which is a byte string of `block_size` bytes. For better performance, use `Crypto.Util.Counter`. - mac_len : integer - (*Only* `MODE_EAX`). Length of the MAC, in bytes. - It must be no larger than 8 (which is the default). segment_size : integer (*Only* `MODE_CFB`).The number of bits the plaintext and ciphertext are segmented in. @@ -126,8 +117,6 @@ MODE_OFB = 5 MODE_CTR = 6 #: OpenPGP Mode. See `blockalgo.MODE_OPENPGP`. MODE_OPENPGP = 7 -#: EAX Mode. See `blockalgo.MODE_EAX`. -MODE_EAX = 9 #: Size of a data block (in bytes) block_size = 8 #: Size of a key (in bytes) diff --git a/modules/Crypto/Cipher/DES.py b/modules/Crypto/Cipher/DES.py index 18a7bd1..2fae42f 100644 --- a/modules/Crypto/Cipher/DES.py +++ b/modules/Crypto/Cipher/DES.py @@ -33,12 +33,12 @@ DES should not be used for new designs. Use `AES`. As an example, encryption can be done as follows: - >>> from Crypto.Cipher import DES + >>> from Crypto.Cipher import DES3 >>> from Crypto import Random >>> - >>> key = b'-8B key-' - >>> iv = Random.new().read(DES.block_size) - >>> cipher = DES.new(key, DES.MODE_OFB, iv) + >>> key = b'Sixteen byte key' + >>> iv = Random.new().read(DES3.block_size) + >>> cipher = DES3.new(key, DES3.MODE_OFB, iv) >>> plaintext = b'sona si latine loqueris ' >>> msg = iv + cipher.encrypt(plaintext) @@ -58,7 +58,7 @@ class DESCipher(blockalgo.BlockAlgo): def __init__(self, key, *args, **kwargs): """Initialize a DES cipher object - + See also `new()` at the module level.""" blockalgo.BlockAlgo.__init__(self, _DES, key, *args, **kwargs) @@ -74,8 +74,6 @@ def new(key, *args, **kwargs): The chaining mode to use for encryption or decryption. Default is `MODE_ECB`. IV : byte string - (*Only* `MODE_CBC`, `MODE_CFB`, `MODE_OFB`, `MODE_OPENPGP`). - The initialization vector to use for encryption or decryption. It is ignored for `MODE_ECB` and `MODE_CTR`. @@ -84,20 +82,13 @@ def new(key, *args, **kwargs): and `block_size` +2 bytes for decryption (in the latter case, it is actually the *encrypted* IV which was prefixed to the ciphertext). It is mandatory. - - For all other modes, it must be 8 bytes long. - nonce : byte string - (*Only* `MODE_EAX`). - A mandatory value that must never be reused for any other encryption. - There are no restrictions on its length, but it is recommended to - use at least 16 bytes. + + For all other modes, it must be `block_size` bytes longs. It is optional and + when not present it will be given a default value of all zeroes. counter : callable (*Only* `MODE_CTR`). A stateful function that returns the next *counter block*, which is a byte string of `block_size` bytes. For better performance, use `Crypto.Util.Counter`. - mac_len : integer - (*Only* `MODE_EAX`). Length of the MAC, in bytes. - It must be no larger than 8 (which is the default). segment_size : integer (*Only* `MODE_CFB`).The number of bits the plaintext and ciphertext are segmented in. @@ -121,8 +112,6 @@ MODE_OFB = 5 MODE_CTR = 6 #: OpenPGP Mode. See `blockalgo.MODE_OPENPGP`. MODE_OPENPGP = 7 -#: EAX Mode. See `blockalgo.MODE_EAX`. -MODE_EAX = 9 #: Size of a data block (in bytes) block_size = 8 #: Size of a key (in bytes) diff --git a/modules/Crypto/Cipher/DES3.py b/modules/Crypto/Cipher/DES3.py index eae2dcc..7fedac8 100644 --- a/modules/Crypto/Cipher/DES3.py +++ b/modules/Crypto/Cipher/DES3.py @@ -44,14 +44,14 @@ as `AES`. As an example, encryption can be done as follows: - >>> from Crypto.Cipher import DES3 + >>> from Crypto.Cipher import DES >>> from Crypto import Random >>> from Crypto.Util import Counter >>> - >>> key = b'Sixteen byte key' - >>> nonce = Random.new().read(DES3.block_size/2) - >>> ctr = Counter.new(DES3.block_size*8/2, prefix=nonce) - >>> cipher = DES3.new(key, DES3.MODE_CTR, counter=ctr) + >>> key = b'-8B key-' + >>> nonce = Random.new().read(DES.block_size/2) + >>> ctr = Counter.new(DES.block_size*8/2, prefix=nonce) + >>> cipher = DES.new(key, DES.MODE_CTR, counter=ctr) >>> plaintext = b'We are no longer the knights who say ni!' >>> msg = nonce + cipher.encrypt(plaintext) @@ -71,7 +71,7 @@ class DES3Cipher(blockalgo.BlockAlgo): def __init__(self, key, *args, **kwargs): """Initialize a TDES cipher object - + See also `new()` at the module level.""" blockalgo.BlockAlgo.__init__(self, _DES3, key, *args, **kwargs) @@ -87,8 +87,6 @@ def new(key, *args, **kwargs): The chaining mode to use for encryption or decryption. Default is `MODE_ECB`. IV : byte string - (*Only* `MODE_CBC`, `MODE_CFB`, `MODE_OFB`, `MODE_OPENPGP`). - The initialization vector to use for encryption or decryption. It is ignored for `MODE_ECB` and `MODE_CTR`. @@ -97,20 +95,13 @@ def new(key, *args, **kwargs): and `block_size` +2 bytes for decryption (in the latter case, it is actually the *encrypted* IV which was prefixed to the ciphertext). It is mandatory. - - For all other modes, it must be 8 bytes long. - nonce : byte string - (*Only* `MODE_EAX`). - A mandatory value that must never be reused for any other encryption. - There are no restrictions on its length, but it is recommended to - use at least 16 bytes. + + For all other modes, it must be `block_size` bytes longs. It is optional and + when not present it will be given a default value of all zeroes. counter : callable (*Only* `MODE_CTR`). A stateful function that returns the next - *counter block*, which is a byte string of 8 bytes. + *counter block*, which is a byte string of `block_size` bytes. For better performance, use `Crypto.Util.Counter`. - mac_len : integer - (*Only* `MODE_EAX`). Length of the MAC, in bytes. - It must be no larger than 8 (which is the default). segment_size : integer (*Only* `MODE_CFB`).The number of bits the plaintext and ciphertext are segmented in. @@ -136,8 +127,6 @@ MODE_OFB = 5 MODE_CTR = 6 #: OpenPGP Mode. See `blockalgo.MODE_OPENPGP`. MODE_OPENPGP = 7 -#: EAX Mode. See `blockalgo.MODE_EAX`. -MODE_EAX = 9 #: Size of a data block (in bytes) block_size = 8 #: Size of a key (in bytes) diff --git a/modules/Crypto/Cipher/PKCS1_OAEP.py b/modules/Crypto/Cipher/PKCS1_OAEP.py index ff45719..9afe176 100644 --- a/modules/Crypto/Cipher/PKCS1_OAEP.py +++ b/modules/Crypto/Cipher/PKCS1_OAEP.py @@ -31,7 +31,7 @@ As an example, a sender may encrypt a message in this way: >>> from Crypto.Cipher import PKCS1_OAEP >>> from Crypto.PublicKey import RSA >>> - >>> message = b'To be encrypted' + >>> message = 'To be encrypted' >>> key = RSA.importKey(open('pubkey.der').read()) >>> cipher = PKCS1_OAEP.new(key) >>> ciphertext = cipher.encrypt(message) @@ -55,7 +55,7 @@ __revision__ = "$Id$" __all__ = [ 'new', 'PKCS1OAEP_Cipher' ] import Crypto.Signature.PKCS1_PSS -import Crypto.Hash.SHA1 +import Crypto.Hash.SHA from Crypto.Util.py3compat import * import Crypto.Util.number @@ -70,17 +70,17 @@ class PKCS1OAEP_Cipher: :Parameters: key : an RSA key object - If a private half is given, both encryption and decryption are possible. - If a public half is given, only encryption is possible. + If a private half is given, both encryption and decryption are possible. + If a public half is given, only encryption is possible. hashAlgo : hash object The hash function to use. This can be a module under `Crypto.Hash` or an existing hash object created from any of such modules. If not specified, - `Crypto.Hash.SHA1` is used. + `Crypto.Hash.SHA` (that is, SHA-1) is used. mgfunc : callable A mask generation function that accepts two parameters: a string to use as seed, and the lenth of the mask to generate, in bytes. If not specified, the standard MGF1 is used (a safe choice). - label : byte string + label : string A label to apply to this particular encryption. If not specified, an empty string is used. Specifying a label does not improve security. @@ -93,7 +93,7 @@ class PKCS1OAEP_Cipher: if hashAlgo: self._hashObj = hashAlgo else: - self._hashObj = Crypto.Hash.SHA1 + self._hashObj = Crypto.Hash.SHA if mgfunc: self._mgf = mgfunc @@ -117,12 +117,12 @@ class PKCS1OAEP_Cipher: section 7.1.1 of RFC3447. :Parameters: - message : byte string + message : string The message to encrypt, also known as plaintext. It can be of variable length, but not longer than the RSA modulus (in bytes) minus 2, minus twice the hash output size. - :Return: A byte string, the ciphertext in which the message is encrypted. + :Return: A string, the ciphertext in which the message is encrypted. It is as long as the RSA modulus (in bytes). :Raise ValueError: If the RSA key length is not sufficiently long to deal with the given @@ -173,10 +173,10 @@ class PKCS1OAEP_Cipher: section 7.1.2 of RFC3447. :Parameters: - ct : byte string + ct : string The ciphertext that contains the message to recover. - :Return: A byte string, the original message. + :Return: A string, the original message. :Raise ValueError: If the ciphertext length is incorrect, or if the decryption does not succeed. @@ -238,12 +238,12 @@ def new(key, hashAlgo=None, mgfunc=None, label=b('')): hashAlgo : hash object The hash function to use. This can be a module under `Crypto.Hash` or an existing hash object created from any of such modules. If not specified, - `Crypto.Hash.SHA1` is used. + `Crypto.Hash.SHA` (that is, SHA-1) is used. mgfunc : callable A mask generation function that accepts two parameters: a string to use as seed, and the lenth of the mask to generate, in bytes. If not specified, the standard MGF1 is used (a safe choice). - label : byte string + label : string A label to apply to this particular encryption. If not specified, an empty string is used. Specifying a label does not improve security. diff --git a/modules/Crypto/Cipher/PKCS1_v1_5.py b/modules/Crypto/Cipher/PKCS1_v1_5.py index 345ffc2..c89035d 100644 --- a/modules/Crypto/Cipher/PKCS1_v1_5.py +++ b/modules/Crypto/Cipher/PKCS1_v1_5.py @@ -34,7 +34,7 @@ As an example, a sender may encrypt a message in this way: >>> from Crypto.PublicKey import RSA >>> from Crypto.Hash import SHA >>> - >>> message = b'To be encrypted' + >>> message = 'To be encrypted' >>> h = SHA.new(message) >>> >>> key = RSA.importKey(open('pubkey.der').read()) diff --git a/modules/Crypto/Cipher/_AES.so b/modules/Crypto/Cipher/_AES.so new file mode 100755 index 0000000..d9bc7d1 Binary files /dev/null and b/modules/Crypto/Cipher/_AES.so differ diff --git a/modules/Crypto/Cipher/_ARC2.so b/modules/Crypto/Cipher/_ARC2.so new file mode 100755 index 0000000..761a686 Binary files /dev/null and b/modules/Crypto/Cipher/_ARC2.so differ diff --git a/modules/Crypto/Cipher/_ARC4.so b/modules/Crypto/Cipher/_ARC4.so new file mode 100755 index 0000000..c9e150b Binary files /dev/null and b/modules/Crypto/Cipher/_ARC4.so differ diff --git a/modules/Crypto/Cipher/_Blowfish.so b/modules/Crypto/Cipher/_Blowfish.so new file mode 100755 index 0000000..15e03cc Binary files /dev/null and b/modules/Crypto/Cipher/_Blowfish.so differ diff --git a/modules/Crypto/Cipher/_CAST.so b/modules/Crypto/Cipher/_CAST.so new file mode 100755 index 0000000..175297e Binary files /dev/null and b/modules/Crypto/Cipher/_CAST.so differ diff --git a/modules/Crypto/Cipher/_DES.so b/modules/Crypto/Cipher/_DES.so new file mode 100755 index 0000000..bffbeb9 Binary files /dev/null and b/modules/Crypto/Cipher/_DES.so differ diff --git a/modules/Crypto/Cipher/_DES3.so b/modules/Crypto/Cipher/_DES3.so new file mode 100755 index 0000000..de8fbd0 Binary files /dev/null and b/modules/Crypto/Cipher/_DES3.so differ diff --git a/modules/Crypto/Cipher/_XOR.so b/modules/Crypto/Cipher/_XOR.so new file mode 100755 index 0000000..3b42b0d Binary files /dev/null and b/modules/Crypto/Cipher/_XOR.so differ diff --git a/modules/Crypto/Cipher/blockalgo.py b/modules/Crypto/Cipher/blockalgo.py index 84b9bc3..dd183dc 100644 --- a/modules/Crypto/Cipher/blockalgo.py +++ b/modules/Crypto/Cipher/blockalgo.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Cipher/blockalgo.py +# Cipher/blockalgo.py # # =================================================================== # The contents of this file are dedicated to the public domain. To @@ -24,21 +24,8 @@ 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 binascii import unhexlify - -from Crypto.Util import Counter -from Crypto.Util.strxor import strxor -from Crypto.Util.number import long_to_bytes, bytes_to_long -import Crypto.Util.Counter -from Crypto.Hash import CMAC -from Crypto.Hash.CMAC import _SmoothMAC -from Crypto.Protocol.KDF import _S2V - -from Crypto.Util import galois - #: *Electronic Code Book (ECB)*. #: This is the simplest encryption mode. Each of the plaintext blocks #: is directly encrypted into a ciphertext block, independently of @@ -55,8 +42,8 @@ MODE_ECB = 1 #: (*IV*) is required. #: #: The *IV* is a data block to be transmitted to the receiver. -#: The *IV* can be made public, but it must be authenticated by the receiver -#: and it should be picked randomly. +#: The *IV* can be made public, but it must be authenticated by the receiver and +#: it should be picked randomly. #: #: See `NIST SP800-38A`_ , Section 6.2 . #: @@ -86,8 +73,7 @@ MODE_PGP = 4 #: *Output FeedBack (OFB)*. This mode is very similar to CBC, but it #: transforms the underlying block cipher into a stream cipher. -#: The keystream is the iterated block encryption of an -#: Initialization Vector (*IV*). +#: The keystream is the iterated block encryption of an Initialization Vector (*IV*). #: #: The *IV* is a data block to be transmitted to the receiver. #: The *IV* can be made public, but it should be picked randomly. @@ -114,7 +100,7 @@ MODE_OFB = 5 #: to the *IV* for the other modes) and increment its lowest **m** bits by #: one (modulo *2^m*) for each block. In most cases, **m** is chosen to be half #: the block size. -#: +#: #: Reusing the same *initial counter block* for encryptions done with the same #: key lead to catastrophic cryptograhic failures. #: @@ -124,574 +110,105 @@ MODE_OFB = 5 #: .. _`NIST SP800-38A` : http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf MODE_CTR = 6 -#: *OpenPGP CFB*. This mode is a variant of CFB, and it is only used in PGP and -#: OpenPGP_ applications. An Initialization Vector (*IV*) is required. -#: -#: Unlike CFB, the IV is not transmitted to the receiver. -#: Instead, the *encrypted* IV is. -#: The IV is a random data block. Two of its bytes are duplicated to act -#: as a checksum for the correctness of the key. The encrypted IV is -#: therefore 2 bytes longer than the clean IV. +#: OpenPGP. This mode is a variant of CFB, and it is only used in PGP and OpenPGP_ applications. +#: An Initialization Vector (*IV*) is required. +#: +#: Unlike CFB, the IV is not transmitted to the receiver. Instead, the *encrypted* IV is. +#: The IV is a random data block. Two of its bytes are duplicated to act as a checksum +#: for the correctness of the key. The encrypted IV is therefore 2 bytes longer than +#: the clean IV. #: #: .. _OpenPGP: http://tools.ietf.org/html/rfc4880 MODE_OPENPGP = 7 -#: *Counter with CBC-MAC (CCM)*. This is an Authenticated Encryption with -#: Associated Data (`AEAD`_) mode. It provides both confidentiality and -#: authenticity. -#: The header of the message may be left in the clear, if needed, and it will -#: still be subject to authentication. The decryption step tells the receiver -#: if the message comes from a source that really knowns the secret key. -#: Additionally, decryption detects if any part of the message - including the -#: header - has been modified or corrupted. -#: -#: This mode requires a nonce. The nonce shall never repeat for two -#: different messages encrypted with the same key, but it does not need -#: to be random. -#: Note that there is a trade-off between the size of the nonce and the -#: maximum size of a single message you can encrypt. -#: -#: It is important to use a large nonce if the key is reused across several -#: messages and the nonce is chosen randomly. -#: -#: It is acceptable to us a short nonce if the key is only used a few times or -#: if the nonce is taken from a counter. -#: -#: The following table shows the trade-off when the nonce is chosen at -#: random. The column on the left shows how many messages it takes -#: for the keystream to repeat **on average**. In practice, you will want to -#: stop using the key way before that. -#: -#: +--------------------+---------------+-------------------+ -#: | Avg. # of messages | nonce | Max. message | -#: | before keystream | size | size | -#: | repeats | (bytes) | (bytes) | -#: +====================+===============+===================+ -#: | 2**52 | 13 | 64K | -#: +--------------------+---------------+-------------------+ -#: | 2**48 | 12 | 16M | -#: +--------------------+---------------+-------------------+ -#: | 2**44 | 11 | 4G | -#: +--------------------+---------------+-------------------+ -#: | 2**40 | 10 | 1T | -#: +--------------------+---------------+-------------------+ -#: | 2**36 | 9 | 64P | -#: +--------------------+---------------+-------------------+ -#: | 2**32 | 8 | 16E | -#: +--------------------+---------------+-------------------+ -#: -#: This mode is only available for ciphers that operate on 128 bits blocks -#: (e.g. AES but not TDES). -#: -#: See `NIST SP800-38C`_ or RFC3610_ . -#: -#: .. _`NIST SP800-38C`: http://csrc.nist.gov/publications/nistpubs/800-38C/SP800-38C.pdf -#: .. _RFC3610: https://tools.ietf.org/html/rfc3610 -#: .. _AEAD: http://blog.cryptographyengineering.com/2012/05/how-to-choose-authenticated-encryption.html -MODE_CCM = 8 - -#: *EAX*. This is an Authenticated Encryption with Associated Data -#: (`AEAD`_) mode. It provides both confidentiality and authenticity. -#: -#: The header of the message may be left in the clear, if needed, and it will -#: still be subject to authentication. -#: -#: The decryption step tells the receiver if the message comes from a source -#: that really knowns the secret key. -#: Additionally, decryption detects if any part of the message - including the -#: header - has been modified or corrupted. -#: -#: This mode requires a nonce. The nonce shall never repeat for two -#: different messages encrypted with the same key, but it does not need to -#: be random. -# -#: This mode is only available for ciphers that operate on 64 or -#: 128 bits blocks. -#: -#: There are no official standards defining EAX. The implementation is based on -#: `a proposal`__ that was presented to NIST. -#: -#: .. _AEAD: http://blog.cryptographyengineering.com/2012/05/how-to-choose-authenticated-encryption.html -#: .. __: http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/eax/eax-spec.pdf -MODE_EAX = 9 - -#: *Synthetic Initialization Vector*. This is an Authenticated Encryption with -#: Associated Data (`AEAD`_) mode. It provides both confidentiality and -#: authenticity. -#: The header of the message may be left in the clear, if needed, and it will -#: still be subject to authentication. The decryption step tells the receiver -#: if the message comes from a source that really knowns the secret key. -#: Additionally, decryption detects if any part of the message - including the -#: header - has been modified or corrupted. -#: -#: If the data being encrypted is completely unpredictable to an adversary -#: (e.g. a secret key, for key wrapping purposes) a nonce is not strictly -#: required. -#: -#: Otherwise, a nonce has to be provided; the nonce shall never repeat -#: for two different messages encrypted with the same key, but it does not -#: need to be random. -#: -#: Unlike other AEAD modes such as CCM, EAX or GCM, accidental reuse of a -#: nonce is not catastrophic for the confidentiality of the message. The only -#: effect is that an attacker can tell when the same plaintext (and same -#: associated data) is protected with the same key. -#: -#: The length of the MAC is fixed to the block size of the underlying cipher. -#: The key size is twice the length of the key of the underlying cipher. -#: -#: This mode is only available for AES ciphers. -#: -#: +--------------------+---------------+-------------------+ -#: | Cipher | SIV MAC size | SIV key length | -#: | | (bytes) | (bytes) | -#: +====================+===============+===================+ -#: | AES-128 | 16 | 32 | -#: +--------------------+---------------+-------------------+ -#: | AES-192 | 16 | 48 | -#: +--------------------+---------------+-------------------+ -#: | AES-256 | 16 | 64 | -#: +--------------------+---------------+-------------------+ -#: -#: See `RFC5297`_ and the `original paper`__. -#: -#: .. _RFC5297: https://tools.ietf.org/html/rfc5297 -#: .. _AEAD: http://blog.cryptographyengineering.com/2012/05/how-to-choose-authenticated-encryption.html -#: .. __: http://www.cs.ucdavis.edu/~rogaway/papers/keywrap.pdf -MODE_SIV = 10 - -#: *Galois/Counter Mode (GCM)*. This is an Authenticated Encryption with -#: Associated Data (`AEAD`_) mode. It provides both confidentiality and -#: authenticity. -#: The header of the message may be left in the clear, if needed, and it will -#: still be subject to authentication. The decryption step tells the receiver -#: if the message comes from a source that really knowns the secret key. -#: Additionally, decryption detects if any part of the message - including the -#: header - has been modified or corrupted. -#: -#: This mode requires a nonce. The nonce shall never repeat for two -#: different messages encrypted with the same key, but it does not need to -#: be random. -#: -#: This mode is only available for ciphers that operate on 128 bits blocks -#: (e.g. AES but not TDES). -#: -#: See `NIST SP800-38D`_ . -#: -#: .. _`NIST SP800-38D`: http://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf -#: .. _AEAD: http://blog.cryptographyengineering.com/2012/05/how-to-choose-authenticated-encryption.html -MODE_GCM = 11 - - def _getParameter(name, index, args, kwargs, default=None): """Find a parameter in tuple and dictionary arguments a function receives""" - param = kwargs.get(name) - if len(args) > index: + if len(args)>index: if param: - raise TypeError("Parameter '%s' is specified twice" % name) + raise ValueError("Parameter '%s' is specified twice" % name) param = args[index] return param or default - - -class _CBCMAC(_SmoothMAC): - - def __init__(self, key, ciphermod): - _SmoothMAC.__init__(self, ciphermod.block_size, None, 0) - self._key = key - self._factory = ciphermod - - def _ignite(self, data): - if self._mac: - raise TypeError("_ignite() cannot be called twice") - - self._buffer.insert(0, data) - self._buffer_len += len(data) - self._mac = self._factory.new(self._key, MODE_CBC, bchr(0) * 16) - self.update(b("")) - - def _update(self, block_data): - self._t = self._mac.encrypt(block_data)[-16:] - - def _digest(self, left_data): - return self._t - - -class _GHASH(_SmoothMAC): - """GHASH function defined in NIST SP 800-38D, Algorithm 2. - - If X_1, X_2, .. X_m are the blocks of input data, the function - computes: - - X_1*H^{m} + X_2*H^{m-1} + ... + X_m*H - - in the Galois field GF(2^256) using the reducing polynomial - (x^128 + x^7 + x^2 + x + 1). - """ - - def __init__(self, hash_subkey, block_size, table_size='64K'): - _SmoothMAC.__init__(self, block_size, None, 0) - if table_size == '64K': - self._hash_subkey = galois._ghash_expand(hash_subkey) - else: - self._hash_subkey = hash_subkey - self._last_y = bchr(0) * 16 - self._mac = galois._ghash - - def copy(self): - clone = _GHASH(self._hash_subkey, self._bs, table_size='0K') - _SmoothMAC._deep_copy(self, clone) - clone._last_y = self._last_y - return clone - - def _update(self, block_data): - self._last_y = galois._ghash(block_data, self._last_y, - self._hash_subkey) - - def _digest(self, left_data): - return self._last_y - - + class BlockAlgo: """Class modelling an abstract block cipher.""" def __init__(self, factory, key, *args, **kwargs): self.mode = _getParameter('mode', 0, args, kwargs, default=MODE_ECB) self.block_size = factory.block_size - self._factory = factory - self._tag = None - - if self.mode == MODE_CCM: - if self.block_size != 16: - raise TypeError("CCM mode is only available for ciphers that operate on 128 bits blocks") - - self._mac_len = kwargs.get('mac_len', 16) # t - if self._mac_len not in (4, 6, 8, 10, 12, 14, 16): - raise ValueError("Parameter 'mac_len' must be even and in the range 4..16") - - self.nonce = _getParameter('nonce', 1, args, kwargs) # N - if not (self.nonce and 7 <= len(self.nonce) <= 13): - raise ValueError("Length of parameter 'nonce' must be" - " in the range 7..13 bytes") - - self._key = key - self._msg_len = kwargs.get('msg_len', None) # p - self._assoc_len = kwargs.get('assoc_len', None) # a - - self._cipherMAC = _CBCMAC(key, factory) - self._done_assoc_data = False # True when all associated data - # has been processed - - # Allowed transitions after initialization - self._next = [self.update, self.encrypt, self.decrypt, - self.digest, self.verify] - - # Try to start CCM - self._start_ccm() - - elif self.mode == MODE_OPENPGP: - self._start_PGP(factory, key, *args, **kwargs) - elif self.mode == MODE_EAX: - self._start_eax(factory, key, *args, **kwargs) - elif self.mode == MODE_SIV: - self._start_siv(factory, key, *args, **kwargs) - elif self.mode == MODE_GCM: - self._start_gcm(factory, key, *args, **kwargs) - else: + + if self.mode != MODE_OPENPGP: self._cipher = factory.new(key, *args, **kwargs) self.IV = self._cipher.IV - - def _start_gcm(self, factory, key, *args, **kwargs): - - if self.block_size != 16: - raise TypeError("GCM mode is only available for ciphers that operate on 128 bits blocks") - - self.nonce = _getParameter('nonce', 1, args, kwargs) - if not self.nonce: - raise TypeError("MODE_GCM requires a nonce") - - self._mac_len = kwargs.get('mac_len', 16) - if not (self._mac_len and 4 <= self._mac_len <= 16): - raise ValueError("Parameter 'mac_len' must not be larger than 16 bytes") - - # Allowed transitions after initialization - self._next = [self.update, self.encrypt, self.decrypt, - self.digest, self.verify] - - self._done_assoc_data = False - - # Length of the ciphertext or plaintext - self._msg_len = 0 - - # Step 1 in SP800-38D, Algorithm 4 (encryption) - Compute H - # See also Algorithm 5 (decryption) - hash_subkey = factory.new(key).encrypt(bchr(0) * 16) - - # Step 2 - Compute J0 (integer, not byte string!) - if len(self.nonce) == 12: - self._j0 = bytes_to_long(self.nonce + b("\x00\x00\x00\x01")) else: - fill = (16 - (len(self.nonce) % 16)) % 16 + 8 - ghash_in = (self.nonce + - bchr(0) * fill + - long_to_bytes(8 * len(self.nonce), 8)) - - mac = _GHASH(hash_subkey, factory.block_size, '0K') - mac.update(ghash_in) - self._j0 = bytes_to_long(mac.digest()) - - # Step 3 - Prepare GCTR cipher for encryption/decryption - ctr = Counter.new(128, initial_value=self._j0 + 1, - allow_wraparound=True) - self._cipher = self._factory.new(key, MODE_CTR, counter=ctr) - - # Step 5 - Bootstrat GHASH - self._cipherMAC = _GHASH(hash_subkey, factory.block_size, '64K') - - # Step 6 - Prepare GCTR cipher for GMAC - ctr = Counter.new(128, initial_value=self._j0, allow_wraparound=True) - self._tag_cipher = self._factory.new(key, MODE_CTR, counter=ctr) - - def _start_siv(self, factory, key, *args, **kwargs): - - subkey_size, rem = divmod(len(key), 2) - if rem: - raise ValueError("MODE_SIV requires a key twice as long as for the underlying cipher") - - # IV is optional - self.nonce = _getParameter('nonce', 1, args, kwargs) - - self._cipherMAC = _S2V(key[:subkey_size], ciphermod=factory) - self._subkey_ctr = key[subkey_size:] - self._mac_len = factory.block_size - - self._cipherMAC = self._cipherMAC - - # Allowed transitions after initialization - self._next = [self.update, self.encrypt, self.decrypt, - self.digest, self.verify] - - def _siv_ctr_cipher(self, tag): - """Create a new CTR cipher from the MAC in SIV mode""" - - tag_int = bytes_to_long(tag) - init_counter = tag_int ^ (tag_int & 0x8000000080000000L) - ctr = Counter.new(self._factory.block_size * 8, - initial_value=init_counter, - allow_wraparound=True) - - return self._factory.new(self._subkey_ctr, MODE_CTR, counter=ctr) - - def _start_eax(self, factory, key, *args, **kwargs): - - self.nonce = _getParameter('nonce', 1, args, kwargs) - if not self.nonce: - raise TypeError("MODE_EAX requires a nonce") - - # Allowed transitions after initialization - self._next = [self.update, self.encrypt, self.decrypt, - self.digest, self.verify] - - self._mac_len = kwargs.get('mac_len', self.block_size) - if not (self._mac_len and 4 <= self._mac_len <= self.block_size): - raise ValueError("Parameter 'mac_len' must not be larger than %d" - % self.block_size) - - self._omac = [ - CMAC.new(key, bchr(0) * (self.block_size - 1) + bchr(i), - ciphermod=factory) - for i in xrange(0, 3) - ] - - # Compute MAC of nonce - self._omac[0].update(self.nonce) - - self._cipherMAC = self._omac[1] - - # MAC of the nonce is also the initial counter for CTR encryption - counter_int = bytes_to_long(self._omac[0].digest()) - counter_obj = Crypto.Util.Counter.new( - self.block_size * 8, - initial_value=counter_int, - allow_wraparound=True) - self._cipher = factory.new(key, MODE_CTR, counter=counter_obj) - - def _start_PGP(self, factory, key, *args, **kwargs): - # OPENPGP mode. For details, see 13.9 in RCC4880. - # - # A few members are specifically created for this mode: - # - _encrypted_iv, set in this constructor - # - _done_first_block, set to True after the first encryption - # - _done_last_block, set to True after a partial block is processed - - self._done_first_block = False - self._done_last_block = False - self.IV = _getParameter('IV', 1, args, kwargs) - if self.IV is None: - # TODO: Decide whether 'IV' or 'iv' should be used going forward, - # and deprecate the other. 'IV' is consistent with the rest of - # PyCrypto, but 'iv' is more common in Python generally. For now, - # we'll support both here. When in doubt, use a positional - # parameter for now. + # OPENPGP mode. For details, see 13.9 in RCC4880. + # + # A few members are specifically created for this mode: + # - _encrypted_iv, set in this constructor + # - _done_first_block, set to True after the first encryption + # - _done_last_block, set to True after a partial block is processed + + self._done_first_block = False + self._done_last_block = False self.IV = _getParameter('iv', 1, args, kwargs) - if not self.IV: - raise ValueError("MODE_OPENPGP requires an IV") - - # Instantiate a temporary cipher to process the IV - IV_cipher = factory.new( - key, - MODE_CFB, - b('\x00') * self.block_size, # IV for CFB - segment_size=self.block_size * 8) - - # The cipher will be used for... - if len(self.IV) == self.block_size: - # ... encryption - self._encrypted_IV = IV_cipher.encrypt( - self.IV + self.IV[-2:] + # Plaintext - b('\x00') * (self.block_size - 2) # Padding - )[:self.block_size + 2] - elif len(self.IV) == self.block_size + 2: - # ... decryption - self._encrypted_IV = self.IV - self.IV = IV_cipher.decrypt( - self.IV + # Ciphertext - b('\x00') * (self.block_size - 2) # Padding - )[:self.block_size + 2] - if self.IV[-2:] != self.IV[-4:-2]: - raise ValueError("Failed integrity check for OPENPGP IV") - self.IV = self.IV[:-2] - else: - raise ValueError("Length of IV must be %d or %d bytes for MODE_OPENPGP" - % (self.block_size, self.block_size+2)) - - # Instantiate the cipher for the real PGP data - self._cipher = factory.new( - key, - MODE_CFB, - self._encrypted_IV[-self.block_size:], - segment_size=self.block_size * 8 - ) - - def _start_ccm(self, assoc_len=None, msg_len=None): - # CCM mode. This method creates the 2 ciphers used for the MAC - # (self._cipherMAC) and for the encryption/decryption (self._cipher). - # - # Member _assoc_buffer may already contain user data that needs to be - # authenticated. - - if self._cipherMAC.can_reduce(): - # Already started - return - if assoc_len is not None: - self._assoc_len = assoc_len - if msg_len is not None: - self._msg_len = msg_len - if None in (self._assoc_len, self._msg_len): - return - - # q is the length of Q, the encoding of the message length - q = 15 - len(self.nonce) - - ## Compute B_0 - flags = ( - 64 * (self._assoc_len > 0) + - 8 * divmod(self._mac_len - 2, 2)[0] + - (q - 1) - ) - b_0 = bchr(flags) + self.nonce + long_to_bytes(self._msg_len, q) - - # Start CBC MAC with zero IV - assoc_len_encoded = b('') - if self._assoc_len > 0: - if self._assoc_len < (2 ** 16 - 2 ** 8): - enc_size = 2 - elif self._assoc_len < (2L ** 32): - assoc_len_encoded = b('\xFF\xFE') - enc_size = 4 + if not self.IV: + raise ValueError("MODE_OPENPGP requires an IV") + + # Instantiate a temporary cipher to process the IV + IV_cipher = factory.new(key, MODE_CFB, + b('\x00')*self.block_size, # IV for CFB + segment_size=self.block_size*8) + + # The cipher will be used for... + if len(self.IV) == self.block_size: + # ... encryption + self._encrypted_IV = IV_cipher.encrypt( + self.IV + self.IV[-2:] + # Plaintext + b('\x00')*(self.block_size-2) # Padding + )[:self.block_size+2] + elif len(self.IV) == self.block_size+2: + # ... decryption + self._encrypted_IV = self.IV + self.IV = IV_cipher.decrypt(self.IV + # Ciphertext + b('\x00')*(self.block_size-2) # Padding + )[:self.block_size+2] + if self.IV[-2:] != self.IV[-4:-2]: + raise ValueError("Failed integrity check for OPENPGP IV") + self.IV = self.IV[:-2] else: - assoc_len_encoded = b('\xFF\xFF') - enc_size = 8 - assoc_len_encoded += long_to_bytes(self._assoc_len, enc_size) - self._cipherMAC._ignite(b_0 + assoc_len_encoded) + raise ValueError("Length of IV must be %d or %d bytes for MODE_OPENPGP" + % (self.block_size, self.block_size+2)) - # Start CTR cipher - prefix = bchr(q - 1) + self.nonce - ctr = Counter.new(128 - len(prefix) * 8, prefix, initial_value=0) - self._cipher = self._factory.new(self._key, MODE_CTR, counter=ctr) - # Will XOR against CBC MAC - self._s_0 = self._cipher.encrypt(bchr(0) * 16) - - def update(self, assoc_data): - """Protect associated data - - When using an AEAD mode like CCM, EAX, GCM or SIV, and - if there is any associated data, the caller has to invoke - this function one or more times, before using - ``decrypt`` or ``encrypt``. - - By *associated data* it is meant any data (e.g. packet headers) that - will not be encrypted and will be transmitted in the clear. - However, the receiver is still able to detect any modification to it. - In CCM and GCM, the *associated data* is also called - *additional authenticated data* (AAD). - In EAX, the *associated data* is called *header*. - - If there is no associated data, this method must not be called. - - The caller may split associated data in segments of any size, and - invoke this method multiple times, each time with the next segment. - - :Parameters: - assoc_data : byte string - A piece of associated data. There are no restrictions on its size. - """ - - if self.mode not in (MODE_CCM, MODE_EAX, MODE_SIV, MODE_GCM): - raise TypeError("update() not supported by this mode of operation") - - if self.update not in self._next: - raise TypeError("update() can only be called immediately after initialization") - - self._next = [self.update, self.encrypt, self.decrypt, - self.digest, self.verify] - - return self._cipherMAC.update(assoc_data) + # Instantiate the cipher for the real PGP data + self._cipher = factory.new(key, MODE_CFB, + self._encrypted_IV[-self.block_size:], + segment_size=self.block_size*8) def encrypt(self, plaintext): """Encrypt data with the key and the parameters set at initialization. - - A cipher object is stateful: once you have encrypted a message - you cannot encrypt (or decrypt) another message using the same - object. - - For `MODE_SIV` (always) and `MODE_CCM` (when ``msg_len`` was not - passed at initialization), this method can be called only **once**. - - For all other modes, the data to encrypt can be broken up in two or - more pieces and `encrypt` can be called multiple times. - + + The cipher object is stateful; encryption of a long block + of data can be broken up in two or more calls to `encrypt()`. That is, the statement: - + >>> c.encrypt(a) + c.encrypt(b) - is equivalent to: + is always equivalent to: >>> c.encrypt(a+b) That also means that you cannot reuse an object for encrypting or decrypting other data with the same key. - This function does not add any padding to the plaintext. - - - For `MODE_ECB` and `MODE_CBC`, *plaintext* length (in bytes) must be - a multiple of *block_size*. + This function does not perform any padding. + + - For `MODE_ECB`, `MODE_CBC`, and `MODE_OFB`, *plaintext* length + (in bytes) must be a multiple of *block_size*. - For `MODE_CFB`, *plaintext* length (in bytes) must be a multiple of *segment_size*/8. - - For `MODE_OFB`, `MODE_CTR` and all AEAD modes - *plaintext* can be of any length. + - For `MODE_CTR`, *plaintext* can be of any length. - For `MODE_OPENPGP`, *plaintext* must be a multiple of *block_size*, unless it is the last chunk of the message. @@ -708,15 +225,14 @@ class BlockAlgo: if self.mode == MODE_OPENPGP: padding_length = (self.block_size - len(plaintext) % self.block_size) % self.block_size - if padding_length > 0: - # CFB mode requires ciphertext to have length multiple - # of block size, + if padding_length>0: + # CFB mode requires ciphertext to have length multiple of block size, # but PGP mode allows the last block to be shorter if self._done_last_block: raise ValueError("Only the last chunk is allowed to have length not multiple of %d bytes", self.block_size) self._done_last_block = True - padded = plaintext + b('\x00') * padding_length + padded = plaintext + b('\x00')*padding_length res = self._cipher.encrypt(padded)[:len(plaintext)] else: res = self._cipher.encrypt(plaintext) @@ -725,312 +241,56 @@ class BlockAlgo: self._done_first_block = True return res - if self.mode in (MODE_CCM, MODE_EAX, MODE_SIV, MODE_GCM): - if self.encrypt not in self._next: - raise TypeError("encrypt() can only be called after initialization or an update()") - self._next = [self.encrypt, self.digest] - - if self.mode == MODE_CCM: - if self._assoc_len is None: - self._start_ccm(assoc_len=self._cipherMAC.get_len()) - if self._msg_len is None: - self._start_ccm(msg_len=len(plaintext)) - self._next = [self.digest] - if not self._done_assoc_data: - self._cipherMAC.zero_pad() - self._done_assoc_data = True - - self._cipherMAC.update(plaintext) - - if self.mode == MODE_SIV: - self._next = [self.digest] - - if self.nonce: - self._cipherMAC.update(self.nonce) - - self._cipherMAC.update(plaintext) - self._cipher = self._siv_ctr_cipher(self._cipherMAC.derive()) - - ct = self._cipher.encrypt(plaintext) - - if self.mode == MODE_EAX: - self._omac[2].update(ct) - - if self.mode == MODE_GCM: - if not self._done_assoc_data: - self._cipherMAC.zero_pad() - self._done_assoc_data = True - self._cipherMAC.update(ct) - self._msg_len += len(plaintext) - - return ct + return self._cipher.encrypt(plaintext) def decrypt(self, ciphertext): """Decrypt data with the key and the parameters set at initialization. - - A cipher object is stateful: once you have decrypted a message - you cannot decrypt (or encrypt) another message with the same - object. - - For `MODE_SIV` (always) and `MODE_CCM` (when ``msg_len`` was not - passed at initialization), this method can be called only **once**. - - For all other modes, the data to decrypt can be broken up in two or - more pieces and `decrypt` can be called multiple times. - + + The cipher object is stateful; decryption of a long block + of data can be broken up in two or more calls to `decrypt()`. That is, the statement: - + >>> c.decrypt(a) + c.decrypt(b) - is equivalent to: + is always equivalent to: >>> c.decrypt(a+b) That also means that you cannot reuse an object for encrypting or decrypting other data with the same key. - This function does not remove any padding from the plaintext. - - - For `MODE_ECB` and `MODE_CBC`, *ciphertext* length (in bytes) must - be a multiple of *block_size*. + This function does not perform any padding. + + - For `MODE_ECB`, `MODE_CBC`, and `MODE_OFB`, *ciphertext* length + (in bytes) must be a multiple of *block_size*. - For `MODE_CFB`, *ciphertext* length (in bytes) must be a multiple of *segment_size*/8. - - For `MODE_OFB`, `MODE_CTR` and all AEAD modes - *ciphertext* can be of any length. + - For `MODE_CTR`, *ciphertext* can be of any length. - For `MODE_OPENPGP`, *plaintext* must be a multiple of *block_size*, unless it is the last chunk of the message. - - For `MODE_SIV`, *ciphertext* can be of any length, but it must also - include the MAC (concatenated at the end). - :Parameters: ciphertext : byte string - The piece of data to decrypt (plus the MAC, for `MODE_SIV` only). - - :Return: the decrypted data (byte string). + The piece of data to decrypt. + :Return: the decrypted data (byte string, as long as *ciphertext*). """ - if self.mode == MODE_OPENPGP: padding_length = (self.block_size - len(ciphertext) % self.block_size) % self.block_size - if padding_length > 0: - # CFB mode requires ciphertext to have length multiple - # of block size, + if padding_length>0: + # CFB mode requires ciphertext to have length multiple of block size, # but PGP mode allows the last block to be shorter if self._done_last_block: raise ValueError("Only the last chunk is allowed to have length not multiple of %d bytes", self.block_size) self._done_last_block = True - padded = ciphertext + b('\x00') * padding_length + padded = ciphertext + b('\x00')*padding_length res = self._cipher.decrypt(padded)[:len(ciphertext)] else: res = self._cipher.decrypt(ciphertext) return res - if self.mode == MODE_SIV: - raise TypeError("decrypt() not allowed for SIV mode." - " Use decrypt_and_verify() instead.") + return self._cipher.decrypt(ciphertext) - if self.mode in (MODE_CCM, MODE_EAX, MODE_GCM): - - if self.decrypt not in self._next: - raise TypeError("decrypt() can only be called after initialization or an update()") - self._next = [self.decrypt, self.verify] - - if self.mode == MODE_CCM: - if self._assoc_len is None: - self._start_ccm(assoc_len=self._cipherMAC.get_len()) - if self._msg_len is None: - self._start_ccm(msg_len=len(ciphertext)) - self._next = [self.verify] - if not self._done_assoc_data: - self._cipherMAC.zero_pad() - self._done_assoc_data = True - - if self.mode == MODE_GCM: - if not self._done_assoc_data: - self._cipherMAC.zero_pad() - self._done_assoc_data = True - - self._cipherMAC.update(ciphertext) - self._msg_len += len(ciphertext) - - if self.mode == MODE_EAX: - self._omac[2].update(ciphertext) - - pt = self._cipher.decrypt(ciphertext) - - if self.mode == MODE_CCM: - self._cipherMAC.update(pt) - - return pt - - def digest(self): - """Compute the *binary* MAC tag in an AEAD mode. - - When using an AEAD mode like CCM or EAX, the caller invokes - this function at the very end. - - This method returns the MAC that shall be sent to the receiver, - together with the ciphertext. - - :Return: the MAC, as a byte string. - """ - - if self.mode not in (MODE_CCM, MODE_EAX, MODE_SIV, MODE_GCM): - raise TypeError("digest() not supported by this mode of operation") - - if self.digest not in self._next: - raise TypeError("digest() cannot be called when decrypting or validating a message") - self._next = [self.digest] - - return self._compute_mac() - - def _compute_mac(self): - """Compute MAC without any FSM checks.""" - - if self._tag: - return self._tag - - if self.mode == MODE_CCM: - - if self._assoc_len is None: - self._start_ccm(assoc_len=self._cipherMAC.get_len()) - if self._msg_len is None: - self._start_ccm(msg_len=0) - self._cipherMAC.zero_pad() - self._tag = strxor(self._cipherMAC.digest(), - self._s_0)[:self._mac_len] - - if self.mode == MODE_GCM: - - # Step 5 in NIST SP 800-38D, Algorithm 4 - Compute S - self._cipherMAC.zero_pad() - auth_len = self._cipherMAC.get_len() - self._msg_len - for tlen in (auth_len, self._msg_len): - self._cipherMAC.update(long_to_bytes(8 * tlen, 8)) - s_tag = self._cipherMAC.digest() - - # Step 6 - Compute T - self._tag = self._tag_cipher.encrypt(s_tag)[:self._mac_len] - - if self.mode == MODE_EAX: - tag = bchr(0) * self.block_size - for i in xrange(3): - tag = strxor(tag, self._omac[i].digest()) - self._tag = tag[:self._mac_len] - - if self.mode == MODE_SIV: - self._tag = self._cipherMAC.derive() - - return self._tag - - def hexdigest(self): - """Compute the *printable* MAC tag in an AEAD mode. - - This method is like `digest`. - - :Return: the MAC, as a hexadecimal string. - """ - return "".join(["%02x" % bord(x) for x in self.digest()]) - - def verify(self, mac_tag): - """Validate the *binary* MAC tag in an AEAD mode. - - When using an AEAD mode like CCM or EAX, the caller invokes - this function at the very end. - - This method checks if the decrypted message is indeed valid - (that is, if the key is correct) and it has not been - tampered with while in transit. - - :Parameters: - mac_tag : byte string - This is the *binary* MAC, as received from the sender. - :Raises ValueError: - if the MAC does not match. The message has been tampered with - or the key is incorrect. - """ - - if self.mode not in (MODE_CCM, MODE_EAX, MODE_SIV, MODE_GCM): - raise TypeError("verify() not supported by this mode of operation") - - if self.verify not in self._next: - raise TypeError("verify() cannot be called when encrypting a message") - self._next = [self.verify] - - res = 0 - # Constant-time comparison - for x, y in zip(self._compute_mac(), mac_tag): - res |= bord(x) ^ bord(y) - if res or len(mac_tag) != self._mac_len: - raise ValueError("MAC check failed") - - def hexverify(self, hex_mac_tag): - """Validate the *printable* MAC tag in an AEAD mode. - - This method is like `verify`. - - :Parameters: - hex_mac_tag : string - This is the *printable* MAC, as received from the sender. - :Raises ValueError: - if the MAC does not match. The message has been tampered with - or the key is incorrect. - """ - - self.verify(unhexlify(hex_mac_tag)) - - def encrypt_and_digest(self, plaintext): - """Perform encrypt() and digest() in one step. - - :Parameters: - plaintext : byte string - The piece of data to encrypt. - :Return: - a tuple with two byte strings: - - - the encrypted data - - the MAC - """ - - return self.encrypt(plaintext), self.digest() - - def decrypt_and_verify(self, ciphertext, mac_tag): - """Perform decrypt() and verify() in one step. - - :Parameters: - ciphertext : byte string - The piece of data to decrypt. - mac_tag : byte string - This is the *binary* MAC, as received from the sender. - - :Return: the decrypted data (byte string). - :Raises ValueError: - if the MAC does not match. The message has been tampered with - or the key is incorrect. - """ - - if self.mode == MODE_SIV: - if self.decrypt not in self._next: - raise TypeError("decrypt() can only be called" - " after initialization or an update()") - self._next = [self.verify] - - # Take the MAC and start the cipher for decryption - self._mac = mac_tag - self._cipher = self._siv_ctr_cipher(self._mac) - - pt = self._cipher.decrypt(ciphertext) - - if self.nonce: - self._cipherMAC.update(self.nonce) - if pt: - self._cipherMAC.update(pt) - else: - pt = self.decrypt(ciphertext) - - self.verify(mac_tag) - return pt diff --git a/modules/Crypto/Hash/HMAC.py b/modules/Crypto/Hash/HMAC.py index 8865411..6244db4 100644 --- a/modules/Crypto/Hash/HMAC.py +++ b/modules/Crypto/Hash/HMAC.py @@ -43,7 +43,7 @@ The strength of an HMAC depends on: - the strength of the hash algorithm - the length and entropy of the secret key -This is an example showing how to *create* a MAC: +An example of possible usage is the following: >>> from Crypto.Hash import HMAC >>> @@ -52,22 +52,6 @@ This is an example showing how to *create* a MAC: >>> h.update(b'Hello') >>> print h.hexdigest() -This is an example showing how to *check* a MAC: - - >>> from Crypto.Hash import HMAC - >>> - >>> # We have received a message 'msg' together - >>> # with its MAC 'mac' - >>> - >>> secret = b'Swordfish' - >>> h = HMAC.new(secret) - >>> h.update(msg) - >>> try: - >>> h.verify(mac) - >>> print "The message '%s' is authentic" % msg - >>> except ValueError: - >>> print "The message or the key is wrong" - .. _RFC2104: http://www.ietf.org/rfc/rfc2104.txt .. _FIPS-198: http://csrc.nist.gov/publications/fips/fips198/fips-198a.pdf """ @@ -79,8 +63,6 @@ __revision__ = "$Id$" __all__ = ['new', 'digest_size', 'HMAC' ] -from binascii import unhexlify - from Crypto.Util.strxor import strxor_c from Crypto.Util.py3compat import * @@ -189,32 +171,12 @@ class HMAC: You can continue updating the object after calling this function. :Return: A byte string of `digest_size` bytes. It may contain non-ASCII - characters, including null bytes. + characters, including null bytes. """ - h = self.outer.copy() h.update(self.inner.digest()) return h.digest() - def verify(self, mac_tag): - """Verify that a given **binary** MAC (computed by another party) is valid. - - :Parameters: - mac_tag : byte string - The expected MAC of the message. - :Raises ValueError: - if the MAC does not match. It means that the message - has been tampered with or that the MAC key is incorrect. - """ - - mac = self.digest() - res = 0 - # Constant-time comparison - for x,y in zip(mac, mac_tag): - res |= bord(x) ^ bord(y) - if res or len(mac_tag)!=self.digest_size: - raise ValueError("MAC check failed") - def hexdigest(self): """Return the **printable** MAC of the message that has been authenticated so far. @@ -227,19 +189,6 @@ class HMAC: return "".join(["%02x" % bord(x) for x in tuple(self.digest())]) - def hexverify(self, hex_mac_tag): - """Verify that a given **printable** MAC (computed by another party) is valid. - - :Parameters: - hex_mac_tag : string - The expected MAC of the message, as a hexadecimal string. - :Raises ValueError: - if the MAC does not match. It means that the message - has been tampered with or that the MAC key is incorrect. - """ - - self.verify(unhexlify(tobytes(hex_mac_tag))) - def new(key, msg = None, digestmod = None): """Create a new HMAC object. diff --git a/modules/Crypto/Hash/MD2.py b/modules/Crypto/Hash/MD2.py new file mode 100644 index 0000000..dac959e --- /dev/null +++ b/modules/Crypto/Hash/MD2.py @@ -0,0 +1,91 @@ +# -*- coding: utf-8 -*- +# +# =================================================================== +# 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. +# =================================================================== + +"""MD2 cryptographic hash algorithm. + +MD2 is specified in RFC1319_ and it produces the 128 bit digest of a message. + + >>> from Crypto.Hash import MD2 + >>> + >>> h = MD2.new() + >>> h.update(b'Hello') + >>> print h.hexdigest() + +MD2 stand for Message Digest version 2, and it was invented by Rivest in 1989. + +This algorithm is both slow and insecure. Do not use it for new designs. + +.. _RFC1319: http://tools.ietf.org/html/rfc1319 +""" + +_revision__ = "$Id$" + +__all__ = ['new', 'digest_size', 'MD2Hash' ] + +from Crypto.Util.py3compat import * +from Crypto.Hash.hashalgo import HashAlgo + +import Crypto.Hash._MD2 as _MD2 +hashFactory = _MD2 + +class MD2Hash(HashAlgo): + """Class that implements an MD2 hash + + :undocumented: block_size + """ + + #: ASN.1 Object identifier (OID):: + #: + #: id-md2 OBJECT IDENTIFIER ::= { + #: iso(1) member-body(2) us(840) rsadsi(113549) + #: digestAlgorithm(2) 2 + #: } + #: + #: This value uniquely identifies the MD2 algorithm. + oid = b('\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x02') + + digest_size = 16 + block_size = 16 + + def __init__(self, data=None): + HashAlgo.__init__(self, hashFactory, data) + + def new(self, data=None): + return MD2Hash(data) + +def new(data=None): + """Return a fresh instance of the hash object. + + :Parameters: + data : byte string + The very first chunk of the message to hash. + It is equivalent to an early call to `MD2Hash.update()`. + Optional. + + :Return: An `MD2Hash` object + """ + return MD2Hash().new(data) + +#: The size of the resulting hash in bytes. +digest_size = MD2Hash.digest_size + +#: The internal block size of the hash algorithm in bytes. +block_size = MD2Hash.block_size + diff --git a/modules/Crypto/Hash/MD4.py b/modules/Crypto/Hash/MD4.py new file mode 100644 index 0000000..e28a201 --- /dev/null +++ b/modules/Crypto/Hash/MD4.py @@ -0,0 +1,91 @@ +# -*- coding: utf-8 -*- +# +# =================================================================== +# 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. +# =================================================================== + +"""MD4 cryptographic hash algorithm. + +MD4 is specified in RFC1320_ and produces the 128 bit digest of a message. + + >>> from Crypto.Hash import MD4 + >>> + >>> h = MD4.new() + >>> h.update(b'Hello') + >>> print h.hexdigest() + +MD4 stand for Message Digest version 4, and it was invented by Rivest in 1990. + +This algorithm is insecure. Do not use it for new designs. + +.. _RFC1320: http://tools.ietf.org/html/rfc1320 +""" + +_revision__ = "$Id$" + +__all__ = ['new', 'digest_size', 'MD4Hash' ] + +from Crypto.Util.py3compat import * +from Crypto.Hash.hashalgo import HashAlgo + +import Crypto.Hash._MD4 as _MD4 +hashFactory = _MD4 + +class MD4Hash(HashAlgo): + """Class that implements an MD4 hash + + :undocumented: block_size + """ + + #: ASN.1 Object identifier (OID):: + #: + #: id-md2 OBJECT IDENTIFIER ::= { + #: iso(1) member-body(2) us(840) rsadsi(113549) + #: digestAlgorithm(2) 4 + #: } + #: + #: This value uniquely identifies the MD4 algorithm. + oid = b('\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x04') + + digest_size = 16 + block_size = 64 + + def __init__(self, data=None): + HashAlgo.__init__(self, hashFactory, data) + + def new(self, data=None): + return MD4Hash(data) + +def new(data=None): + """Return a fresh instance of the hash object. + + :Parameters: + data : byte string + The very first chunk of the message to hash. + It is equivalent to an early call to `MD4Hash.update()`. + Optional. + + :Return: A `MD4Hash` object + """ + return MD4Hash().new(data) + +#: The size of the resulting hash in bytes. +digest_size = MD4Hash.digest_size + +#: The internal block size of the hash algorithm in bytes. +block_size = MD4Hash.block_size + diff --git a/modules/Crypto/Hash/MD5.py b/modules/Crypto/Hash/MD5.py index c5df793..18e9e7b 100644 --- a/modules/Crypto/Hash/MD5.py +++ b/modules/Crypto/Hash/MD5.py @@ -35,58 +35,63 @@ This algorithm is insecure. Do not use it for new designs. .. _RFC1321: http://tools.ietf.org/html/rfc1321 """ -from __future__ import nested_scopes - _revision__ = "$Id$" -__all__ = ['new', 'block_size', 'digest_size'] +__all__ = ['new', 'digest_size', 'MD5Hash' ] from Crypto.Util.py3compat import * -if sys.version_info[0] == 2 and sys.version_info[1] == 1: - from Crypto.Util.py21compat import * +from Crypto.Hash.hashalgo import HashAlgo -def __make_constructor(): - try: - # The md5 module is deprecated in Python 2.6, so use hashlib when possible. - from hashlib import md5 as _hash_new - except ImportError: - from md5 import new as _hash_new +try: + # The md5 module is deprecated in Python 2.6, so use hashlib when possible. + import hashlib + hashFactory = hashlib.md5 - h = _hash_new() - if hasattr(h, 'new') and hasattr(h, 'name') and hasattr(h, 'digest_size') and hasattr(h, 'block_size'): - # The module from stdlib has the API that we need. Just use it. - return _hash_new - else: - # Wrap the hash object in something that gives us the expected API. - _copy_sentinel = object() - class _MD5(object): - digest_size = 16 - block_size = 64 - name = "md5" - def __init__(self, *args): - if args and args[0] is _copy_sentinel: - self._h = args[1] - else: - self._h = _hash_new(*args) - def copy(self): - return _MD5(_copy_sentinel, self._h.copy()) - def update(self, *args): - f = self.update = self._h.update - f(*args) - def digest(self): - f = self.digest = self._h.digest - return f() - def hexdigest(self): - f = self.hexdigest = self._h.hexdigest - return f() - _MD5.new = _MD5 - return _MD5 +except ImportError: + import md5 + hashFactory = md5 -new = __make_constructor() -del __make_constructor +class MD5Hash(HashAlgo): + """Class that implements an MD5 hash + + :undocumented: block_size + """ + + #: ASN.1 Object identifier (OID):: + #: + #: id-md5 OBJECT IDENTIFIER ::= { + #: iso(1) member-body(2) us(840) rsadsi(113549) + #: digestAlgorithm(2) 5 + #: } + #: + #: This value uniquely identifies the MD5 algorithm. + oid = b('\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x05') + + digest_size = 16 + block_size = 64 + + def __init__(self, data=None): + HashAlgo.__init__(self, hashFactory, data) + + def new(self, data=None): + return MD5Hash(data) + +def new(data=None): + """Return a fresh instance of the hash object. + + :Parameters: + data : byte string + The very first chunk of the message to hash. + It is equivalent to an early call to `MD5Hash.update()`. + Optional. + + :Return: A `MD5Hash` object + """ + return MD5Hash().new(data) #: The size of the resulting hash in bytes. -digest_size = new().digest_size +digest_size = MD5Hash.digest_size #: The internal block size of the hash algorithm in bytes. -block_size = new().block_size +block_size = MD5Hash.block_size + diff --git a/modules/Crypto/Hash/RIPEMD.py b/modules/Crypto/Hash/RIPEMD.py index 4e80235..33099cb 100644 --- a/modules/Crypto/Hash/RIPEMD.py +++ b/modules/Crypto/Hash/RIPEMD.py @@ -18,9 +18,77 @@ # SOFTWARE. # =================================================================== -# This file exists for backward compatibility with old code that refers to -# Crypto.Hash.RIPEMD +"""RIPEMD-160 cryptographic hash algorithm. -"""Deprecated alias for `Crypto.Hash.RIPEMD160`""" +RIPEMD-160_ produces the 160 bit digest of a message. + + >>> from Crypto.Hash import RIPEMD + >>> + >>> h = RIPEMD.new() + >>> h.update(b'Hello') + >>> print h.hexdigest() + +RIPEMD-160 stands for RACE Integrity Primitives Evaluation Message Digest +with a 160 bit digest. It was invented by Dobbertin, Bosselaers, and Preneel. + +This algorithm is considered secure, although it has not been scrutinized as +extensively as SHA-1. Moreover, it provides an informal security level of just +80bits. + +.. _RIPEMD-160: http://homes.esat.kuleuven.be/~bosselae/ripemd160.html +""" + +_revision__ = "$Id$" + +__all__ = ['new', 'digest_size', 'RIPEMD160Hash' ] + +from Crypto.Util.py3compat import * +from Crypto.Hash.hashalgo import HashAlgo + +import Crypto.Hash._RIPEMD160 as _RIPEMD160 +hashFactory = _RIPEMD160 + +class RIPEMD160Hash(HashAlgo): + """Class that implements a RIPMD-160 hash + + :undocumented: block_size + """ + + #: ASN.1 Object identifier (OID):: + #: + #: id-ripemd160 OBJECT IDENTIFIER ::= { + #: iso(1) identified-organization(3) teletrust(36) + #: algorithm(3) hashAlgorithm(2) ripemd160(1) + #: } + #: + #: This value uniquely identifies the RIPMD-160 algorithm. + oid = b("\x06\x05\x2b\x24\x03\x02\x01") + + digest_size = 20 + block_size = 64 + + def __init__(self, data=None): + HashAlgo.__init__(self, hashFactory, data) + + def new(self, data=None): + return RIPEMD160Hash(data) + +def new(data=None): + """Return a fresh instance of the hash object. + + :Parameters: + data : byte string + The very first chunk of the message to hash. + It is equivalent to an early call to `RIPEMD160Hash.update()`. + Optional. + + :Return: A `RIPEMD160Hash` object + """ + return RIPEMD160Hash().new(data) + +#: The size of the resulting hash in bytes. +digest_size = RIPEMD160Hash.digest_size + +#: The internal block size of the hash algorithm in bytes. +block_size = RIPEMD160Hash.block_size -from Crypto.Hash.RIPEMD160 import new, block_size, digest_size diff --git a/modules/Crypto/Hash/SHA.py b/modules/Crypto/Hash/SHA.py index 0cc141c..0bc5917 100644 --- a/modules/Crypto/Hash/SHA.py +++ b/modules/Crypto/Hash/SHA.py @@ -18,7 +18,81 @@ # SOFTWARE. # =================================================================== -# This file exists for backward compatibility with old code that refers to -# Crypto.Hash.SHA +"""SHA-1 cryptographic hash algorithm. + +SHA-1_ produces the 160 bit digest of a message. + + >>> from Crypto.Hash import SHA + >>> + >>> h = SHA.new() + >>> h.update(b'Hello') + >>> print h.hexdigest() + +*SHA* stands for Secure Hash Algorithm. + +This algorithm is not considered secure. Do not use it for new designs. + +.. _SHA-1: http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf +""" + +_revision__ = "$Id$" + +__all__ = ['new', 'digest_size', 'SHA1Hash' ] + +from Crypto.Util.py3compat import * +from Crypto.Hash.hashalgo import HashAlgo + +try: + # The sha module is deprecated in Python 2.6, so use hashlib when possible. + import hashlib + hashFactory = hashlib.sha1 + +except ImportError: + import sha + hashFactory = sha + +class SHA1Hash(HashAlgo): + """Class that implements a SHA-1 hash + + :undocumented: block_size + """ + + #: ASN.1 Object identifier (OID):: + #: + #: id-sha1 OBJECT IDENTIFIER ::= { + #: iso(1) identified-organization(3) oiw(14) secsig(3) + #: algorithms(2) 26 + #: } + #: + #: This value uniquely identifies the SHA-1 algorithm. + oid = b('\x06\x05\x2b\x0e\x03\x02\x1a') + + digest_size = 20 + block_size = 64 + + def __init__(self, data=None): + HashAlgo.__init__(self, hashFactory, data) + + def new(self, data=None): + return SHA1Hash(data) + +def new(data=None): + """Return a fresh instance of the hash object. + + :Parameters: + data : byte string + The very first chunk of the message to hash. + It is equivalent to an early call to `SHA1Hash.update()`. + Optional. + + :Return: A `SHA1Hash` object + """ + return SHA1Hash().new(data) + +#: The size of the resulting hash in bytes. +digest_size = SHA1Hash.digest_size + +#: The internal block size of the hash algorithm in bytes. +block_size = SHA1Hash.block_size + -from Crypto.Hash.SHA1 import __doc__, new, block_size, digest_size diff --git a/modules/Crypto/Hash/SHA224.py b/modules/Crypto/Hash/SHA224.py new file mode 100644 index 0000000..959b56d --- /dev/null +++ b/modules/Crypto/Hash/SHA224.py @@ -0,0 +1,95 @@ +# -*- coding: utf-8 -*- +# +# =================================================================== +# 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. +# =================================================================== + +"""SHA-224 cryptographic hash algorithm. + +SHA-224 belongs to the SHA-2_ family of cryptographic hashes. +It produces the 224 bit digest of a message. + + >>> from Crypto.Hash import SHA224 + >>> + >>> h = SHA224.new() + >>> h.update(b'Hello') + >>> print h.hexdigest() + +*SHA* stands for Secure Hash Algorithm. + +.. _SHA-2: http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf +""" + +_revision__ = "$Id$" + +__all__ = ['new', 'digest_size', 'SHA224Hash' ] + +from Crypto.Util.py3compat import * +from Crypto.Hash.hashalgo import HashAlgo + +try: + import hashlib + hashFactory = hashlib.sha224 + +except ImportError: + from Crypto.Hash import _SHA224 + hashFactory = _SHA224 + +class SHA224Hash(HashAlgo): + """Class that implements a SHA-224 hash + + :undocumented: block_size + """ + + #: ASN.1 Object identifier (OID):: + #: + #: id-sha224 OBJECT IDENTIFIER ::= { + #: joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101) csor(3) + #: nistalgorithm(4) hashalgs(2) 4 + #: } + #: + #: This value uniquely identifies the SHA-224 algorithm. + oid = b('\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x04') + + digest_size = 28 + block_size = 64 + + def __init__(self, data=None): + HashAlgo.__init__(self, hashFactory, data) + + def new(self, data=None): + return SHA224Hash(data) + +def new(data=None): + """Return a fresh instance of the hash object. + + :Parameters: + data : byte string + The very first chunk of the message to hash. + It is equivalent to an early call to `SHA224Hash.update()`. + Optional. + + :Return: A `SHA224Hash` object + """ + return SHA224Hash().new(data) + +#: The size of the resulting hash in bytes. +digest_size = SHA224Hash.digest_size + +#: The internal block size of the hash algorithm in bytes. +block_size = SHA224Hash.block_size + diff --git a/modules/Crypto/Hash/SHA256.py b/modules/Crypto/Hash/SHA256.py new file mode 100644 index 0000000..b0a99b3 --- /dev/null +++ b/modules/Crypto/Hash/SHA256.py @@ -0,0 +1,95 @@ +# -*- coding: utf-8 -*- +# +# =================================================================== +# 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. +# =================================================================== + +"""SHA-256 cryptographic hash algorithm. + +SHA-256 belongs to the SHA-2_ family of cryptographic hashes. +It produces the 256 bit digest of a message. + + >>> from Crypto.Hash import SHA256 + >>> + >>> h = SHA256.new() + >>> h.update(b'Hello') + >>> print h.hexdigest() + +*SHA* stands for Secure Hash Algorithm. + +.. _SHA-2: http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf +""" + +_revision__ = "$Id$" + +__all__ = ['new', 'digest_size', 'SHA256Hash' ] + +from Crypto.Util.py3compat import * +from Crypto.Hash.hashalgo import HashAlgo + +try: + import hashlib + hashFactory = hashlib.sha256 + +except ImportError: + from Crypto.Hash import _SHA256 + hashFactory = _SHA256 + +class SHA256Hash(HashAlgo): + """Class that implements a SHA-256 hash + + :undocumented: block_size + """ + + #: ASN.1 Object identifier (OID):: + #: + #: id-sha256 OBJECT IDENTIFIER ::= { + #: joint-iso-itu-t(2) country(16) us(840) organization(1) + #: gov(101) csor(3) nistalgorithm(4) hashalgs(2) 1 + #: } + #: + #: This value uniquely identifies the SHA-256 algorithm. + oid = b('\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01') + + digest_size = 32 + block_size = 64 + + def __init__(self, data=None): + HashAlgo.__init__(self, hashFactory, data) + + def new(self, data=None): + return SHA256Hash(data) + +def new(data=None): + """Return a fresh instance of the hash object. + + :Parameters: + data : byte string + The very first chunk of the message to hash. + It is equivalent to an early call to `SHA256Hash.update()`. + Optional. + + :Return: A `SHA256Hash` object + """ + return SHA256Hash().new(data) + +#: The size of the resulting hash in bytes. +digest_size = SHA256Hash.digest_size + +#: The internal block size of the hash algorithm in bytes. +block_size = SHA256Hash.block_size + diff --git a/modules/Crypto/Hash/SHA384.py b/modules/Crypto/Hash/SHA384.py new file mode 100644 index 0000000..3490b02 --- /dev/null +++ b/modules/Crypto/Hash/SHA384.py @@ -0,0 +1,96 @@ +# -*- coding: utf-8 -*- +# +# =================================================================== +# 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. +# =================================================================== + +"""SHA-384 cryptographic hash algorithm. + +SHA-384 belongs to the SHA-2_ family of cryptographic hashes. +It produces the 384 bit digest of a message. + + >>> from Crypto.Hash import SHA384 + >>> + >>> h = SHA384.new() + >>> h.update(b'Hello') + >>> print h.hexdigest() + +*SHA* stands for Secure Hash Algorithm. + +.. _SHA-2: http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf +""" + +_revision__ = "$Id$" + +__all__ = ['new', 'digest_size', 'SHA384Hash' ] + +from Crypto.Util.py3compat import * +from Crypto.Hash.hashalgo import HashAlgo + +try: + import hashlib + hashFactory = hashlib.sha384 + +except ImportError: + from Crypto.Hash import _SHA384 + hashFactory = _SHA384 + +class SHA384Hash(HashAlgo): + """Class that implements a SHA-384 hash + + :undocumented: block_size + """ + + #: ASN.1 Object identifier (OID):: + #: + #: id-sha384 OBJECT IDENTIFIER ::= { + #: joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101) csor(3) + #: nistalgorithm(4) hashalgs(2) 2 + #: } + #: + #: This value uniquely identifies the SHA-384 algorithm. + oid = b('\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02') + + digest_size = 48 + block_size = 128 + + def __init__(self, data=None): + HashAlgo.__init__(self, hashFactory, data) + + def new(self, data=None): + return SHA384Hash(data) + +def new(data=None): + """Return a fresh instance of the hash object. + + :Parameters: + data : byte string + The very first chunk of the message to hash. + It is equivalent to an early call to `SHA384Hash.update()`. + Optional. + + :Return: A `SHA384Hash` object + """ + return SHA384Hash().new(data) + +#: The size of the resulting hash in bytes. +digest_size = SHA384Hash.digest_size + +#: The internal block size of the hash algorithm in bytes. +block_size = SHA384Hash.block_size + + diff --git a/modules/Crypto/Hash/SHA512.py b/modules/Crypto/Hash/SHA512.py new file mode 100644 index 0000000..d57548d --- /dev/null +++ b/modules/Crypto/Hash/SHA512.py @@ -0,0 +1,95 @@ +# -*- coding: utf-8 -*- +# +# =================================================================== +# 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. +# =================================================================== + +"""SHA-512 cryptographic hash algorithm. + +SHA-512 belongs to the SHA-2_ family of cryptographic hashes. +It produces the 512 bit digest of a message. + + >>> from Crypto.Hash import SHA512 + >>> + >>> h = SHA512.new() + >>> h.update(b'Hello') + >>> print h.hexdigest() + +*SHA* stands for Secure Hash Algorithm. + +.. _SHA-2: http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf +""" + +_revision__ = "$Id$" + +__all__ = ['new', 'digest_size', 'SHA512Hash' ] + +from Crypto.Util.py3compat import * +from Crypto.Hash.hashalgo import HashAlgo + +try: + import hashlib + hashFactory = hashlib.sha512 + +except ImportError: + from Crypto.Hash import _SHA512 + hashFactory = _SHA512 + +class SHA512Hash(HashAlgo): + """Class that implements a SHA-512 hash + + :undocumented: block_size + """ + + #: ASN.1 Object identifier (OID):: + #: + #: id-sha512 OBJECT IDENTIFIER ::= { + #: joint-iso-itu-t(2) + #: country(16) us(840) organization(1) gov(101) csor(3) nistalgorithm(4) hashalgs(2) 3 + #: } + #: + #: This value uniquely identifies the SHA-512 algorithm. + oid = b('\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03') + + digest_size = 64 + block_size = 128 + + def __init__(self, data=None): + HashAlgo.__init__(self, hashFactory, data) + + def new(self, data=None): + return SHA512Hash(data) + +def new(data=None): + """Return a fresh instance of the hash object. + + :Parameters: + data : byte string + The very first chunk of the message to hash. + It is equivalent to an early call to `SHA512Hash.update()`. + Optional. + + :Return: A `SHA512Hash` object + """ + return SHA512Hash().new(data) + +#: The size of the resulting hash in bytes. +digest_size = SHA512Hash.digest_size + +#: The internal block size of the hash algorithm in bytes. +block_size = SHA512Hash.block_size + diff --git a/modules/Crypto/Hash/_MD2.so b/modules/Crypto/Hash/_MD2.so new file mode 100755 index 0000000..8d5252b Binary files /dev/null and b/modules/Crypto/Hash/_MD2.so differ diff --git a/modules/Crypto/Hash/_MD4.so b/modules/Crypto/Hash/_MD4.so new file mode 100755 index 0000000..87e57bf Binary files /dev/null and b/modules/Crypto/Hash/_MD4.so differ diff --git a/modules/Crypto/Hash/_RIPEMD160.so b/modules/Crypto/Hash/_RIPEMD160.so new file mode 100755 index 0000000..12b0ce0 Binary files /dev/null and b/modules/Crypto/Hash/_RIPEMD160.so differ diff --git a/modules/Crypto/Hash/_SHA224.so b/modules/Crypto/Hash/_SHA224.so new file mode 100755 index 0000000..60650bc Binary files /dev/null and b/modules/Crypto/Hash/_SHA224.so differ diff --git a/modules/Crypto/Hash/_SHA256.so b/modules/Crypto/Hash/_SHA256.so new file mode 100755 index 0000000..abf871e Binary files /dev/null and b/modules/Crypto/Hash/_SHA256.so differ diff --git a/modules/Crypto/Hash/_SHA384.so b/modules/Crypto/Hash/_SHA384.so new file mode 100755 index 0000000..49bef7d Binary files /dev/null and b/modules/Crypto/Hash/_SHA384.so differ diff --git a/modules/Crypto/Hash/_SHA512.so b/modules/Crypto/Hash/_SHA512.so new file mode 100755 index 0000000..f600f07 Binary files /dev/null and b/modules/Crypto/Hash/_SHA512.so differ diff --git a/modules/Crypto/Hash/__init__.py b/modules/Crypto/Hash/__init__.py index f16e253..4582c66 100644 --- a/modules/Crypto/Hash/__init__.py +++ b/modules/Crypto/Hash/__init__.py @@ -49,128 +49,8 @@ The hashing modules here all support the interface described in `PEP :undocumented: _MD2, _MD4, _RIPEMD160, _SHA224, _SHA256, _SHA384, _SHA512 """ -__all__ = ['HMAC', 'MD2', 'MD4', 'MD5', 'RIPEMD160', 'SHA1', - 'SHA224', 'SHA256', 'SHA384', 'SHA512', 'CMAC'] - +__all__ = ['HMAC', 'MD2', 'MD4', 'MD5', 'RIPEMD', 'SHA', + 'SHA224', 'SHA256', 'SHA384', 'SHA512'] __revision__ = "$Id$" -import sys -if sys.version_info[0] == 2 and sys.version_info[1] == 1: - from Crypto.Util.py21compat import * -from Crypto.Util.py3compat import * -def new(algo, *args): - """Initialize a new hash object. - - The first argument to this function may be an algorithm name or another - hash object. - - This function has significant overhead. It's recommended that you instead - import and use the individual hash modules directly. - """ - - # Try just invoking algo.new() - # We do this first so that this is the fastest. - try: - new_func = algo.new - except AttributeError: - pass - else: - return new_func(*args) - - # Try getting the algorithm name. - if isinstance(algo, str): - name = algo - else: - try: - name = algo.name - except AttributeError: - raise ValueError("unsupported hash type %r" % (algo,)) - - # Got the name. Let's see if we have a PyCrypto implementation. - try: - new_func = _new_funcs[name] - except KeyError: - # No PyCrypto implementation. Try hashlib. - try: - import hashlib - except ImportError: - # There is no hashlib. - raise ValueError("unsupported hash type %s" % (name,)) - return hashlib.new(name, *args) - else: - # We have a PyCrypto implementation. Instantiate it. - return new_func(*args) - -# This dict originally gets the following _*_new methods, but its members get -# replaced with the real new() methods of the various hash modules as they are -# used. We do it without locks to improve performance, which is safe in -# CPython because dict access is atomic in CPython. This might break PyPI. -_new_funcs = {} - -def _md2_new(*args): - from Crypto.Hash import MD2 - _new_funcs['MD2'] = _new_funcs['md2'] = MD2.new - return MD2.new(*args) -_new_funcs['MD2'] = _new_funcs['md2'] = _md2_new -del _md2_new - -def _md4_new(*args): - from Crypto.Hash import MD4 - _new_funcs['MD4'] = _new_funcs['md4'] = MD4.new - return MD4.new(*args) -_new_funcs['MD4'] = _new_funcs['md4'] = _md4_new -del _md4_new - -def _md5_new(*args): - from Crypto.Hash import MD5 - _new_funcs['MD5'] = _new_funcs['md5'] = MD5.new - return MD5.new(*args) -_new_funcs['MD5'] = _new_funcs['md5'] = _md5_new -del _md5_new - -def _ripemd160_new(*args): - from Crypto.Hash import RIPEMD160 - _new_funcs['RIPEMD160'] = _new_funcs['ripemd160'] = \ - _new_funcs['RIPEMD'] = _new_funcs['ripemd'] = RIPEMD160.new - return RIPEMD160.new(*args) -_new_funcs['RIPEMD160'] = _new_funcs['ripemd160'] = \ - _new_funcs['RIPEMD'] = _new_funcs['ripemd'] = _ripemd160_new -del _ripemd160_new - -def _sha1_new(*args): - from Crypto.Hash import SHA1 - _new_funcs['SHA1'] = _new_funcs['sha1'] = \ - _new_funcs['SHA'] = _new_funcs['sha'] = SHA1.new - return SHA1.new(*args) -_new_funcs['SHA1'] = _new_funcs['sha1'] = \ - _new_funcs['SHA'] = _new_funcs['sha'] = _sha1_new -del _sha1_new - -def _sha224_new(*args): - from Crypto.Hash import SHA224 - _new_funcs['SHA224'] = _new_funcs['sha224'] = SHA224.new - return SHA224.new(*args) -_new_funcs['SHA224'] = _new_funcs['sha224'] = _sha224_new -del _sha224_new - -def _sha256_new(*args): - from Crypto.Hash import SHA256 - _new_funcs['SHA256'] = _new_funcs['sha256'] = SHA256.new - return SHA256.new(*args) -_new_funcs['SHA256'] = _new_funcs['sha256'] = _sha256_new -del _sha256_new - -def _sha384_new(*args): - from Crypto.Hash import SHA384 - _new_funcs['SHA384'] = _new_funcs['sha384'] = SHA384.new - return SHA384.new(*args) -_new_funcs['SHA384'] = _new_funcs['sha384'] = _sha384_new -del _sha384_new - -def _sha512_new(*args): - from Crypto.Hash import SHA512 - _new_funcs['SHA512'] = _new_funcs['sha512'] = SHA512.new - return SHA512.new(*args) -_new_funcs['SHA512'] = _new_funcs['sha512'] = _sha512_new -del _sha512_new diff --git a/modules/Crypto/Hash/hashalgo.py b/modules/Crypto/Hash/hashalgo.py new file mode 100644 index 0000000..b38b3a6 --- /dev/null +++ b/modules/Crypto/Hash/hashalgo.py @@ -0,0 +1,116 @@ +# -*- coding: utf-8 -*- +# +# =================================================================== +# 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. +# =================================================================== + +from binascii import hexlify + +class HashAlgo: + """A generic class for an abstract cryptographic hash algorithm. + + :undocumented: block_size + """ + + #: The size of the resulting hash in bytes. + digest_size = None + #: The internal block size of the hash algorithm in bytes. + block_size = None + + def __init__(self, hashFactory, data=None): + """Initialize the hash object. + + :Parameters: + hashFactory : callable + An object that will generate the actual hash implementation. + *hashFactory* must have a *new()* method, or must be directly + callable. + data : byte string + The very first chunk of the message to hash. + It is equivalent to an early call to `update()`. + """ + if hasattr(hashFactory, 'new'): + self._hash = hashFactory.new() + else: + self._hash = hashFactory() + if data: + self.update(data) + + def update(self, data): + """Continue hashing of a message by consuming the next chunk of data. + + Repeated calls are equivalent to a single call with the concatenation + of all the arguments. In other words: + + >>> m.update(a); m.update(b) + + is equivalent to: + + >>> m.update(a+b) + + :Parameters: + data : byte string + The next chunk of the message being hashed. + """ + return self._hash.update(data) + + def digest(self): + """Return the **binary** (non-printable) digest of the message that has been hashed so far. + + This method does not change the state of the hash object. + You can continue updating the object after calling this function. + + :Return: A byte string of `digest_size` bytes. It may contain non-ASCII + characters, including null bytes. + """ + return self._hash.digest() + + def hexdigest(self): + """Return the **printable** digest of the message that has been hashed so far. + + This method does not change the state of the hash object. + + :Return: A string of 2* `digest_size` characters. It contains only + hexadecimal ASCII digits. + """ + return self._hash.hexdigest() + + def copy(self): + """Return a copy ("clone") of the hash object. + + The copy will have the same internal state as the original hash + object. + This can be used to efficiently compute the digests of strings that + share a common initial substring. + + :Return: A hash object of the same type + """ + return self._hash.copy() + + def new(self, data=None): + """Return a fresh instance of the hash object. + + Unlike the `copy` method, the internal state of the object is empty. + + :Parameters: + data : byte string + The next chunk of the message being hashed. + + :Return: A hash object of the same type + """ + pass + diff --git a/modules/Crypto/Protocol/KDF.py b/modules/Crypto/Protocol/KDF.py index c5967e4..973b7af 100644 --- a/modules/Crypto/Protocol/KDF.py +++ b/modules/Crypto/Protocol/KDF.py @@ -38,21 +38,16 @@ __revision__ = "$Id$" import math import struct -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.Hash import SHA1, HMAC, CMAC +from Crypto.Hash import SHA as SHA1, HMAC from Crypto.Util.strxor import strxor -from Crypto.Util.number import long_to_bytes, bytes_to_long def PBKDF1(password, salt, dkLen, count=1000, hashAlgo=None): """Derive one key from a password (or passphrase). This function performs key derivation according an old version of the PKCS#5 standard (v1.5). - + This algorithm is called ``PBKDF1``. Even though it is still described in the latest version of the PKCS#5 standard (version 2, or RFC2898), newer applications should use the more secure and versatile `PBKDF2` instead. @@ -81,7 +76,7 @@ def PBKDF1(password, salt, dkLen, count=1000, hashAlgo=None): pHash = hashAlgo.new(password+salt) digest = pHash.digest_size if dkLen>digest: - raise TypeError("Selected hash algorithm has a too short digest (%d bytes)." % digest) + raise ValueError("Selected hash algorithm has a too short digest (%d bytes)." % digest) if len(salt)!=8: raise ValueError("Salt is not 8 bytes long.") for i in xrange(count-1): @@ -126,84 +121,3 @@ def PBKDF2(password, salt, dkLen=16, count=1000, prf=None): i = i + 1 return key[:dkLen] -class _S2V(object): - """String-to-vector PRF as defined in `RFC5297`_. - - This class implements a pseudorandom function family - based on CMAC that takes as input a vector of strings. - - .. _RFC5297: http://tools.ietf.org/html/rfc5297 - """ - - def __init__(self, key, ciphermod): - """Initialize the S2V PRF. - - :Parameters: - key : byte string - A secret that can be used as key for CMACs - based on ciphers from ``ciphermod``. - ciphermod : module - A block cipher module from `Crypto.Cipher`. - """ - - self._key = key - self._ciphermod = ciphermod - self._last_string = self._cache = bchr(0)*ciphermod.block_size - self._n_updates = ciphermod.block_size*8-1 - - def new(key, ciphermod): - """Create a new S2V PRF. - - :Parameters: - key : byte string - A secret that can be used as key for CMACs - based on ciphers from ``ciphermod``. - ciphermod : module - A block cipher module from `Crypto.Cipher`. - """ - return _S2V(key, ciphermod) - new = staticmethod(new) - - def _double(self, bs): - doubled = bytes_to_long(bs)<<1 - if bord(bs[0]) & 0x80: - doubled ^= 0x87 - return long_to_bytes(doubled, len(bs))[-len(bs):] - - def update(self, item): - """Pass the next component of the vector. - - The maximum number of components you can pass is equal to the block - length of the cipher (in bits) minus 1. - - :Parameters: - item : byte string - The next component of the vector. - :Raise TypeError: when the limit on the number of components has been reached. - :Raise ValueError: when the component is empty - """ - - if not item: - raise ValueError("A component cannot be empty") - - if self._n_updates==0: - raise TypeError("Too many components passed to S2V") - self._n_updates -= 1 - - mac = CMAC.new(self._key, msg=self._last_string, ciphermod=self._ciphermod) - self._cache = strxor(self._double(self._cache), mac.digest()) - self._last_string = item - - def derive(self): - """"Derive a secret from the vector of components. - - :Return: a byte string, as long as the block length of the cipher. - """ - - if len(self._last_string)>=16: - final = self._last_string[:-16] + strxor(self._last_string[-16:], self._cache) - else: - padded = (self._last_string + bchr(0x80)+ bchr(0)*15)[:16] - final = strxor(padded, self._double(self._cache)) - mac = CMAC.new(self._key, msg=final, ciphermod=self._ciphermod) - return mac.digest() diff --git a/modules/Crypto/PublicKey/DSA.py b/modules/Crypto/PublicKey/DSA.py index 1818def..d6bffd6 100644 --- a/modules/Crypto/PublicKey/DSA.py +++ b/modules/Crypto/PublicKey/DSA.py @@ -59,20 +59,14 @@ verification. >>> from Crypto.Random import random >>> from Crypto.PublicKey import DSA - >>> from Crypto.Hash import SHA256 + >>> from Crypto.Hash import SHA >>> >>> message = "Hello" - >>> key = DSA.generate(2048) - >>> f = open("public_key.pem", "w") - >>> f.write(key.publickey().exportKey(key)) - >>> h = SHA256.new(message).digest() + >>> key = DSA.generate(1024) + >>> h = SHA.new(message).digest() >>> k = random.StrongRandom().randint(1,key.q-1) >>> sig = key.sign(h,k) >>> ... - >>> ... - >>> f = open("public_key.pem", "r") - >>> h = SHA256.new(message).digest() - >>> key = DSA.importKey(f.read()) >>> if key.verify(h,sig): >>> print "OK" >>> else: @@ -85,64 +79,20 @@ verification. __revision__ = "$Id$" -__all__ = ['generate', 'construct', 'error', 'DSAImplementation', - '_DSAobj', 'importKey'] - -import binascii -import struct +__all__ = ['generate', 'construct', 'error', 'DSAImplementation', '_DSAobj'] 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.PublicKey import _DSA, _slowmath, pubkey from Crypto import Random -from Crypto.IO import PKCS8, PEM -from Crypto.Util.number import bytes_to_long, long_to_bytes -from Crypto.PublicKey import _DSA, _slowmath, pubkey, KeyFormatError -from Crypto.Util.asn1 import DerObject, DerSequence,\ - DerInteger, DerObjectId, DerBitString, newDerSequence, newDerBitString try: from Crypto.PublicKey import _fastmath except ImportError: _fastmath = None -def decode_der(obj_class, binstr): - """Instantiate a DER object class, decode a DER binary string in it, - and return the object.""" - der = obj_class() - der.decode(binstr) - return der - -# ; The following ASN.1 types are relevant for DSA -# -# SubjectPublicKeyInfo ::= SEQUENCE { -# algorithm AlgorithmIdentifier, -# subjectPublicKey BIT STRING -# } -# -# id-dsa ID ::= { iso(1) member-body(2) us(840) x9-57(10040) x9cm(4) 1 } -# -# ; See RFC3279 -# Dss-Parms ::= SEQUENCE { -# p INTEGER, -# q INTEGER, -# g INTEGER -# } -# -# DSAPublicKey ::= INTEGER -# -# DSSPrivatKey_OpenSSL ::= SEQUENCE -# version INTEGER, -# p INTEGER, -# q INTEGER, -# g INTEGER, -# y INTEGER, -# x INTEGER -# } -# - class _DSAobj(pubkey.pubkey): """Class defining an actual DSA key. @@ -162,12 +112,9 @@ class _DSAobj(pubkey.pubkey): #: - **x**, the private key. keydata = ['y', 'g', 'p', 'q', 'x'] - def __init__(self, implementation, key, randfunc=None): + def __init__(self, implementation, key): self.implementation = implementation self.key = key - if randfunc is None: - randfunc = Random.new().read - self._randfunc = randfunc def __getattr__(self, attrname): if attrname in self.keydata: @@ -270,8 +217,6 @@ class _DSAobj(pubkey.pubkey): def __setstate__(self, d): if not hasattr(self, 'implementation'): self.implementation = DSAImplementation() - if not hasattr(self, '_randfunc'): - self._randfunc = Random.new().read t = [] for k in self.keydata: if not d.has_key(k): @@ -291,124 +236,6 @@ class _DSAobj(pubkey.pubkey): # PY3K: This is meant to be text, do not change to bytes (data) return "<%s @0x%x %s>" % (self.__class__.__name__, id(self), ",".join(attrs)) - def exportKey(self, format='PEM', pkcs8=None, passphrase=None, - protection=None): - """Export this DSA key. - - :Parameters: - format : string - The format to use for wrapping the key: - - - *'DER'*. Binary encoding. - - *'PEM'*. Textual encoding, done according to `RFC1421`_/ - `RFC1423`_ (default). - - *'OpenSSH'*. Textual encoding, one line of text, see `RFC4253`_. - Only suitable for public keys, not private keys. - - passphrase : string - For private keys only. The pass phrase to use for deriving - the encryption key. - - pkcs8 : boolean - For private keys only. If ``True`` (default), the key is arranged - according to `PKCS#8`_ and if `False`, according to the custom - OpenSSL/OpenSSH encoding. - - protection : string - The encryption scheme to use for protecting the private key. - It is only meaningful when a pass phrase is present too. - - If ``pkcs8`` takes value ``True``, ``protection`` is the PKCS#8 - algorithm to use for deriving the secret and encrypting - the private DSA key. - For a complete list of algorithms, see `Crypto.IO.PKCS8`. - The default is *PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC*. - - If ``pkcs8`` is ``False``, the obsolete PEM encryption scheme is - used. It is based on MD5 for key derivation, and Triple DES for - encryption. Parameter ``protection`` is ignored. - - The combination ``format='DER'`` and ``pkcs8=False`` is not allowed - if a passphrase is present. - - :Return: A byte string with the encoded public or private half - of the key. - :Raise ValueError: - When the format is unknown or when you try to encrypt a private - key with *DER* format and OpenSSL/OpenSSH. - :attention: - If you don't provide a pass phrase, the private key will be - exported in the clear! - - .. _RFC1421: http://www.ietf.org/rfc/rfc1421.txt - .. _RFC1423: http://www.ietf.org/rfc/rfc1423.txt - .. _RFC4253: http://www.ietf.org/rfc/rfc4253.txt - .. _`PKCS#8`: http://www.ietf.org/rfc/rfc5208.txt - """ - if passphrase is not None: - passphrase = tobytes(passphrase) - if format == 'OpenSSH': - tup1 = [long_to_bytes(x) for x in (self.p, self.q, self.g, self.y)] - - def func(x): - if (bord(x[0]) & 0x80): - return bchr(0) + x - else: - return x - - tup2 = map(func, tup1) - keyparts = [b('ssh-dss')] + tup2 - keystring = b('').join( - [struct.pack(">I", len(kp)) + kp for kp in keyparts] - ) - return b('ssh-dss ') + binascii.b2a_base64(keystring)[:-1] - - # DER format is always used, even in case of PEM, which simply - # encodes it into BASE64. - params = newDerSequence(self.p, self.q, self.g) - if self.has_private(): - if pkcs8 is None: - pkcs8 = True - if pkcs8: - if not protection: - protection = 'PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC' - private_key = DerInteger(self.x).encode() - binary_key = PKCS8.wrap( - private_key, oid, passphrase, - protection, key_params=params, - randfunc=self._randfunc - ) - if passphrase: - key_type = 'ENCRYPTED PRIVATE' - else: - key_type = 'PRIVATE' - passphrase = None - else: - if format != 'PEM' and passphrase: - raise ValueError("DSA private key cannot be encrypted") - ints = [0, self.p, self.q, self.g, self.y, self.x] - binary_key = newDerSequence(*ints).encode() - key_type = "DSA PRIVATE" - else: - if pkcs8: - raise ValueError("PKCS#8 is only meaningful for private keys") - binary_key = newDerSequence( - newDerSequence(DerObjectId(oid), params), - newDerBitString(DerInteger(self.y)) - ).encode() - key_type = "DSA PUBLIC" - - if format == 'DER': - return binary_key - if format == 'PEM': - pem_str = PEM.encode( - binary_key, key_type + " KEY", - passphrase, self._randfunc - ) - return tobytes(pem_str) - raise ValueError("Unknown key format '%s'. Cannot export the DSA key." % format) - - class DSAImplementation(object): """ A DSA key factory. @@ -416,7 +243,7 @@ class DSAImplementation(object): This class is only internally used to implement the methods of the `Crypto.PublicKey.DSA` module. """ - + def __init__(self, **kwargs): """Create a new DSA key factory. @@ -543,139 +370,9 @@ class DSAImplementation(object): key = self._math.dsa_construct(*tup) return _DSAobj(self, key) - def _importKeyDER(self, key_data, passphrase=None, params=None): - """Import a DSA key (public or private half), encoded in DER form.""" - - try: - # - # Dss-Parms ::= SEQUENCE { - # p OCTET STRING, - # q OCTET STRING, - # g OCTET STRING - # } - # - - # Try a simple private key first - if params: - x = decode_der(DerInteger, key_data).value - params = decode_der(DerSequence, params) # Dss-Parms - p, q, g = list(params) - y = pow(g, x, p) - tup = (y, g, p, q, x) - return self.construct(tup) - - der = decode_der(DerSequence, key_data) - - # Try OpenSSL format for private keys - if len(der) == 6 and der.hasOnlyInts() and der[0] == 0: - tup = [der[comp] for comp in (4, 3, 1, 2, 5)] - return self.construct(tup) - - # Try SubjectPublicKeyInfo - if len(der) == 2: - try: - algo = decode_der(DerSequence, der[0]) - algo_oid = decode_der(DerObjectId, algo[0]).value - params = decode_der(DerSequence, algo[1]) # Dss-Parms - - if algo_oid == oid and len(params) == 3 and\ - params.hasOnlyInts(): - bitmap = decode_der(DerBitString, der[1]) - pub_key = decode_der(DerInteger, bitmap.value) - tup = [pub_key.value] - tup += [params[comp] for comp in (2, 0, 1)] - return self.construct(tup) - except (ValueError, EOFError): - pass - - # Try unencrypted PKCS#8 - p8_pair = PKCS8.unwrap(key_data, passphrase) - if p8_pair[0] == oid: - return self._importKeyDER(p8_pair[1], passphrase, p8_pair[2]) - - except (ValueError, EOFError): - pass - - raise KeyFormatError("DSA key format is not supported") - - def importKey(self, extern_key, passphrase=None): - """Import a DSA key (public or private). - - :Parameters: - extern_key : (byte) string - The DSA key to import. - - An DSA *public* key can be in any of the following formats: - - - X.509 ``subjectPublicKeyInfo`` (binary or PEM) - - OpenSSH (one line of text, see `RFC4253`_) - - A DSA *private* key can be in any of the following formats: - - - `PKCS#8`_ ``PrivateKeyInfo`` or ``EncryptedPrivateKeyInfo`` - DER SEQUENCE (binary or PEM encoding) - - OpenSSL/OpenSSH (binary or PEM) - - For details about the PEM encoding, see `RFC1421`_/`RFC1423`_. - - The private key may be encrypted by means of a certain pass phrase - either at the PEM level or at the PKCS#8 level. - - passphrase : string - In case of an encrypted private key, this is the pass phrase - from which the decryption key is derived. - - :Return: A DSA key object (`_DSAobj`). - :Raise KeyFormatError: - When the given key cannot be parsed (possibly because - the pass phrase is wrong). - - .. _RFC1421: http://www.ietf.org/rfc/rfc1421.txt - .. _RFC1423: http://www.ietf.org/rfc/rfc1423.txt - .. _RFC4253: http://www.ietf.org/rfc/rfc4253.txt - .. _PKCS#8: http://www.ietf.org/rfc/rfc5208.txt - """ - - extern_key = tobytes(extern_key) - if passphrase is not None: - passphrase = tobytes(passphrase) - - if extern_key.startswith(b('-----')): - # This is probably a PEM encoded key - (der, marker, enc_flag) = PEM.decode(tostr(extern_key), passphrase) - if enc_flag: - passphrase = None - return self._importKeyDER(der, passphrase) - - if extern_key.startswith(b('ssh-dss ')): - # This is probably a public OpenSSH key - keystring = binascii.a2b_base64(extern_key.split(b(' '))[1]) - keyparts = [] - while len(keystring) > 4: - length = struct.unpack(">I", keystring[:4])[0] - keyparts.append(keystring[4:4 + length]) - keystring = keystring[4 + length:] - if keyparts[0] == b("ssh-dss"): - tup = [bytes_to_long(keyparts[x]) for x in (4, 3, 1, 2)] - return self.construct(tup) - - if bord(extern_key[0]) == 0x30: - # This is probably a DER encoded key - return self._importKeyDER(extern_key, passphrase) - - raise KeyFormatError("DSA key format is not supported") - -#: `Object ID`_ for a DSA key. -#: -#: id-dsa ID ::= { iso(1) member-body(2) us(840) x9-57(10040) x9cm(4) 1 } -#: -#: .. _`Object ID`: http://www.alvestrand.no/objectid/1.2.840.10040.4.1.html -oid = "1.2.840.10040.4.1" - _impl = DSAImplementation() generate = _impl.generate construct = _impl.construct -importKey = _impl.importKey error = _impl.error # vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/modules/Crypto/PublicKey/ElGamal.py b/modules/Crypto/PublicKey/ElGamal.py index 0ab07fc..99af71c 100644 --- a/modules/Crypto/PublicKey/ElGamal.py +++ b/modules/Crypto/PublicKey/ElGamal.py @@ -111,7 +111,6 @@ __all__ = ['generate', 'construct', 'error', 'ElGamalobj'] from Crypto.PublicKey.pubkey import * from Crypto.Util import number -from Crypto import Random class error (Exception): pass @@ -243,11 +242,6 @@ class ElGamalobj(pubkey): #: - **x**, the private key. keydata=['p', 'g', 'y', 'x'] - def __init__(self, randfunc=None): - if randfunc is None: - randfunc = Random.new().read - self._randfunc = randfunc - def encrypt(self, plaintext, K): """Encrypt a piece of data with ElGamal. @@ -337,11 +331,8 @@ class ElGamalobj(pubkey): def _decrypt(self, M): if (not hasattr(self, 'x')): raise TypeError('Private key not available in this object') - r = number.getRandomRange(2, self.p-1, self._randfunc) - a_blind = (M[0] * pow(self.g, r, self.p)) % self.p - ax=pow(a_blind, self.x, self.p) - plaintext_blind = (M[1] * inverse(ax, self.p ) ) % self.p - plaintext = (plaintext_blind * pow(self.y, r, self.p)) % self.p + ax=pow(M[0], self.x, self.p) + plaintext=(M[1] * inverse(ax, self.p ) ) % self.p return plaintext def _sign(self, M, K): diff --git a/modules/Crypto/PublicKey/RSA.py b/modules/Crypto/PublicKey/RSA.py index a5afeb9..99d851d 100644 --- a/modules/Crypto/PublicKey/RSA.py +++ b/modules/Crypto/PublicKey/RSA.py @@ -46,7 +46,7 @@ them from known components, exporting them, and importing them. >>> >>> key = RSA.generate(2048) >>> f = open('mykey.pem','w') - >>> f.write(key.exportKey('PEM')) + >>> f.write(RSA.exportKey('PEM')) >>> f.close() ... >>> f = open('mykey.pem','r') @@ -65,39 +65,31 @@ it is recommended to use one of the standardized schemes instead (like __revision__ = "$Id$" -__all__ = ['generate', 'construct', 'error', 'importKey', 'RSAImplementation', - '_RSAobj', 'oid' , 'algorithmIdentifier' ] +__all__ = ['generate', 'construct', 'error', 'importKey', 'RSAImplementation', '_RSAobj'] 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.python_compat import * from Crypto.Util.number import getRandomRange, bytes_to_long, long_to_bytes from Crypto.PublicKey import _RSA, _slowmath, pubkey -from Crypto.IO import PKCS8, PEM from Crypto import Random -from Crypto.Util.asn1 import * - +from Crypto.Util.asn1 import DerObject, DerSequence, DerNull import binascii import struct from Crypto.Util.number import inverse +from Crypto.Util.number import inverse + try: from Crypto.PublicKey import _fastmath except ImportError: _fastmath = None -def decode_der(obj_class, binstr): - """Instantiate a DER object class, decode a DER binary string in it, and - return the object.""" - der = obj_class() - der.decode(binstr) - return der - class _RSAobj(pubkey.pubkey): """Class defining an actual RSA key. @@ -294,8 +286,6 @@ class _RSAobj(pubkey.pubkey): def __setstate__(self, d): if not hasattr(self, 'implementation'): self.implementation = RSAImplementation() - if not hasattr(self, '_randfunc'): - self._randfunc = Random.new().read t = [] for k in self.keydata: if not d.has_key(k): @@ -315,66 +305,36 @@ class _RSAobj(pubkey.pubkey): # PY3K: This is meant to be text, do not change to bytes (data) return "<%s @0x%x %s>" % (self.__class__.__name__, id(self), ",".join(attrs)) - def exportKey(self, format='PEM', passphrase=None, pkcs=1, protection=None): + def exportKey(self, format='PEM', passphrase=None, pkcs=1): """Export this RSA key. - :Parameters: - format : string - The format to use for wrapping the key: + :Parameter format: The format to use for wrapping the key. - - *'DER'*. Binary encoding. + - *'DER'*. Binary encoding, always unencrypted. - *'PEM'*. Textual encoding, done according to `RFC1421`_/`RFC1423`_. + Unencrypted (default) or encrypted. - *'OpenSSH'*. Textual encoding, done according to OpenSSH specification. Only suitable for public keys (not private keys). + :Type format: string - passphrase : string - For private keys only. The pass phrase used for deriving the encryption - key. + :Parameter passphrase: In case of PEM, the pass phrase to derive the encryption key from. + :Type passphrase: string - pkcs : integer - For *DER* and *PEM* format only. - The PKCS standard to follow for assembling the components of the key. - You have two choices: + :Parameter pkcs: The PKCS standard to follow for assembling the key. + You have two choices: - - **1** (default): the public key is embedded into - an X.509 ``SubjectPublicKeyInfo`` DER SEQUENCE. - The private key is embedded into a `PKCS#1`_ - ``RSAPrivateKey`` DER SEQUENCE. - - **8**: the private key is embedded into a `PKCS#8`_ - ``PrivateKeyInfo`` DER SEQUENCE. This value cannot be used - for public keys. + - with **1**, the public key is embedded into an X.509 `SubjectPublicKeyInfo` DER SEQUENCE. + The private key is embedded into a `PKCS#1`_ `RSAPrivateKey` DER SEQUENCE. + This mode is the default. + - with **8**, the private key is embedded into a `PKCS#8`_ `PrivateKeyInfo` DER SEQUENCE. + This mode is not available for public keys. - protection : string - The encryption scheme to use for protecting the private key. + PKCS standards are not relevant for the *OpenSSH* format. + :Type pkcs: integer - If ``None`` (default), the behavior depends on ``format``: - - - For *DER*, the *PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC* - scheme is used. The following operations are performed: - - 1. A 16 byte Triple DES key is derived from the passphrase - using `Crypto.Protocol.KDF.PBKDF2` with 8 bytes salt, - and 1 000 iterations of `Crypto.Hash.HMAC`. - 2. The private key is encrypted using CBC. - 3. The encrypted key is encoded according to PKCS#8. - - - For *PEM*, the obsolete PEM encryption scheme is used. - It is based on MD5 for key derivation, and Triple DES for encryption. - - Specifying a value for ``protection`` is only meaningful for PKCS#8 - (that is, ``pkcs=8``) and only if a pass phrase is present too. - - The supported schemes for PKCS#8 are listed in the - `Crypto.IO.PKCS8` module (see ``wrap_algo`` parameter). - - :Return: A byte string with the encoded public or private half - of the key. + :Return: A byte string with the encoded public or private half. :Raise ValueError: - When the format is unknown or when you try to encrypt a private - key with *DER* format and PKCS#1. - :attention: - If you don't provide a pass phrase, the private key will be - exported in the clear! + When the format is unknown. .. _RFC1421: http://www.ietf.org/rfc/rfc1421.txt .. _RFC1423: http://www.ietf.org/rfc/rfc1423.txt @@ -388,52 +348,59 @@ class _RSAobj(pubkey.pubkey): nb = long_to_bytes(self.n) if bord(eb[0]) & 0x80: eb=bchr(0x00)+eb if bord(nb[0]) & 0x80: nb=bchr(0x00)+nb - keyparts = [ b('ssh-rsa'), eb, nb ] - keystring = b('').join([ struct.pack(">I",len(kp))+kp for kp in keyparts]) - return b('ssh-rsa ')+binascii.b2a_base64(keystring)[:-1] + keyparts = [ 'ssh-rsa', eb, nb ] + keystring = ''.join([ struct.pack(">I",len(kp))+kp for kp in keyparts]) + return 'ssh-rsa '+binascii.b2a_base64(keystring)[:-1] # DER format is always used, even in case of PEM, which simply # encodes it into BASE64. + der = DerSequence() if self.has_private(): - binary_key = newDerSequence( - 0, - self.n, - self.e, - self.d, - self.p, - self.q, - self.d % (self.p-1), - self.d % (self.q-1), - inverse(self.q, self.p) - ).encode() - if pkcs==1: - keyType = 'RSA PRIVATE' - if format=='DER' and passphrase: - raise ValueError("PKCS#1 private key cannot be encrypted") - else: # PKCS#8 - if format=='PEM' and protection is None: - keyType = 'PRIVATE' - binary_key = PKCS8.wrap(binary_key, oid, None) - else: - keyType = 'ENCRYPTED PRIVATE' - if not protection: - protection = 'PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC' - binary_key = PKCS8.wrap(binary_key, oid, passphrase, protection) - passphrase = None + keyType= { 1: 'RSA PRIVATE', 8: 'PRIVATE' }[pkcs] + der[:] = [ 0, self.n, self.e, self.d, self.p, self.q, + self.d % (self.p-1), self.d % (self.q-1), + inverse(self.q, self.p) ] + if pkcs==8: + derkey = der.encode() + der = DerSequence([0]) + der.append(algorithmIdentifier) + der.append(DerObject('OCTET STRING', derkey).encode()) else: - keyType = "RSA PUBLIC" - binary_key = newDerSequence( - algorithmIdentifier, - newDerBitString( - newDerSequence( self.n, self.e ) - ) - ).encode() + keyType = "PUBLIC" + der.append(algorithmIdentifier) + bitmap = DerObject('BIT STRING') + derPK = DerSequence( [ self.n, self.e ] ) + bitmap.payload = bchr(0x00) + derPK.encode() + der.append(bitmap.encode()) if format=='DER': - return binary_key + return der.encode() if format=='PEM': - pem_str = PEM.encode(binary_key, keyType+" KEY", passphrase, self._randfunc) - return tobytes(pem_str) - raise ValueError("Unknown key format '%s'. Cannot export the RSA key." % format) + pem = b("-----BEGIN " + keyType + " KEY-----\n") + objenc = None + if passphrase and keyType.endswith('PRIVATE'): + # We only support 3DES for encryption + import Crypto.Hash.MD5 + from Crypto.Cipher import DES3 + from Crypto.Protocol.KDF import PBKDF1 + salt = self._randfunc(8) + key = PBKDF1(passphrase, salt, 16, 1, Crypto.Hash.MD5) + key += PBKDF1(key+passphrase, salt, 8, 1, Crypto.Hash.MD5) + objenc = DES3.new(key, Crypto.Cipher.DES3.MODE_CBC, salt) + pem += b('Proc-Type: 4,ENCRYPTED\n') + pem += b('DEK-Info: DES-EDE3-CBC,') + binascii.b2a_hex(salt).upper() + b('\n\n') + + binaryKey = der.encode() + if objenc: + # Add PKCS#7-like padding + padding = objenc.block_size-len(binaryKey)%objenc.block_size + binaryKey = objenc.encrypt(binaryKey+bchr(padding)*padding) + + # Each BASE64 line can take up to 64 characters (=48 bytes of data) + chunks = [ binascii.b2a_base64(binaryKey[i:i+48]) for i in range(0, len(binaryKey), 48) ] + pem += b('').join(chunks) + pem += b("-----END " + keyType + " KEY-----") + return pem + return ValueError("Unknown key format '%s'. Cannot export the RSA key." % format) class RSAImplementation(object): """ @@ -572,139 +539,159 @@ class RSAImplementation(object): key = self._math.rsa_construct(*tup) return _RSAobj(self, key) - def _importKeyDER(self, extern_key, passphrase=None): + def _importKeyDER(self, externKey): """Import an RSA key (public or private half), encoded in DER form.""" try: - der = decode_der(DerSequence, extern_key) + der = DerSequence() + der.decode(externKey, True) # Try PKCS#1 first, for a private key - if len(der) == 9 and der.hasOnlyInts() and der[0] == 0: + if len(der)==9 and der.hasOnlyInts() and der[0]==0: # ASN.1 RSAPrivateKey element - del der[6:] # Remove d mod (p-1), - # d mod (q-1), and - # q^{-1} mod p - der.append(inverse(der[4], der[5])) # Add p^{-1} mod q + del der[6:] # Remove d mod (p-1), d mod (q-1), and q^{-1} mod p + der.append(inverse(der[4],der[5])) # Add p^{-1} mod q del der[0] # Remove version return self.construct(der[:]) # Keep on trying PKCS#1, but now for a public key - if len(der) == 2: - try: - # The DER object is an RSAPublicKey SEQUENCE with - # two elements - if der.hasOnlyInts(): - return self.construct(der[:]) - # The DER object is a SubjectPublicKeyInfo SEQUENCE - # with two elements: an 'algorithmIdentifier' and a - # 'subjectPublicKey'BIT STRING. - # 'algorithmIdentifier' takes the value given at the - # module level. - # 'subjectPublicKey' encapsulates the actual ASN.1 - # RSAPublicKey element. - if der[0] == algorithmIdentifier: - bitmap = decode_der(DerBitString, der[1]) - rsaPub = decode_der(DerSequence, bitmap.value) - if len(rsaPub) == 2 and rsaPub.hasOnlyInts(): - return self.construct(rsaPub[:]) - except (ValueError, EOFError): - pass + if len(der)==2: + # The DER object is an RSAPublicKey SEQUENCE with two elements + if der.hasOnlyInts(): + return self.construct(der[:]) + # The DER object is a SubjectPublicKeyInfo SEQUENCE with two elements: + # an 'algorithm' (or 'algorithmIdentifier') SEQUENCE and a 'subjectPublicKey' BIT STRING. + # 'algorithm' takes the value given a few lines above. + # 'subjectPublicKey' encapsulates the actual ASN.1 RSAPublicKey element. + if der[0]==algorithmIdentifier: + bitmap = DerObject() + bitmap.decode(der[1], True) + if bitmap.isType('BIT STRING') and bord(bitmap.payload[0])==0x00: + der.decode(bitmap.payload[1:], True) + if len(der)==2 and der.hasOnlyInts(): + return self.construct(der[:]) - # Try PKCS#8 (possibly encrypted) - k = PKCS8.unwrap(extern_key, passphrase) - if k[0] == oid: - return self._importKeyDER(k[1], passphrase) + # Try unencrypted PKCS#8 + if der[0]==0: + # The second element in the SEQUENCE is algorithmIdentifier. + # It must say RSA (see above for description). + if der[1]==algorithmIdentifier: + privateKey = DerObject() + privateKey.decode(der[2], True) + if privateKey.isType('OCTET STRING'): + return self._importKeyDER(privateKey.payload) - except (ValueError, EOFError): + except ValueError, IndexError: pass raise ValueError("RSA key format is not supported") - def importKey(self, extern_key, passphrase=None): - """Import an RSA key (public or private half), encoded in standard - form. + def importKey(self, externKey, passphrase=None): + """Import an RSA key (public or private half), encoded in standard form. - :Parameter extern_key: + :Parameter externKey: The RSA key to import, encoded as a string. An RSA public key can be in any of the following formats: - - X.509 ``subjectPublicKeyInfo`` DER SEQUENCE (binary or PEM - encoding) - - `PKCS#1`_ ``RSAPublicKey`` DER SEQUENCE (binary or PEM encoding) + - X.509 `subjectPublicKeyInfo` DER SEQUENCE (binary or PEM encoding) + - `PKCS#1`_ `RSAPublicKey` DER SEQUENCE (binary or PEM encoding) - OpenSSH (textual public key only) An RSA private key can be in any of the following formats: - - PKCS#1 ``RSAPrivateKey`` DER SEQUENCE (binary or PEM encoding) - - `PKCS#8`_ ``PrivateKeyInfo`` or ``EncryptedPrivateKeyInfo`` - DER SEQUENCE (binary or PEM encoding) + - PKCS#1 `RSAPrivateKey` DER SEQUENCE (binary or PEM encoding) + - `PKCS#8`_ `PrivateKeyInfo` DER SEQUENCE (binary or PEM encoding) - OpenSSH (textual public key only) For details about the PEM encoding, see `RFC1421`_/`RFC1423`_. - - The private key may be encrypted by means of a certain pass phrase - either at the PEM level or at the PKCS#8 level. - :Type extern_key: string + + In case of PEM encoding, the private key can be encrypted with DES or 3TDES according to a certain ``pass phrase``. + Only OpenSSL-compatible pass phrases are supported. + :Type externKey: string :Parameter passphrase: - In case of an encrypted private key, this is the pass phrase from - which the decryption key is derived. + In case of an encrypted PEM key, this is the pass phrase from which the encryption key is derived. :Type passphrase: string - + :Return: An RSA key object (`_RSAobj`). :Raise ValueError/IndexError/TypeError: - When the given key cannot be parsed (possibly because the pass - phrase is wrong). + When the given key cannot be parsed (possibly because the pass phrase is wrong). .. _RFC1421: http://www.ietf.org/rfc/rfc1421.txt .. _RFC1423: http://www.ietf.org/rfc/rfc1423.txt .. _`PKCS#1`: http://www.ietf.org/rfc/rfc3447.txt .. _`PKCS#8`: http://www.ietf.org/rfc/rfc5208.txt """ - extern_key = tobytes(extern_key) + externKey = tobytes(externKey) if passphrase is not None: passphrase = tobytes(passphrase) - if extern_key.startswith(b('-----')): - # This is probably a PEM encoded key. - (der, marker, enc_flag) = PEM.decode(tostr(extern_key), passphrase) - if enc_flag: - passphrase = None - return self._importKeyDER(der, passphrase) + if externKey.startswith(b('-----')): + # This is probably a PEM encoded key + lines = externKey.replace(b(" "),b('')).split() + keyobj = None - if extern_key.startswith(b('ssh-rsa ')): + # The encrypted PEM format + if lines[1].startswith(b('Proc-Type:4,ENCRYPTED')): + DEK = lines[2].split(b(':')) + if len(DEK)!=2 or DEK[0]!=b('DEK-Info') or not passphrase: + raise ValueError("PEM encryption format not supported.") + algo, salt = DEK[1].split(b(',')) + salt = binascii.a2b_hex(salt) + import Crypto.Hash.MD5 + from Crypto.Cipher import DES, DES3 + from Crypto.Protocol.KDF import PBKDF1 + if algo==b("DES-CBC"): + # This is EVP_BytesToKey in OpenSSL + key = PBKDF1(passphrase, salt, 8, 1, Crypto.Hash.MD5) + keyobj = DES.new(key, Crypto.Cipher.DES.MODE_CBC, salt) + elif algo==b("DES-EDE3-CBC"): + # Note that EVP_BytesToKey is note exactly the same as PBKDF1 + key = PBKDF1(passphrase, salt, 16, 1, Crypto.Hash.MD5) + key += PBKDF1(key+passphrase, salt, 8, 1, Crypto.Hash.MD5) + keyobj = DES3.new(key, Crypto.Cipher.DES3.MODE_CBC, salt) + else: + raise ValueError("Unsupport PEM encryption algorithm.") + lines = lines[2:] + + der = binascii.a2b_base64(b('').join(lines[1:-1])) + if keyobj: + der = keyobj.decrypt(der) + padding = bord(der[-1]) + der = der[:-padding] + return self._importKeyDER(der) + + if externKey.startswith(b('ssh-rsa ')): # This is probably an OpenSSH key - keystring = binascii.a2b_base64(extern_key.split(b(' '))[1]) + keystring = binascii.a2b_base64(externKey.split(b(' '))[1]) keyparts = [] - while len(keystring) > 4: - l = struct.unpack(">I", keystring[:4])[0] - keyparts.append(keystring[4:4 + l]) - keystring = keystring[4 + l:] + while len(keystring)>4: + l = struct.unpack(">I",keystring[:4])[0] + keyparts.append(keystring[4:4+l]) + keystring = keystring[4+l:] e = bytes_to_long(keyparts[1]) n = bytes_to_long(keyparts[2]) return self.construct([n, e]) - - if bord(extern_key[0]) == 0x30: + if bord(externKey[0])==0x30: # This is probably a DER encoded key - return self._importKeyDER(extern_key, passphrase) - + return self._importKeyDER(externKey) + raise ValueError("RSA key format is not supported") -#: `Object ID`_ for the RSA encryption algorithm. This OID often indicates -#: a generic RSA key, even when such key will be actually used for digital -#: signatures. -#: -#: .. _`Object ID`: http://www.alvestrand.no/objectid/1.2.840.113549.1.1.1.html -oid = "1.2.840.113549.1.1.1" - -#: This is the standard DER object that qualifies a cryptographic algorithm -#: in ASN.1-based data structures (e.g. X.509 certificates). +#: This is the ASN.1 DER object that qualifies an algorithm as +#: compliant to PKCS#1 (that is, the standard RSA). +# It is found in all 'algorithm' fields (also called 'algorithmIdentifier'). +# It is a SEQUENCE with the oid assigned to RSA and with its parameters (none). +# 0x06 0x09 OBJECT IDENTIFIER, 9 bytes of payload +# 0x2A 0x86 0x48 0x86 0xF7 0x0D 0x01 0x01 0x01 +# rsaEncryption (1 2 840 113549 1 1 1) (PKCS #1) +# 0x05 0x00 NULL algorithmIdentifier = DerSequence( - [DerObjectId(oid).encode(), # algorithm field - DerNull().encode()] # parameters field + [ b('\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01'), + DerNull().encode() ] ).encode() _impl = RSAImplementation() diff --git a/modules/Crypto/PublicKey/_DSA.py b/modules/Crypto/PublicKey/_DSA.py index f027d92..6b7a964 100644 --- a/modules/Crypto/PublicKey/_DSA.py +++ b/modules/Crypto/PublicKey/_DSA.py @@ -30,7 +30,7 @@ __revision__ = "$Id$" from Crypto.PublicKey.pubkey import * from Crypto.Util import number from Crypto.Util.number import bytes_to_long, long_to_bytes -from Crypto.Hash import SHA1 +from Crypto.Hash import SHA from Crypto.Util.py3compat import * class error (Exception): @@ -38,8 +38,8 @@ class error (Exception): def generateQ(randfunc): S=randfunc(20) - hash1=SHA1.new(S).digest() - hash2=SHA1.new(long_to_bytes(bytes_to_long(S)+1)).digest() + hash1=SHA.new(S).digest() + hash2=SHA.new(long_to_bytes(bytes_to_long(S)+1)).digest() q = bignum(0) for i in range(0,20): c=bord(hash1[i])^bord(hash2[i]) @@ -77,7 +77,7 @@ def generate_py(bits, randfunc, progress_func=None): powL1=pow(bignum(2), bits-1) while C<4096: for k in range(0, n+1): - V[k]=bytes_to_long(SHA1.new(S+bstr(N)+bstr(k)).digest()) + V[k]=bytes_to_long(SHA.new(S+bstr(N)+bstr(k)).digest()) W=V[n] % powb for k in range(n-1, -1, -1): W=(W<<160L)+V[k] diff --git a/modules/Crypto/PublicKey/__init__.py b/modules/Crypto/PublicKey/__init__.py index df60c25..503809f 100644 --- a/modules/Crypto/PublicKey/__init__.py +++ b/modules/Crypto/PublicKey/__init__.py @@ -36,9 +36,6 @@ Crypto.PublicKey.RSA (Signing, encryption, and blinding) :undocumented: _DSA, _RSA, _fastmath, _slowmath, pubkey """ -class KeyFormatError(ValueError): - pass - __all__ = ['RSA', 'DSA', 'ElGamal'] __revision__ = "$Id$" diff --git a/modules/Crypto/Random/Fortuna/FortunaAccumulator.py b/modules/Crypto/Random/Fortuna/FortunaAccumulator.py index 5ffe825..5ebbe2b 100644 --- a/modules/Crypto/Random/Fortuna/FortunaAccumulator.py +++ b/modules/Crypto/Random/Fortuna/FortunaAccumulator.py @@ -36,9 +36,6 @@ import warnings from Crypto.pct_warnings import ClockRewindWarning import SHAd256 -# If the system has monotonic time, we'll use it. -from Crypto.Util._time import maybe_monotonic_time - import FortunaGenerator class FortunaPool(object): @@ -97,25 +94,8 @@ def which_pools(r): class FortunaAccumulator(object): - # An estimate of how many bytes we must append to pool 0 before it will - # contain 128 bits of entropy (with respect to an attack). We reseed the - # generator only after pool 0 contains `min_pool_size` bytes. Note that - # unlike with some other PRNGs, Fortuna's security does not rely on the - # accuracy of this estimate---we can accord to be optimistic here. - min_pool_size = 64 # size in bytes - - # If an attacker can predict some (but not all) of our entropy sources, the - # `min_pool_size` check may not be sufficient to prevent a successful state - # compromise extension attack. To resist this attack, Fortuna spreads the - # input across 32 pools, which are then consumed (to reseed the output - # generator) with exponentially decreasing frequency. - # - # In order to prevent an attacker from gaining knowledge of all 32 pools - # before we have a chance to fill them with enough information that the - # attacker cannot predict, we impose a rate limit of 10 reseeds/second (one - # per 100 ms). This ensures that a hypothetical 33rd pool would only be - # needed after a minimum of 13 years of sustained attack. - reseed_interval = 0.100 # time in seconds + min_pool_size = 64 # TODO: explain why + reseed_interval = 0.100 # 100 ms TODO: explain why def __init__(self): self.reseed_count = 0 @@ -129,17 +109,8 @@ class FortunaAccumulator(object): self.pools = [FortunaPool() for i in range(32)] # 32 pools assert(self.pools[0] is not self.pools[1]) - def _forget_last_reseed(self): - # This is not part of the standard Fortuna definition, and using this - # function frequently can weaken Fortuna's ability to resist a state - # compromise extension attack, but we need this in order to properly - # implement Crypto.Random.atfork(). Otherwise, forked child processes - # might continue to use their parent's PRNG state for up to 100ms in - # some cases. (e.g. CVE-2013-1445) - self.last_reseed = None - def random_data(self, bytes): - current_time = maybe_monotonic_time() + current_time = time.time() if (self.last_reseed is not None and self.last_reseed > current_time): # Avoid float comparison to None to make Py3k happy warnings.warn("Clock rewind detected. Resetting last_reseed.", ClockRewindWarning) self.last_reseed = None @@ -152,7 +123,7 @@ class FortunaAccumulator(object): def _reseed(self, current_time=None): if current_time is None: - current_time = maybe_monotonic_time() + current_time = time.time() seed = [] self.reseed_count += 1 self.last_reseed = current_time diff --git a/modules/Crypto/Random/Fortuna/FortunaGenerator.py b/modules/Crypto/Random/Fortuna/FortunaGenerator.py index 09351fc..723fa63 100644 --- a/modules/Crypto/Random/Fortuna/FortunaGenerator.py +++ b/modules/Crypto/Random/Fortuna/FortunaGenerator.py @@ -25,7 +25,7 @@ __revision__ = "$Id$" import sys -if sys.version_info[0] == 2 and sys.version_info[1] == 1: +if sys.version_info[0] is 2 and sys.version_info[1] is 1: from Crypto.Util.py21compat import * from Crypto.Util.py3compat import * diff --git a/modules/Crypto/Random/OSRNG/nt.py b/modules/Crypto/Random/OSRNG/nt.py index 40aa495..c1c2f44 100644 --- a/modules/Crypto/Random/OSRNG/nt.py +++ b/modules/Crypto/Random/OSRNG/nt.py @@ -25,7 +25,7 @@ __revision__ = "$Id$" __all__ = ['WindowsRNG'] -from Crypto.Random.OSRNG import winrandom +import winrandom from rng_base import BaseRNG class WindowsRNG(BaseRNG): diff --git a/modules/Crypto/Random/_UserFriendlyRNG.py b/modules/Crypto/Random/_UserFriendlyRNG.py index 957e006..c2a2eae 100644 --- a/modules/Crypto/Random/_UserFriendlyRNG.py +++ b/modules/Crypto/Random/_UserFriendlyRNG.py @@ -90,24 +90,9 @@ class _UserFriendlyRNG(object): """Initialize the random number generator and seed it with entropy from the operating system. """ - - # Save the pid (helps ensure that Crypto.Random.atfork() gets called) self._pid = os.getpid() - - # Collect entropy from the operating system and feed it to - # FortunaAccumulator self._ec.reinit() - # Override FortunaAccumulator's 100ms minimum re-seed interval. This - # is necessary to avoid a race condition between this function and - # self.read(), which that can otherwise cause forked child processes to - # produce identical output. (e.g. CVE-2013-1445) - # - # Note that if this function can be called frequently by an attacker, - # (and if the bits from OSRNG are insufficiently random) it will weaken - # Fortuna's ability to resist a state compromise extension attack. - self._fa._forget_last_reseed() - def close(self): self.closed = True self._osrng = None diff --git a/modules/Crypto/Random/random.py b/modules/Crypto/Random/random.py index 6b1c57a..bef02e6 100644 --- a/modules/Crypto/Random/random.py +++ b/modules/Crypto/Random/random.py @@ -103,13 +103,13 @@ class StrongRandom(object): def shuffle(self, x): """Shuffle the sequence in place.""" - # Fisher-Yates shuffle. O(n) - # See http://en.wikipedia.org/wiki/Fisher-Yates_shuffle - # Working backwards from the end of the array, we choose a random item - # from the remaining items until all items have been chosen. - for i in xrange(len(x)-1, 0, -1): # iterate from len(x)-1 downto 1 - j = self.randrange(0, i+1) # choose random j such that 0 <= j <= i - x[i], x[j] = x[j], x[i] # exchange x[i] and x[j] + # Make a (copy) of the list of objects we want to shuffle + items = list(x) + + # Choose a random item (without replacement) until all the items have been + # chosen. + for i in xrange(len(x)): + x[i] = items.pop(self.randrange(len(items))) def sample(self, population, k): """Return a k-length list of unique elements chosen from the population sequence.""" diff --git a/modules/Crypto/SelfTest/Cipher/common.py b/modules/Crypto/SelfTest/Cipher/common.py index 420b6ff..8bebed9 100644 --- a/modules/Crypto/SelfTest/Cipher/common.py +++ b/modules/Crypto/SelfTest/Cipher/common.py @@ -24,19 +24,12 @@ """Self-testing for PyCrypto hash modules""" -from __future__ import nested_scopes - __revision__ = "$Id$" import sys -if sys.version_info[0] == 2 and sys.version_info[1] == 1: - from Crypto.Util.py21compat import * - import unittest -from binascii import a2b_hex, b2a_hex, hexlify - +from binascii import a2b_hex, b2a_hex from Crypto.Util.py3compat import * -from Crypto.Util.strxor import strxor_c # For compatibility with Python 2.1 and Python 2.2 if sys.hexversion < 0x02030000: @@ -73,22 +66,14 @@ class CipherSelfTest(unittest.TestCase): self.plaintext = b(_extract(params, 'plaintext')) self.ciphertext = b(_extract(params, 'ciphertext')) self.module_name = _extract(params, 'module_name', None) - self.assoc_data = _extract(params, 'assoc_data', None) - self.mac = _extract(params, 'mac', None) - if self.assoc_data: - self.mac = b(self.mac) mode = _extract(params, 'mode', None) self.mode_name = str(mode) if mode is not None: # Block cipher self.mode = getattr(self.module, "MODE_" + mode) - self.iv = _extract(params, 'iv', None) - if self.iv is None: - self.iv = _extract(params, 'nonce', None) - if self.iv is not None: - self.iv = b(self.iv) + if self.iv is not None: self.iv = b(self.iv) # Only relevant for OPENPGP mode self.encrypted_iv = _extract(params, 'encrypted_iv', None) @@ -132,60 +117,29 @@ class CipherSelfTest(unittest.TestCase): else: return self.module.new(a2b_hex(self.key), self.mode, a2b_hex(self.iv), **params) - def isMode(self, name): - if not hasattr(self.module, "MODE_"+name): - return False - return self.mode == getattr(self.module, "MODE_"+name) - def runTest(self): plaintext = a2b_hex(self.plaintext) ciphertext = a2b_hex(self.ciphertext) - assoc_data = [] - if self.assoc_data: - assoc_data = [ a2b_hex(b(x)) for x in self.assoc_data] - ct = None - pt = None + ct1 = b2a_hex(self._new().encrypt(plaintext)) + pt1 = b2a_hex(self._new(1).decrypt(ciphertext)) + ct2 = b2a_hex(self._new().encrypt(plaintext)) + pt2 = b2a_hex(self._new(1).decrypt(ciphertext)) - # - # Repeat the same encryption or decryption twice and verify - # that the result is always the same - # - for i in xrange(2): - cipher = self._new() - decipher = self._new(1) - - # Only AEAD modes - for comp in assoc_data: - cipher.update(comp) - decipher.update(comp) - - ctX = b2a_hex(cipher.encrypt(plaintext)) - if self.isMode("SIV"): - ptX = b2a_hex(decipher.decrypt_and_verify(ciphertext, a2b_hex(self.mac))) - else: - ptX = b2a_hex(decipher.decrypt(ciphertext)) - - if ct: - self.assertEqual(ct, ctX) - self.assertEqual(pt, ptX) - ct, pt = ctX, ptX - - if self.isMode("OPENPGP"): + if hasattr(self.module, "MODE_OPENPGP") and self.mode == self.module.MODE_OPENPGP: # In PGP mode, data returned by the first encrypt() # is prefixed with the encrypted IV. # Here we check it and then remove it from the ciphertexts. eilen = len(self.encrypted_iv) - self.assertEqual(self.encrypted_iv, ct[:eilen]) - ct = ct[eilen:] + self.assertEqual(self.encrypted_iv, ct1[:eilen]) + self.assertEqual(self.encrypted_iv, ct2[:eilen]) + ct1 = ct1[eilen:] + ct2 = ct2[eilen:] - self.assertEqual(self.ciphertext, ct) # encrypt - self.assertEqual(self.plaintext, pt) # decrypt - - if self.mac: - mac = b2a_hex(cipher.digest()) - self.assertEqual(self.mac, mac) - decipher.verify(a2b_hex(self.mac)) + self.assertEqual(self.ciphertext, ct1) # encrypt + self.assertEqual(self.ciphertext, ct2) # encrypt (second time) + self.assertEqual(self.plaintext, pt1) # decrypt + self.assertEqual(self.plaintext, pt2) # decrypt (second time) class CipherStreamingSelfTest(CipherSelfTest): @@ -241,43 +195,18 @@ class CTRWraparoundTest(unittest.TestCase): self.module_name = params.get('module_name', None) def shortDescription(self): - return """Regression test: %s with MODE_CTR raising OverflowError on wraparound""" % (self.module_name,) + return """Regression test: %s with MODE_CTR should raise OverflowError on wraparound when shortcut used""" % (self.module_name,) def runTest(self): from Crypto.Util import Counter - def pythonCounter(): - state = [0] - def ctr(): - # First block succeeds; Second and subsequent blocks raise OverflowError - if state[0] == 0: - state[0] = 1 - return b("\xff") * self.module.block_size - else: - raise OverflowError - return ctr - - for little_endian in (0, 1): # (False, True) Test both endiannesses - block = b("\x00") * self.module.block_size - - # Test PyObject_CallObject code path: if the counter raises OverflowError - cipher = self.module.new(a2b_hex(self.key), self.module.MODE_CTR, counter=pythonCounter()) - cipher.encrypt(block) - self.assertRaises(OverflowError, cipher.encrypt, block) - self.assertRaises(OverflowError, cipher.encrypt, block) - - # Test PyObject_CallObject code path: counter object should raise OverflowError - ctr = Counter.new(8*self.module.block_size, initial_value=2L**(8*self.module.block_size)-1, little_endian=little_endian) - ctr() - self.assertRaises(OverflowError, ctr) - self.assertRaises(OverflowError, ctr) - - # Test the CTR-mode shortcut - ctr = Counter.new(8*self.module.block_size, initial_value=2L**(8*self.module.block_size)-1, little_endian=little_endian) - cipher = self.module.new(a2b_hex(self.key), self.module.MODE_CTR, counter=ctr) - cipher.encrypt(block) - self.assertRaises(OverflowError, cipher.encrypt, block) - self.assertRaises(OverflowError, cipher.encrypt, block) + for disable_shortcut in (0, 1): # (False, True) Test CTR-mode shortcut and PyObject_CallObject code paths + for little_endian in (0, 1): # (False, True) Test both endiannesses + ctr = Counter.new(8*self.module.block_size, initial_value=2L**(8*self.module.block_size)-1, little_endian=little_endian, disable_shortcut=disable_shortcut) + cipher = self.module.new(a2b_hex(self.key), self.module.MODE_CTR, counter=ctr) + block = b("\x00") * self.module.block_size + cipher.encrypt(block) + self.assertRaises(OverflowError, cipher.encrypt, block) class CFBSegmentSizeTest(unittest.TestCase): @@ -296,301 +225,6 @@ class CFBSegmentSizeTest(unittest.TestCase): self.assertRaises(ValueError, self.module.new, a2b_hex(self.key), self.module.MODE_CFB, segment_size=i) self.module.new(a2b_hex(self.key), self.module.MODE_CFB, "\0"*self.module.block_size, segment_size=8) # should succeed -class CCMMACLengthTest(unittest.TestCase): - """CCM specific tests about MAC""" - - def __init__(self, module): - unittest.TestCase.__init__(self) - self.module = module - self.key = b('\xFF')*16 - self.iv = b('\x00')*10 - - def shortDescription(self): - return self.description - - def runTest(self): - """Verify that MAC can only be 4,6,8,..,16 bytes long.""" - for i in range(3,16,2): - self.description = "CCM MAC length check (%d bytes)" % i - self.assertRaises(ValueError, self.module.new, self.key, - self.module.MODE_CCM, self.iv, msg_len=10, mac_len=i) - - """Verify that default MAC length is 16.""" - self.description = "CCM default MAC length check" - cipher = self.module.new(self.key, self.module.MODE_CCM, - self.iv, msg_len=4) - cipher.encrypt(b('z')*4) - self.assertEqual(len(cipher.digest()), 16) - -class CCMSplitEncryptionTest(unittest.TestCase): - """CCM specific tests to validate how encrypt() - decrypt() can be called multiple times on the - same object.""" - - def __init__(self, module): - unittest.TestCase.__init__(self) - self.module = module - self.key = b('\xFF')*16 - self.iv = b('\x00')*10 - self.description = "CCM Split Encryption Test" - - def shortDescription(self): - return self.description - - def runTest(self): - """Verify that CCM update()/encrypt() can be called multiple times, - provided that lengths are declared beforehand""" - - data = b("AUTH DATA") - pt1 = b("PLAINTEXT1") # Short - pt2 = b("PLAINTEXT2") # Long - pt_ref = pt1+pt2 - - # REFERENCE: Run with 1 update() and 1 encrypt() - cipher = self.module.new(self.key, self.module.MODE_CCM, - self.iv) - cipher.update(data) - ct_ref = cipher.encrypt(pt_ref) - mac_ref = cipher.digest() - - # Verify that calling CCM encrypt()/decrypt() twice is not - # possible without the 'msg_len' parameter and regardless - # of the 'assoc_len' parameter - for ad_len in None, len(data): - cipher = self.module.new(self.key, self.module.MODE_CCM, - self.iv, assoc_len=ad_len) - cipher.update(data) - cipher.encrypt(pt1) - self.assertRaises(TypeError, cipher.encrypt, pt2) - - cipher = self.module.new(self.key, self.module.MODE_CCM, - self.iv, assoc_len=ad_len) - cipher.update(data) - cipher.decrypt(ct_ref[:len(pt1)]) - self.assertRaises(TypeError, cipher.decrypt, ct_ref[len(pt1):]) - - # Run with 2 encrypt()/decrypt(). Results must be the same - # regardless of the 'assoc_len' parameter - for ad_len in None, len(data): - cipher = self.module.new(self.key, self.module.MODE_CCM, - self.iv, assoc_len=ad_len, msg_len=len(pt_ref)) - cipher.update(data) - ct = cipher.encrypt(pt1) - ct += cipher.encrypt(pt2) - mac = cipher.digest() - self.assertEqual(ct_ref, ct) - self.assertEqual(mac_ref, mac) - - cipher = self.module.new(self.key, self.module.MODE_CCM, - self.iv, msg_len=len(pt1+pt2)) - cipher.update(data) - pt = cipher.decrypt(ct[:len(pt1)]) - pt += cipher.decrypt(ct[len(pt1):]) - mac = cipher.verify(mac_ref) - self.assertEqual(pt_ref, pt) - -class AEADTests(unittest.TestCase): - """Tests generic to all AEAD modes""" - - def __init__(self, module, mode_name, key_size): - unittest.TestCase.__init__(self) - self.module = module - self.mode_name = mode_name - self.mode = getattr(module, mode_name) - if not self.isMode("SIV"): - self.key = b('\xFF')*key_size - else: - self.key = b('\xFF')*key_size*2 - self.iv = b('\x00')*10 - self.description = "AEAD Test" - - def isMode(self, name): - if not hasattr(self.module, "MODE_"+name): - return False - return self.mode == getattr(self.module, "MODE_"+name) - - def right_mac_test(self): - """Positive tests for MAC""" - - self.description = "Test for right MAC in %s of %s" % \ - (self.mode_name, self.module.__name__) - - ad_ref = b("Reference AD") - pt_ref = b("Reference plaintext") - - # Encrypt and create the reference MAC - cipher = self.module.new(self.key, self.mode, self.iv) - cipher.update(ad_ref) - ct_ref = cipher.encrypt(pt_ref) - mac_ref = cipher.digest() - - # Decrypt and verify that MAC is accepted - decipher = self.module.new(self.key, self.mode, self.iv) - decipher.update(ad_ref) - pt = decipher.decrypt_and_verify(ct_ref, mac_ref) - self.assertEqual(pt, pt_ref) - - # Verify that hexverify work - decipher.hexverify(hexlify(mac_ref)) - - def wrong_mac_test(self): - """Negative tests for MAC""" - - self.description = "Test for wrong MAC in %s of %s" % \ - (self.mode_name, self.module.__name__) - - ad_ref = b("Reference AD") - pt_ref = b("Reference plaintext") - - # Encrypt and create the reference MAC - cipher = self.module.new(self.key, self.mode, self.iv) - cipher.update(ad_ref) - ct_ref = cipher.encrypt(pt_ref) - mac_ref = cipher.digest() - - # Modify the MAC and verify it is NOT ACCEPTED - wrong_mac = strxor_c(mac_ref, 255) - decipher = self.module.new(self.key, self.mode, self.iv) - decipher.update(ad_ref) - self.assertRaises(ValueError, decipher.decrypt_and_verify, - ct_ref, wrong_mac) - - def zero_data(self): - """Verify transition from INITIALIZED to FINISHED""" - - self.description = "Test for zero data in %s of %s" % \ - (self.mode_name, self.module.__name__) - cipher = self.module.new(self.key, self.mode, self.iv) - cipher.digest() - - def multiple_updates(self): - """Verify that update() can be called multiple times""" - - self.description = "Test for multiple updates in %s of %s" % \ - (self.mode_name, self.module.__name__) - - # In all modes other than SIV, the associated data is a single - # component that can be arbitrarilly split and submitted to update(). - # - # In SIV, associated data is instead organized in a vector or multiple - # components. Each component is passed to update() as a whole. - # This test is therefore not meaningful to SIV. - if self.isMode("SIV"): - return - - ad = b("").join([bchr(x) for x in xrange(0,128)]) - - mac1, mac2, mac3 = (None,)*3 - for chunk_length in 1,10,40,80,128: - chunks = [ad[i:i+chunk_length] for i in range(0, len(ad), chunk_length)] - - # No encryption/decryption - cipher = self.module.new(self.key, self.mode, self.iv) - for c in chunks: - cipher.update(c) - if mac1: - cipher.verify(mac1) - else: - mac1 = cipher.digest() - - # Encryption - cipher = self.module.new(self.key, self.mode, self.iv) - for c in chunks: - cipher.update(c) - ct = cipher.encrypt(b("PT")) - mac2 = cipher.digest() - - # Decryption - cipher = self.module.new(self.key, self.mode, self.iv) - for c in chunks: - cipher.update(c) - cipher.decrypt(ct) - cipher.verify(mac2) - - def no_mix_encrypt_decrypt(self): - """Verify that encrypt and decrypt cannot be mixed up""" - - self.description = "Test for mix of encrypt and decrypt in %s of %s" % \ - (self.mode_name, self.module.__name__) - - # Calling decrypt after encrypt raises an exception - cipher = self.module.new(self.key, self.mode, self.iv) - cipher.encrypt(b("PT")*40) - self.assertRaises(TypeError, cipher.decrypt, b("XYZ")*40) - - # Calling encrypt() after decrypt() raises an exception - # (excluded for SIV, since decrypt() is not valid) - if not self.isMode("SIV"): - cipher = self.module.new(self.key, self.mode, self.iv) - cipher.decrypt(b("CT")*40) - self.assertRaises(TypeError, cipher.encrypt, b("XYZ")*40) - - # Calling verify after encrypt raises an exception - cipher = self.module.new(self.key, self.mode, self.iv) - cipher.encrypt(b("PT")*40) - self.assertRaises(TypeError, cipher.verify, b("XYZ")) - self.assertRaises(TypeError, cipher.hexverify, "12") - - # Calling digest() after decrypt() raises an exception - # (excluded for SIV, since decrypt() is not valid) - if not self.isMode("SIV"): - cipher = self.module.new(self.key, self.mode, self.iv) - cipher.decrypt(b("CT")*40) - self.assertRaises(TypeError, cipher.digest) - self.assertRaises(TypeError, cipher.hexdigest) - - def no_late_update(self): - """Verify that update cannot be called after encrypt or decrypt""" - - self.description = "Test for late update in %s of %s" % \ - (self.mode_name, self.module.__name__) - - # Calling update after encrypt raises an exception - cipher = self.module.new(self.key, self.mode, self.iv) - cipher.update(b("XX")) - cipher.encrypt(b("PT")*40) - self.assertRaises(TypeError, cipher.update, b("XYZ")) - - # Calling update() after decrypt() raises an exception - # (excluded for SIV, since decrypt() is not valid) - if not self.isMode("SIV"): - cipher = self.module.new(self.key, self.mode, self.iv) - cipher.update(b("XX")) - cipher.decrypt(b("CT")*40) - self.assertRaises(TypeError, cipher.update, b("XYZ")) - - def loopback(self): - """Verify composition of encrypt_and_digest() and decrypt_and_verify() - is the identity function.""" - - self.description = "Lookback test decrypt_and_verify(encrypt_and_digest)"\ - "for %s in %s" % (self.mode_name, - self.module.__name__) - - enc_cipher = self.module.new(self.key, self.mode, self.iv) - dec_cipher = self.module.new(self.key, self.mode, self.iv) - - enc_cipher.update(b("XXX")) - dec_cipher.update(b("XXX")) - - plaintext = b("Reference") * 10 - ct, mac = enc_cipher.encrypt_and_digest(plaintext) - pt = dec_cipher.decrypt_and_verify(ct, mac) - - self.assertEqual(plaintext, pt) - - def runTest(self): - self.right_mac_test() - self.wrong_mac_test() - self.zero_data() - self.multiple_updates() - self.no_mix_encrypt_decrypt() - self.no_late_update() - self.loopback() - - def shortDescription(self): - return self.description - class RoundtripTest(unittest.TestCase): def __init__(self, module, params): from Crypto import Random @@ -608,7 +242,7 @@ class RoundtripTest(unittest.TestCase): for mode in (self.module.MODE_ECB, self.module.MODE_CBC, self.module.MODE_CFB, self.module.MODE_OFB, self.module.MODE_OPENPGP): encryption_cipher = self.module.new(a2b_hex(self.key), mode, self.iv) ciphertext = encryption_cipher.encrypt(self.plaintext) - + if mode != self.module.MODE_OPENPGP: decryption_cipher = self.module.new(a2b_hex(self.key), mode, self.iv) else: @@ -649,17 +283,13 @@ class IVLengthTest(unittest.TestCase): self.module.MODE_OFB, "") self.assertRaises(ValueError, self.module.new, a2b_hex(self.key), self.module.MODE_OPENPGP, "") - if hasattr(self.module, "MODE_CCM"): - for ivlen in (0,6,14): - self.assertRaises(ValueError, self.module.new, a2b_hex(self.key), - self.module.MODE_CCM, bchr(0)*ivlen, msg_len=10) self.module.new(a2b_hex(self.key), self.module.MODE_ECB, "") self.module.new(a2b_hex(self.key), self.module.MODE_CTR, "", counter=self._dummy_counter) def _dummy_counter(self): return "\0" * self.module.block_size -def make_block_tests(module, module_name, test_data, additional_params=dict()): +def make_block_tests(module, module_name, test_data): tests = [] extra_tests_added = 0 for i in range(len(test_data)): @@ -696,7 +326,6 @@ def make_block_tests(module, module_name, test_data, additional_params=dict()): name = "%s #%d: %s" % (module_name, i+1, description) params['description'] = name params['module_name'] = module_name - params.update(additional_params) # Add extra test(s) to the test suite before the current test if not extra_tests_added: @@ -710,18 +339,11 @@ def make_block_tests(module, module_name, test_data, additional_params=dict()): ] extra_tests_added = 1 - # Extract associated data and MAC for AEAD modes - if p_mode in ('CCM', 'EAX', 'SIV', 'GCM'): - assoc_data, params['plaintext'] = params['plaintext'].split('|') - assoc_data2, params['ciphertext'], params['mac'] = params['ciphertext'].split('|') - params['assoc_data'] = assoc_data.split("-") - params['mac_len'] = len(params['mac'])>>1 - # Add the current test to the test suite tests.append(CipherSelfTest(module, params)) # When using CTR mode, test that the interface behaves like a stream cipher - if p_mode in ('OFB', 'CTR'): + if p_mode == 'CTR': tests.append(CipherStreamingSelfTest(module, params)) # When using CTR mode, test the non-shortcut code path. @@ -733,25 +355,6 @@ def make_block_tests(module, module_name, test_data, additional_params=dict()): if not params2['ctr_params'].has_key('disable_shortcut'): params2['ctr_params']['disable_shortcut'] = 1 tests.append(CipherSelfTest(module, params2)) - - # Add tests that don't use test vectors - if hasattr(module, "MODE_CCM"): - tests += [ - CCMMACLengthTest(module), - CCMSplitEncryptionTest(module), - ] - for aead_mode in ("MODE_CCM","MODE_EAX", "MODE_SIV", "MODE_GCM"): - if hasattr(module, aead_mode): - key_sizes = [] - try: - key_sizes += module.key_size - except TypeError: - key_sizes = [ module.key_size ] - for ks in key_sizes: - tests += [ - AEADTests(module, aead_mode, ks), - ] - return tests def make_stream_tests(module, module_name, test_data): diff --git a/modules/Crypto/SelfTest/Cipher/test_AES.py b/modules/Crypto/SelfTest/Cipher/test_AES.py index f54f473..ea7c323 100644 --- a/modules/Crypto/SelfTest/Cipher/test_AES.py +++ b/modules/Crypto/SelfTest/Cipher/test_AES.py @@ -26,11 +26,8 @@ __revision__ = "$Id$" -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 common import dict # For compatibility with Python 2.1 and 2.2 +from Crypto.Util.py3compat import * from binascii import hexlify # This is a list of (plaintext, ciphertext, key[, description[, params]]) tuples. @@ -1323,30 +1320,6 @@ test_data = [ 'NIST 800-38A, F.4.5, OFB and AES-256', dict(mode='OFB', iv='000102030405060708090a0b0c0d0e0f')), - ('6bc1bee22e409f96e93d7e117393172a'+'ae2d8a571e03ac9c9eb76fac45af8e51'+ - '30c81c46a35ce411e5fbc1191a0a52ef'+'f69f2445df4f9b17', - '3b3fd92eb72dad20333449f8e83cfb4a'+'7789508d16918f03f53c52dac54ed825'+ - '9740051e9c5fecf64344f7a82260edcc'+'304c6528f659c778', - '2b7e151628aed2a6abf7158809cf4f3c', - 'NIST 800-38A, F.4.1, OFB and AES-128 (partial last block)', - dict(mode='OFB', iv='000102030405060708090a0b0c0d0e0f')), - - ('6bc1bee22e409f96e93d7e117393172a'+'ae2d8a571e03ac9c9eb76fac45af8e51'+ - '30c81c46a35ce411e5fbc1191a0a52ef'+'f69f2445df4f9b17', - 'cdc80d6fddf18cab34c25909c99a4174'+'fcc28b8d4c63837c09e81700c1100401'+ - '8d9a9aeac0f6596f559c6d4daf59a5f2'+'6d9f200857ca6c3e', - '8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b', - 'NIST 800-38A, F.4.3, OFB and AES-192 (partial last block)', - dict(mode='OFB', iv='000102030405060708090a0b0c0d0e0f')), - - ('6bc1bee22e409f96e93d7e117393172a'+'ae2d8a571e03ac9c9eb76fac45af8e51'+ - '30c81c46a35ce411e5fbc1191a0a52ef'+'f69f2445df4f9b17', - 'dc7e84bfda79164b7ecd8486985d3860'+'4febdc6740d20b3ac88f6ad82a4fb08d'+ - '71ab47a086e86eedf39d1c5bba97c408'+'0126141d67f37be8', - '603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4', - 'NIST 800-38A, F.4.5, OFB and AES-256 (partial last block)', - dict(mode='OFB', iv='000102030405060708090a0b0c0d0e0f')), - ('6bc1bee22e409f96e93d7e117393172a'+'ae2d8a571e03ac9c9eb76fac45af8e51'+ '30c81c46a35ce411e5fbc1191a0a52ef'+'f69f2445df4f9b17ad2b417be66c3710', '874d6191b620e3261bef6864990db6ce'+'9806f66b7970fdff8617187bb9fffdff'+ @@ -1445,565 +1418,12 @@ test_data = [ '5baa61e4c9b93f3f0682250b6cf8331b', # Key (hash of 'password') 'GPG Test Vector #1', dict(mode='OPENPGP', iv='3d7d3e62282add7eb203eeba5c800733', encrypted_iv='fd934601ef49cb58b6d9aebca6056bdb96ef' ) ), - - # NIST SP 800-38C test vectors for CCM - # This is a list of tuples with 5 items: - # - # 1. Associated data + '|' + plaintext - # 2. Associated data + '|' + ciphertext + '|' + MAC - # 3. AES-128 key - # 4. Description - # 5. Dictionary of parameters to be passed to AES.new(). - # It must include the nonce. - # - ( '0001020304050607|20212223', - '0001020304050607|7162015b|4dac255d', - '404142434445464748494a4b4c4d4e4f', - 'NIST SP 800-38C Appex C.1', - dict(mode='CCM', nonce='10111213141516') - ), - ( '000102030405060708090a0b0c0d0e0f|202122232425262728292a2b2c2d2e2f', - '000102030405060708090a0b0c0d0e0f|d2a1f0e051ea5f62081a7792073d593d|1fc64fbfaccd', - '404142434445464748494a4b4c4d4e4f', - 'NIST SP 800-38C Appex C.2', - dict(mode='CCM', nonce='1011121314151617') - ), - ( '000102030405060708090a0b0c0d0e0f10111213|'+ - '202122232425262728292a2b2c2d2e2f3031323334353637', - '000102030405060708090a0b0c0d0e0f10111213|'+ - 'e3b201a9f5b71a7a9b1ceaeccd97e70b6176aad9a4428aa5|484392fbc1b09951', - '404142434445464748494a4b4c4d4e4f', - 'NIST SP 800-38C Appex C.3', - dict(mode='CCM', nonce='101112131415161718191a1b') - ), - ( - (''.join(["%02X" % (x*16+y) for x in xrange(0,16) for y in xrange(0,16)]))*256+'|'+ - '202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f', - (''.join(["%02X" % (x*16+y) for x in xrange(0,16) for y in xrange(0,16)]))*256+'|'+ - '69915dad1e84c6376a68c2967e4dab615ae0fd1faec44cc484828529463ccf72|'+ - 'b4ac6bec93e8598e7f0dadbcea5b', - '404142434445464748494a4b4c4d4e4f', - 'NIST SP 800-38C Appex C.4', - dict(mode='CCM', nonce='101112131415161718191a1b1c') - ), - # RFC3610 test vectors - ( - '0001020304050607|08090a0b0c0d0e0f101112131415161718191a1b1c1d1e', - '0001020304050607|588c979a61c663d2f066d0c2c0f989806d5f6b61dac384|'+ - '17e8d12cfdf926e0', - 'c0c1c2c3c4c5c6c7c8c9cacbcccdcecf', - 'RFC3610 Packet Vector #1', - dict(mode='CCM', nonce='00000003020100a0a1a2a3a4a5') - ), - ( - '0001020304050607|08090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f', - '0001020304050607|72c91a36e135f8cf291ca894085c87e3cc15c439c9e43a3b|'+ - 'a091d56e10400916', - 'c0c1c2c3c4c5c6c7c8c9cacbcccdcecf', - 'RFC3610 Packet Vector #2', - dict(mode='CCM', nonce='00000004030201a0a1a2a3a4a5') - ), - ( - '0001020304050607|08090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20', - '0001020304050607|51b1e5f44a197d1da46b0f8e2d282ae871e838bb64da859657|'+ - '4adaa76fbd9fb0c5', - 'c0c1c2c3c4c5c6c7c8c9cacbcccdcecf', - 'RFC3610 Packet Vector #3', - dict(mode='CCM', nonce='00000005040302A0A1A2A3A4A5') - ), - ( - '000102030405060708090a0b|0c0d0e0f101112131415161718191a1b1c1d1e', - '000102030405060708090a0b|a28c6865939a9a79faaa5c4c2a9d4a91cdac8c|'+ - '96c861b9c9e61ef1', - 'c0c1c2c3c4c5c6c7c8c9cacbcccdcecf', - 'RFC3610 Packet Vector #4', - dict(mode='CCM', nonce='00000006050403a0a1a2a3a4a5') - ), - ( - '000102030405060708090a0b|0c0d0e0f101112131415161718191a1b1c1d1e1f', - '000102030405060708090a0b|dcf1fb7b5d9e23fb9d4e131253658ad86ebdca3e|'+ - '51e83f077d9c2d93', - 'c0c1c2c3c4c5c6c7c8c9cacbcccdcecf', - 'RFC3610 Packet Vector #5', - dict(mode='CCM', nonce='00000007060504a0a1a2a3a4a5') - ), - ( - '000102030405060708090a0b|0c0d0e0f101112131415161718191a1b1c1d1e1f20', - '000102030405060708090a0b|6fc1b011f006568b5171a42d953d469b2570a4bd87|'+ - '405a0443ac91cb94', - 'c0c1c2c3c4c5c6c7c8c9cacbcccdcecf', - 'RFC3610 Packet Vector #6', - dict(mode='CCM', nonce='00000008070605a0a1a2a3a4a5') - ), - ( - '0001020304050607|08090a0b0c0d0e0f101112131415161718191a1b1c1d1e', - '0001020304050607|0135d1b2c95f41d5d1d4fec185d166b8094e999dfed96c|'+ - '048c56602c97acbb7490', - 'c0c1c2c3c4c5c6c7c8c9cacbcccdcecf', - 'RFC3610 Packet Vector #7', - dict(mode='CCM', nonce='00000009080706a0a1a2a3a4a5') - ), - ( - '0001020304050607|08090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f', - '0001020304050607|7b75399ac0831dd2f0bbd75879a2fd8f6cae6b6cd9b7db24|'+ - 'c17b4433f434963f34b4', - 'c0c1c2c3c4c5c6c7c8c9cacbcccdcecf', - 'RFC3610 Packet Vector #8', - dict(mode='CCM', nonce='0000000a090807a0a1a2a3a4a5') - ), - ( - '0001020304050607|08090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20', - '0001020304050607|82531a60cc24945a4b8279181ab5c84df21ce7f9b73f42e197|'+ - 'ea9c07e56b5eb17e5f4e', - 'c0c1c2c3c4c5c6c7c8c9cacbcccdcecf', - 'RFC3610 Packet Vector #9', - dict(mode='CCM', nonce='0000000b0a0908a0a1a2a3a4a5') - ), - ( - '000102030405060708090a0b|0c0d0e0f101112131415161718191a1b1c1d1e', - '000102030405060708090a0b|07342594157785152b074098330abb141b947b|'+ - '566aa9406b4d999988dd', - 'c0c1c2c3c4c5c6c7c8c9cacbcccdcecf', - 'RFC3610 Packet Vector #10', - dict(mode='CCM', nonce='0000000c0b0a09a0a1a2a3a4a5') - ), - ( - '000102030405060708090a0b|0c0d0e0f101112131415161718191a1b1c1d1e1f', - '000102030405060708090a0b|676bb20380b0e301e8ab79590a396da78b834934|'+ - 'f53aa2e9107a8b6c022c', - 'c0c1c2c3c4c5c6c7c8c9cacbcccdcecf', - 'RFC3610 Packet Vector #11', - dict(mode='CCM', nonce='0000000d0c0b0aa0a1a2a3a4a5') - ), - ( - '000102030405060708090a0b|0c0d0e0f101112131415161718191a1b1c1d1e1f20', - '000102030405060708090a0b|c0ffa0d6f05bdb67f24d43a4338d2aa4bed7b20e43|'+ - 'cd1aa31662e7ad65d6db', - 'c0c1c2c3c4c5c6c7c8c9cacbcccdcecf', - 'RFC3610 Packet Vector #12', - dict(mode='CCM', nonce='0000000e0d0c0ba0a1a2a3a4a5') - ), - ( - '0be1a88bace018b1|08e8cf97d820ea258460e96ad9cf5289054d895ceac47c', - '0be1a88bace018b1|4cb97f86a2a4689a877947ab8091ef5386a6ffbdd080f8|'+ - 'e78cf7cb0cddd7b3', - 'd7828d13b2b0bdc325a76236df93cc6b', - 'RFC3610 Packet Vector #13', - dict(mode='CCM', nonce='00412b4ea9cdbe3c9696766cfa') - ), - ( - '63018f76dc8a1bcb|9020ea6f91bdd85afa0039ba4baff9bfb79c7028949cd0ec', - '63018f76dc8a1bcb|4ccb1e7ca981befaa0726c55d378061298c85c92814abc33|'+ - 'c52ee81d7d77c08a', - 'd7828d13b2b0bdc325a76236df93cc6b', - 'RFC3610 Packet Vector #14', - dict(mode='CCM', nonce='0033568ef7b2633c9696766cfa') - ), - ( - 'aa6cfa36cae86b40|b916e0eacc1c00d7dcec68ec0b3bbb1a02de8a2d1aa346132e', - 'aa6cfa36cae86b40|b1d23a2220ddc0ac900d9aa03c61fcf4a559a4417767089708|'+ - 'a776796edb723506', - 'd7828d13b2b0bdc325a76236df93cc6b', - 'RFC3610 Packet Vector #15', - dict(mode='CCM', nonce='00103fe41336713c9696766cfa') - ), - ( - 'd0d0735c531e1becf049c244|12daac5630efa5396f770ce1a66b21f7b2101c', - 'd0d0735c531e1becf049c244|14d253c3967b70609b7cbb7c49916028324526|'+ - '9a6f49975bcadeaf', - 'd7828d13b2b0bdc325a76236df93cc6b', - 'RFC3610 Packet Vector #16', - dict(mode='CCM', nonce='00764c63b8058e3c9696766cfa') - ), - ( - '77b60f011c03e1525899bcae|e88b6a46c78d63e52eb8c546efb5de6f75e9cc0d', - '77b60f011c03e1525899bcae|5545ff1a085ee2efbf52b2e04bee1e2336c73e3f|'+ - '762c0c7744fe7e3c', - 'd7828d13b2b0bdc325a76236df93cc6b', - 'RFC3610 Packet Vector #17', - dict(mode='CCM', nonce='00f8b678094e3b3c9696766cfa') - ), - ( - 'cd9044d2b71fdb8120ea60c0|6435acbafb11a82e2f071d7ca4a5ebd93a803ba87f', - 'cd9044d2b71fdb8120ea60c0|009769ecabdf48625594c59251e6035722675e04c8|'+ - '47099e5ae0704551', - 'd7828d13b2b0bdc325a76236df93cc6b', - 'RFC3610 Packet Vector #18', - dict(mode='CCM', nonce='00d560912d3f703c9696766cfa') - ), - ( - 'd85bc7e69f944fb8|8a19b950bcf71a018e5e6701c91787659809d67dbedd18', - 'd85bc7e69f944fb8|bc218daa947427b6db386a99ac1aef23ade0b52939cb6a|'+ - '637cf9bec2408897c6ba', - 'd7828d13b2b0bdc325a76236df93cc6b', - 'RFC3610 Packet Vector #19', - dict(mode='CCM', nonce='0042fff8f1951c3c9696766cfa') - ), - ( - '74a0ebc9069f5b37|1761433c37c5a35fc1f39f406302eb907c6163be38c98437', - '74a0ebc9069f5b37|5810e6fd25874022e80361a478e3e9cf484ab04f447efff6|'+ - 'f0a477cc2fc9bf548944', - 'd7828d13b2b0bdc325a76236df93cc6b', - 'RFC3610 Packet Vector #20', - dict(mode='CCM', nonce='00920f40e56cdc3c9696766cfa') - ), - ( - '44a3aa3aae6475ca|a434a8e58500c6e41530538862d686ea9e81301b5ae4226bfa', - '44a3aa3aae6475ca|f2beed7bc5098e83feb5b31608f8e29c38819a89c8e776f154|'+ - '4d4151a4ed3a8b87b9ce', - 'd7828d13b2b0bdc325a76236df93cc6b', - 'RFC3610 Packet Vector #21', - dict(mode='CCM', nonce='0027ca0c7120bc3c9696766cfa') - ), - ( - 'ec46bb63b02520c33c49fd70|b96b49e21d621741632875db7f6c9243d2d7c2', - 'ec46bb63b02520c33c49fd70|31d750a09da3ed7fddd49a2032aabf17ec8ebf|'+ - '7d22c8088c666be5c197', - 'd7828d13b2b0bdc325a76236df93cc6b', - 'RFC3610 Packet Vector #22', - dict(mode='CCM', nonce='005b8ccbcd9af83c9696766cfa') - ), - ( - '47a65ac78b3d594227e85e71|e2fcfbb880442c731bf95167c8ffd7895e337076', - '47a65ac78b3d594227e85e71|e882f1dbd38ce3eda7c23f04dd65071eb41342ac|'+ - 'df7e00dccec7ae52987d', - 'd7828d13b2b0bdc325a76236df93cc6b', - 'RFC3610 Packet Vector #23', - dict(mode='CCM', nonce='003ebe94044b9a3c9696766cfa') - ), - ( - '6e37a6ef546d955d34ab6059|abf21c0b02feb88f856df4a37381bce3cc128517d4', - '6e37a6ef546d955d34ab6059|f32905b88a641b04b9c9ffb58cc390900f3da12ab1|'+ - '6dce9e82efa16da62059', - 'd7828d13b2b0bdc325a76236df93cc6b', - 'RFC3610 Packet Vector #24', - dict(mode='CCM', nonce='008d493b30ae8b3c9696766cfa') - ), - - # Test vectors for EAX taken from http://www.cs.ucdavis.edu/~rogaway/papers/eax.pdf - # This is a list of tuples with 5 items: - # - # 1. Header + '|' + plaintext - # 2. Header + '|' + ciphertext + '|' + MAC - # 3. AES-128 key - # 4. Description - # 5. Dictionary of parameters to be passed to AES.new(). It must - # include the nonce. - # - ( '6bfb914fd07eae6b|', - '6bfb914fd07eae6b||e037830e8389f27b025a2d6527e79d01', - '233952dee4d5ed5f9b9c6d6ff80ff478', - 'EAX spec Appendix G', - dict(mode='EAX', nonce='62EC67F9C3A4A407FCB2A8C49031A8B3') - ), - - ( 'fa3bfd4806eb53fa|f7fb', - 'fa3bfd4806eb53fa|19dd|5c4c9331049d0bdab0277408f67967e5', - '91945d3f4dcbee0bf45ef52255f095a4', - 'EAX spec Appendix G', - dict(mode='EAX', nonce='BECAF043B0A23D843194BA972C66DEBD') - ), - - ( '234a3463c1264ac6|1a47cb4933', - '234a3463c1264ac6|d851d5bae0|3a59f238a23e39199dc9266626c40f80', - '01f74ad64077f2e704c0f60ada3dd523', - 'EAX spec Appendix G', - dict(mode='EAX', nonce='70C3DB4F0D26368400A10ED05D2BFF5E') - ), - - ( '33cce2eabff5a79d|481c9e39b1', - '33cce2eabff5a79d|632a9d131a|d4c168a4225d8e1ff755939974a7bede', - 'd07cf6cbb7f313bdde66b727afd3c5e8', - 'EAX spec Appendix G', - dict(mode='EAX', nonce='8408DFFF3C1A2B1292DC199E46B7D617') - ), - - ( 'aeb96eaebe2970e9|40d0c07da5e4', - 'aeb96eaebe2970e9|071dfe16c675|cb0677e536f73afe6a14b74ee49844dd', - '35b6d0580005bbc12b0587124557d2c2', - 'EAX spec Appendix G', - dict(mode='EAX', nonce='FDB6B06676EEDC5C61D74276E1F8E816') - ), - - ( 'd4482d1ca78dce0f|4de3b35c3fc039245bd1fb7d', - 'd4482d1ca78dce0f|835bb4f15d743e350e728414|abb8644fd6ccb86947c5e10590210a4f', - 'bd8e6e11475e60b268784c38c62feb22', - 'EAX spec Appendix G', - dict(mode='EAX', nonce='6EAC5C93072D8E8513F750935E46DA1B') - ), - - ( '65d2017990d62528|8b0a79306c9ce7ed99dae4f87f8dd61636', - '65d2017990d62528|02083e3979da014812f59f11d52630da30|137327d10649b0aa6e1c181db617d7f2', - '7c77d6e813bed5ac98baa417477a2e7d', - 'EAX spec Appendix G', - dict(mode='EAX', nonce='1A8C98DCD73D38393B2BF1569DEEFC19') - ), - - ( '54b9f04e6a09189a|1bda122bce8a8dbaf1877d962b8592dd2d56', - '54b9f04e6a09189a|2ec47b2c4954a489afc7ba4897edcdae8cc3|3b60450599bd02c96382902aef7f832a', - '5fff20cafab119ca2fc73549e20f5b0d', - 'EAX spec Appendix G', - dict(mode='EAX', nonce='DDE59B97D722156D4D9AFF2BC7559826') - ), - - ( '899a175897561d7e|6cf36720872b8513f6eab1a8a44438d5ef11', - '899a175897561d7e|0de18fd0fdd91e7af19f1d8ee8733938b1e8|e7f6d2231618102fdb7fe55ff1991700', - 'a4a4782bcffd3ec5e7ef6d8c34a56123', - 'EAX spec Appendix G', - dict(mode='EAX', nonce='B781FCF2F75FA5A8DE97A9CA48E522EC') - ), - - ( '126735fcc320d25a|ca40d7446e545ffaed3bd12a740a659ffbbb3ceab7', - '126735fcc320d25a|cb8920f87a6c75cff39627b56e3ed197c552d295a7|cfc46afc253b4652b1af3795b124ab6e', - '8395fcf1e95bebd697bd010bc766aac3', - 'EAX spec Appendix G', - dict(mode='EAX', nonce='22E7ADD93CFC6393C57EC0B3C17D6B44') - ), - - # Test vectors for SIV taken from RFC5297 - # This is a list of tuples with 5 items: - # - # 1. Header + '|' + plaintext - # 2. Header + '|' + ciphertext + '|' + MAC - # 3. AES-128 key - # 4. Description - # 5. Dictionary of parameters to be passed to AES.new(). - # It must include the nonce. - # - # A "Header" is a dash ('-') separated sequece of components. - # - ( '101112131415161718191a1b1c1d1e1f2021222324252627|112233445566778899aabbccddee', - '101112131415161718191a1b1c1d1e1f2021222324252627|40c02b9690c4dc04daef7f6afe5c|' + - '85632d07c6e8f37f950acd320a2ecc93', - 'fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff', - 'RFC5297 A.1', - dict(mode='SIV', nonce=None) - ), - - ( '00112233445566778899aabbccddeeffdeaddadadeaddadaffeeddccbbaa9988' + - '7766554433221100-102030405060708090a0|' + - '7468697320697320736f6d6520706c61696e7465787420746f20656e63727970' + - '74207573696e67205349562d414553', - - '00112233445566778899aabbccddeeffdeaddadadeaddadaffeeddccbbaa9988' + - '7766554433221100-102030405060708090a0|' + - 'cb900f2fddbe404326601965c889bf17dba77ceb094fa663b7a3f748ba8af829' + - 'ea64ad544a272e9c485b62a3fd5c0d|' + - '7bdb6e3b432667eb06f4d14bff2fbd0f', - - '7f7e7d7c7b7a79787776757473727170404142434445464748494a4b4c4d4e4f', - 'RFC5297 A.2', - dict(mode='SIV', nonce='09f911029d74e35bd84156c5635688c0') - ), - - # Test vectors for GCM taken from - # http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-revised-spec.pdf - # This is a list of tuples with 5 items: - # - # 1. Header + '|' + plaintext - # 2. Header + '|' + ciphertext + '|' + MAC - # 3. AES-128 key - # 4. Description - # 5. Dictionary of parameters to be passed to AES.new(). - # It must include the nonce. - # - ( '|', - '||58e2fccefa7e3061367f1d57a4e7455a', - '00000000000000000000000000000000', - 'GCM Test Case 1', - dict(mode='GCM', nonce='000000000000000000000000') - ), - - ( '|00000000000000000000000000000000', - '|0388dace60b6a392f328c2b971b2fe78|ab6e47d42cec13bdf53a67b21257bddf', - '00000000000000000000000000000000', - 'GCM Test Case 2', - dict(mode='GCM', nonce='000000000000000000000000') - ), - - ( '|d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a72' + - '1c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255', - '|42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e' + - '21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091473f5985|' + - '4d5c2af327cd64a62cf35abd2ba6fab4', - 'feffe9928665731c6d6a8f9467308308', - 'GCM Test Case 3', - dict(mode='GCM', nonce='cafebabefacedbaddecaf888') - ), - - ( 'feedfacedeadbeeffeedfacedeadbeefabaddad2|' + - 'd9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a72' + - '1c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39', - 'feedfacedeadbeeffeedfacedeadbeefabaddad2|' + - '42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e' + - '21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091|' + - '5bc94fbc3221a5db94fae95ae7121a47', - 'feffe9928665731c6d6a8f9467308308', - 'GCM Test Case 4', - dict(mode='GCM', nonce='cafebabefacedbaddecaf888') - ), - - ( 'feedfacedeadbeeffeedfacedeadbeefabaddad2|' + - 'd9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a72' + - '1c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39', - 'feedfacedeadbeeffeedfacedeadbeefabaddad2|' + - '61353b4c2806934a777ff51fa22a4755699b2a714fcdc6f83766e5f97b6c7423' + - '73806900e49f24b22b097544d4896b424989b5e1ebac0f07c23f4598|' + - '3612d2e79e3b0785561be14aaca2fccb', - 'feffe9928665731c6d6a8f9467308308', - 'GCM Test Case 5', - dict(mode='GCM', nonce='cafebabefacedbad') - ), - - ( 'feedfacedeadbeeffeedfacedeadbeefabaddad2|' + - 'd9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a72' + - '1c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39', - 'feedfacedeadbeeffeedfacedeadbeefabaddad2|' + - '8ce24998625615b603a033aca13fb894be9112a5c3a211a8ba262a3cca7e2ca7' + - '01e4a9a4fba43c90ccdcb281d48c7c6fd62875d2aca417034c34aee5|' + - '619cc5aefffe0bfa462af43c1699d050', - 'feffe9928665731c6d6a8f9467308308', - 'GCM Test Case 6', - dict(mode='GCM', nonce='9313225df88406e555909c5aff5269aa'+ - '6a7a9538534f7da1e4c303d2a318a728c3c0c95156809539fcf0e2429a6b5254'+ - '16aedbf5a0de6a57a637b39b' ) - ), - - ( '|', - '||cd33b28ac773f74ba00ed1f312572435', - '000000000000000000000000000000000000000000000000', - 'GCM Test Case 7', - dict(mode='GCM', nonce='000000000000000000000000') - ), - - ( '|00000000000000000000000000000000', - '|98e7247c07f0fe411c267e4384b0f600|2ff58d80033927ab8ef4d4587514f0fb', - '000000000000000000000000000000000000000000000000', - 'GCM Test Case 8', - dict(mode='GCM', nonce='000000000000000000000000') - ), - - ( '|d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a72' + - '1c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255', - '|3980ca0b3c00e841eb06fac4872a2757859e1ceaa6efd984628593b40ca1e19c' + - '7d773d00c144c525ac619d18c84a3f4718e2448b2fe324d9ccda2710acade256|' + - '9924a7c8587336bfb118024db8674a14', - 'feffe9928665731c6d6a8f9467308308feffe9928665731c', - 'GCM Test Case 9', - dict(mode='GCM', nonce='cafebabefacedbaddecaf888') - ), - - ( 'feedfacedeadbeeffeedfacedeadbeefabaddad2|' + - 'd9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a72' + - '1c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39', - 'feedfacedeadbeeffeedfacedeadbeefabaddad2|' + - '3980ca0b3c00e841eb06fac4872a2757859e1ceaa6efd984628593b40ca1e19c' + - '7d773d00c144c525ac619d18c84a3f4718e2448b2fe324d9ccda2710|' + - '2519498e80f1478f37ba55bd6d27618c', - 'feffe9928665731c6d6a8f9467308308feffe9928665731c', - 'GCM Test Case 10', - dict(mode='GCM', nonce='cafebabefacedbaddecaf888') - ), - - ( 'feedfacedeadbeeffeedfacedeadbeefabaddad2|' + - 'd9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a72' + - '1c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39', - 'feedfacedeadbeeffeedfacedeadbeefabaddad2|' + - '0f10f599ae14a154ed24b36e25324db8c566632ef2bbb34f8347280fc4507057' + - 'fddc29df9a471f75c66541d4d4dad1c9e93a19a58e8b473fa0f062f7|' + - '65dcc57fcf623a24094fcca40d3533f8', - 'feffe9928665731c6d6a8f9467308308feffe9928665731c', - 'GCM Test Case 11', - dict(mode='GCM', nonce='cafebabefacedbad') - ), - - ( 'feedfacedeadbeeffeedfacedeadbeefabaddad2|' + - 'd9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a72' + - '1c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39', - 'feedfacedeadbeeffeedfacedeadbeefabaddad2|' + - 'd27e88681ce3243c4830165a8fdcf9ff1de9a1d8e6b447ef6ef7b79828666e45' + - '81e79012af34ddd9e2f037589b292db3e67c036745fa22e7e9b7373b|' + - 'dcf566ff291c25bbb8568fc3d376a6d9', - 'feffe9928665731c6d6a8f9467308308feffe9928665731c', - 'GCM Test Case 12', - dict(mode='GCM', nonce='9313225df88406e555909c5aff5269aa'+ - '6a7a9538534f7da1e4c303d2a318a728c3c0c95156809539fcf0e2429a6b5254'+ - '16aedbf5a0de6a57a637b39b' ) - ), - - ( '|', - '||530f8afbc74536b9a963b4f1c4cb738b', - '0000000000000000000000000000000000000000000000000000000000000000', - 'GCM Test Case 13', - dict(mode='GCM', nonce='000000000000000000000000') - ), - - ( '|00000000000000000000000000000000', - '|cea7403d4d606b6e074ec5d3baf39d18|d0d1c8a799996bf0265b98b5d48ab919', - '0000000000000000000000000000000000000000000000000000000000000000', - 'GCM Test Case 14', - dict(mode='GCM', nonce='000000000000000000000000') - ), - - ( '|d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a72' + - '1c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255', - '|522dc1f099567d07f47f37a32a84427d643a8cdcbfe5c0c97598a2bd2555d1aa' + - '8cb08e48590dbb3da7b08b1056828838c5f61e6393ba7a0abcc9f662898015ad|' + - 'b094dac5d93471bdec1a502270e3cc6c', - 'feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308', - 'GCM Test Case 15', - dict(mode='GCM', nonce='cafebabefacedbaddecaf888') - ), - - ( 'feedfacedeadbeeffeedfacedeadbeefabaddad2|' + - 'd9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a72' + - '1c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39', - 'feedfacedeadbeeffeedfacedeadbeefabaddad2|' + - '522dc1f099567d07f47f37a32a84427d643a8cdcbfe5c0c97598a2bd2555d1aa' + - '8cb08e48590dbb3da7b08b1056828838c5f61e6393ba7a0abcc9f662|' + - '76fc6ece0f4e1768cddf8853bb2d551b', - 'feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308', - 'GCM Test Case 16', - dict(mode='GCM', nonce='cafebabefacedbaddecaf888') - ), - - ( 'feedfacedeadbeeffeedfacedeadbeefabaddad2|' + - 'd9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a72' + - '1c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39', - 'feedfacedeadbeeffeedfacedeadbeefabaddad2|' + - 'c3762df1ca787d32ae47c13bf19844cbaf1ae14d0b976afac52ff7d79bba9de0' + - 'feb582d33934a4f0954cc2363bc73f7862ac430e64abe499f47c9b1f|' + - '3a337dbf46a792c45e454913fe2ea8f2', - 'feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308', - 'GCM Test Case 17', - dict(mode='GCM', nonce='cafebabefacedbad') - ), - - ( 'feedfacedeadbeeffeedfacedeadbeefabaddad2|' + - 'd9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a72' + - '1c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39', - 'feedfacedeadbeeffeedfacedeadbeefabaddad2|' + - '5a8def2f0c9e53f1f75d7853659e2a20eeb2b22aafde6419a058ab4f6f746bf4' + - '0fc0c3b780f244452da3ebf1c5d82cdea2418997200ef82e44ae7e3f|' + - 'a44a8266ee1c8eb0c8b5d4cf5ae9f19a', - 'feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308', - 'GCM Test Case 18', - dict(mode='GCM', nonce='9313225df88406e555909c5aff5269aa'+ - '6a7a9538534f7da1e4c303d2a318a728c3c0c95156809539fcf0e2429a6b5254'+ - '16aedbf5a0de6a57a637b39b' ) - ), ] def get_tests(config={}): from Crypto.Cipher import AES - from Crypto.Util import cpuid from common import make_block_tests - - tests = make_block_tests(AES, "AES", test_data, {'use_aesni': False}) - if cpuid.have_aes_ni(): - # Run tests with AES-NI instructions if they are available. - tests += make_block_tests(AES, "AESNI", test_data, {'use_aesni': True}) - return tests + return make_block_tests(AES, "AES", test_data) if __name__ == '__main__': import unittest diff --git a/modules/Crypto/SelfTest/Cipher/test_ARC4.py b/modules/Crypto/SelfTest/Cipher/test_ARC4.py index 801d2cb..4e039d1 100644 --- a/modules/Crypto/SelfTest/Cipher/test_ARC4.py +++ b/modules/Crypto/SelfTest/Cipher/test_ARC4.py @@ -27,10 +27,6 @@ __revision__ = "$Id$" from Crypto.Util.py3compat import * -from Crypto.SelfTest.st_common import * -from binascii import unhexlify - -from Crypto.Cipher import ARC4 # This is a list of (plaintext, ciphertext, key[, description]) tuples. test_data = [ @@ -72,382 +68,10 @@ test_data = [ "Test vector 4"), ] -class RFC6229_Tests(unittest.TestCase): - # Test vectors from RFC 6229. Each test vector is a tuple with two items: - # the ARC4 key and a dictionary. The dictionary has keystream offsets as keys - # and the 16-byte keystream starting at the relevant offset as value. - rfc6229_data = [ - # Page 3 - ( - '0102030405', - { - 0: 'b2 39 63 05 f0 3d c0 27 cc c3 52 4a 0a 11 18 a8', - 16: '69 82 94 4f 18 fc 82 d5 89 c4 03 a4 7a 0d 09 19', - 240: '28 cb 11 32 c9 6c e2 86 42 1d ca ad b8 b6 9e ae', - 256: '1c fc f6 2b 03 ed db 64 1d 77 df cf 7f 8d 8c 93', - 496: '42 b7 d0 cd d9 18 a8 a3 3d d5 17 81 c8 1f 40 41', - 512: '64 59 84 44 32 a7 da 92 3c fb 3e b4 98 06 61 f6', - 752: 'ec 10 32 7b de 2b ee fd 18 f9 27 76 80 45 7e 22', - 768: 'eb 62 63 8d 4f 0b a1 fe 9f ca 20 e0 5b f8 ff 2b', - 1008:'45 12 90 48 e6 a0 ed 0b 56 b4 90 33 8f 07 8d a5', - 1024:'30 ab bc c7 c2 0b 01 60 9f 23 ee 2d 5f 6b b7 df', - 1520:'32 94 f7 44 d8 f9 79 05 07 e7 0f 62 e5 bb ce ea', - 1536:'d8 72 9d b4 18 82 25 9b ee 4f 82 53 25 f5 a1 30', - 2032:'1e b1 4a 0c 13 b3 bf 47 fa 2a 0b a9 3a d4 5b 8b', - 2048:'cc 58 2f 8b a9 f2 65 e2 b1 be 91 12 e9 75 d2 d7', - 3056:'f2 e3 0f 9b d1 02 ec bf 75 aa ad e9 bc 35 c4 3c', - 3072:'ec 0e 11 c4 79 dc 32 9d c8 da 79 68 fe 96 56 81', - 4080:'06 83 26 a2 11 84 16 d2 1f 9d 04 b2 cd 1c a0 50', - 4096:'ff 25 b5 89 95 99 67 07 e5 1f bd f0 8b 34 d8 75' - } - ), - # Page 4 - ( - '01020304050607', - { - 0: '29 3f 02 d4 7f 37 c9 b6 33 f2 af 52 85 fe b4 6b', - 16: 'e6 20 f1 39 0d 19 bd 84 e2 e0 fd 75 20 31 af c1', - 240: '91 4f 02 53 1c 92 18 81 0d f6 0f 67 e3 38 15 4c', - 256: 'd0 fd b5 83 07 3c e8 5a b8 39 17 74 0e c0 11 d5', - 496: '75 f8 14 11 e8 71 cf fa 70 b9 0c 74 c5 92 e4 54', - 512: '0b b8 72 02 93 8d ad 60 9e 87 a5 a1 b0 79 e5 e4', - 752: 'c2 91 12 46 b6 12 e7 e7 b9 03 df ed a1 da d8 66', - 768: '32 82 8f 91 50 2b 62 91 36 8d e8 08 1d e3 6f c2', - 1008:'f3 b9 a7 e3 b2 97 bf 9a d8 04 51 2f 90 63 ef f1', - 1024:'8e cb 67 a9 ba 1f 55 a5 a0 67 e2 b0 26 a3 67 6f', - 1520:'d2 aa 90 2b d4 2d 0d 7c fd 34 0c d4 58 10 52 9f', - 1536:'78 b2 72 c9 6e 42 ea b4 c6 0b d9 14 e3 9d 06 e3', - 2032:'f4 33 2f d3 1a 07 93 96 ee 3c ee 3f 2a 4f f0 49', - 2048:'05 45 97 81 d4 1f da 7f 30 c1 be 7e 12 46 c6 23', - 3056:'ad fd 38 68 b8 e5 14 85 d5 e6 10 01 7e 3d d6 09', - 3072:'ad 26 58 1c 0c 5b e4 5f 4c ea 01 db 2f 38 05 d5', - 4080:'f3 17 2c ef fc 3b 3d 99 7c 85 cc d5 af 1a 95 0c', - 4096:'e7 4b 0b 97 31 22 7f d3 7c 0e c0 8a 47 dd d8 b8' - } - ), - ( - '0102030405060708', - { - 0: '97 ab 8a 1b f0 af b9 61 32 f2 f6 72 58 da 15 a8', - 16: '82 63 ef db 45 c4 a1 86 84 ef 87 e6 b1 9e 5b 09', - 240: '96 36 eb c9 84 19 26 f4 f7 d1 f3 62 bd df 6e 18', - 256: 'd0 a9 90 ff 2c 05 fe f5 b9 03 73 c9 ff 4b 87 0a', - 496: '73 23 9f 1d b7 f4 1d 80 b6 43 c0 c5 25 18 ec 63', - 512: '16 3b 31 99 23 a6 bd b4 52 7c 62 61 26 70 3c 0f', - 752: '49 d6 c8 af 0f 97 14 4a 87 df 21 d9 14 72 f9 66', - 768: '44 17 3a 10 3b 66 16 c5 d5 ad 1c ee 40 c8 63 d0', - 1008:'27 3c 9c 4b 27 f3 22 e4 e7 16 ef 53 a4 7d e7 a4', - 1024:'c6 d0 e7 b2 26 25 9f a9 02 34 90 b2 61 67 ad 1d', - 1520:'1f e8 98 67 13 f0 7c 3d 9a e1 c1 63 ff 8c f9 d3', - 1536:'83 69 e1 a9 65 61 0b e8 87 fb d0 c7 91 62 aa fb', - 2032:'0a 01 27 ab b4 44 84 b9 fb ef 5a bc ae 1b 57 9f', - 2048:'c2 cd ad c6 40 2e 8e e8 66 e1 f3 7b db 47 e4 2c', - 3056:'26 b5 1e a3 7d f8 e1 d6 f7 6f c3 b6 6a 74 29 b3', - 3072:'bc 76 83 20 5d 4f 44 3d c1 f2 9d da 33 15 c8 7b', - 4080:'d5 fa 5a 34 69 d2 9a aa f8 3d 23 58 9d b8 c8 5b', - 4096:'3f b4 6e 2c 8f 0f 06 8e dc e8 cd cd 7d fc 58 62' - } - ), - # Page 5 - ( - '0102030405060708090a', - { - 0: 'ed e3 b0 46 43 e5 86 cc 90 7d c2 18 51 70 99 02', - 16: '03 51 6b a7 8f 41 3b eb 22 3a a5 d4 d2 df 67 11', - 240: '3c fd 6c b5 8e e0 fd de 64 01 76 ad 00 00 04 4d', - 256: '48 53 2b 21 fb 60 79 c9 11 4c 0f fd 9c 04 a1 ad', - 496: '3e 8c ea 98 01 71 09 97 90 84 b1 ef 92 f9 9d 86', - 512: 'e2 0f b4 9b db 33 7e e4 8b 8d 8d c0 f4 af ef fe', - 752: '5c 25 21 ea cd 79 66 f1 5e 05 65 44 be a0 d3 15', - 768: 'e0 67 a7 03 19 31 a2 46 a6 c3 87 5d 2f 67 8a cb', - 1008:'a6 4f 70 af 88 ae 56 b6 f8 75 81 c0 e2 3e 6b 08', - 1024:'f4 49 03 1d e3 12 81 4e c6 f3 19 29 1f 4a 05 16', - 1520:'bd ae 85 92 4b 3c b1 d0 a2 e3 3a 30 c6 d7 95 99', - 1536:'8a 0f ed db ac 86 5a 09 bc d1 27 fb 56 2e d6 0a', - 2032:'b5 5a 0a 5b 51 a1 2a 8b e3 48 99 c3 e0 47 51 1a', - 2048:'d9 a0 9c ea 3c e7 5f e3 96 98 07 03 17 a7 13 39', - 3056:'55 22 25 ed 11 77 f4 45 84 ac 8c fa 6c 4e b5 fc', - 3072:'7e 82 cb ab fc 95 38 1b 08 09 98 44 21 29 c2 f8', - 4080:'1f 13 5e d1 4c e6 0a 91 36 9d 23 22 be f2 5e 3c', - 4096:'08 b6 be 45 12 4a 43 e2 eb 77 95 3f 84 dc 85 53' - } - ), - ( - '0102030405060708090a0b0c0d0e0f10', - { - 0: '9a c7 cc 9a 60 9d 1e f7 b2 93 28 99 cd e4 1b 97', - 16: '52 48 c4 95 90 14 12 6a 6e 8a 84 f1 1d 1a 9e 1c', - 240: '06 59 02 e4 b6 20 f6 cc 36 c8 58 9f 66 43 2f 2b', - 256: 'd3 9d 56 6b c6 bc e3 01 07 68 15 15 49 f3 87 3f', - 496: 'b6 d1 e6 c4 a5 e4 77 1c ad 79 53 8d f2 95 fb 11', - 512: 'c6 8c 1d 5c 55 9a 97 41 23 df 1d bc 52 a4 3b 89', - 752: 'c5 ec f8 8d e8 97 fd 57 fe d3 01 70 1b 82 a2 59', - 768: 'ec cb e1 3d e1 fc c9 1c 11 a0 b2 6c 0b c8 fa 4d', - 1008:'e7 a7 25 74 f8 78 2a e2 6a ab cf 9e bc d6 60 65', - 1024:'bd f0 32 4e 60 83 dc c6 d3 ce dd 3c a8 c5 3c 16', - 1520:'b4 01 10 c4 19 0b 56 22 a9 61 16 b0 01 7e d2 97', - 1536:'ff a0 b5 14 64 7e c0 4f 63 06 b8 92 ae 66 11 81', - 2032:'d0 3d 1b c0 3c d3 3d 70 df f9 fa 5d 71 96 3e bd', - 2048:'8a 44 12 64 11 ea a7 8b d5 1e 8d 87 a8 87 9b f5', - 3056:'fa be b7 60 28 ad e2 d0 e4 87 22 e4 6c 46 15 a3', - 3072:'c0 5d 88 ab d5 03 57 f9 35 a6 3c 59 ee 53 76 23', - 4080:'ff 38 26 5c 16 42 c1 ab e8 d3 c2 fe 5e 57 2b f8', - 4096:'a3 6a 4c 30 1a e8 ac 13 61 0c cb c1 22 56 ca cc' - } - ), - # Page 6 - ( - '0102030405060708090a0b0c0d0e0f101112131415161718', - { - 0: '05 95 e5 7f e5 f0 bb 3c 70 6e da c8 a4 b2 db 11', - 16: 'df de 31 34 4a 1a f7 69 c7 4f 07 0a ee 9e 23 26', - 240: 'b0 6b 9b 1e 19 5d 13 d8 f4 a7 99 5c 45 53 ac 05', - 256: '6b d2 37 8e c3 41 c9 a4 2f 37 ba 79 f8 8a 32 ff', - 496: 'e7 0b ce 1d f7 64 5a db 5d 2c 41 30 21 5c 35 22', - 512: '9a 57 30 c7 fc b4 c9 af 51 ff da 89 c7 f1 ad 22', - 752: '04 85 05 5f d4 f6 f0 d9 63 ef 5a b9 a5 47 69 82', - 768: '59 1f c6 6b cd a1 0e 45 2b 03 d4 55 1f 6b 62 ac', - 1008:'27 53 cc 83 98 8a fa 3e 16 88 a1 d3 b4 2c 9a 02', - 1024:'93 61 0d 52 3d 1d 3f 00 62 b3 c2 a3 bb c7 c7 f0', - 1520:'96 c2 48 61 0a ad ed fe af 89 78 c0 3d e8 20 5a', - 1536:'0e 31 7b 3d 1c 73 b9 e9 a4 68 8f 29 6d 13 3a 19', - 2032:'bd f0 e6 c3 cc a5 b5 b9 d5 33 b6 9c 56 ad a1 20', - 2048:'88 a2 18 b6 e2 ec e1 e6 24 6d 44 c7 59 d1 9b 10', - 3056:'68 66 39 7e 95 c1 40 53 4f 94 26 34 21 00 6e 40', - 3072:'32 cb 0a 1e 95 42 c6 b3 b8 b3 98 ab c3 b0 f1 d5', - 4080:'29 a0 b8 ae d5 4a 13 23 24 c6 2e 42 3f 54 b4 c8', - 4096:'3c b0 f3 b5 02 0a 98 b8 2a f9 fe 15 44 84 a1 68' - } - ), - ( - '0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20', - { - 0: 'ea a6 bd 25 88 0b f9 3d 3f 5d 1e 4c a2 61 1d 91', - 16: 'cf a4 5c 9f 7e 71 4b 54 bd fa 80 02 7c b1 43 80', - 240: '11 4a e3 44 de d7 1b 35 f2 e6 0f eb ad 72 7f d8', - 256: '02 e1 e7 05 6b 0f 62 39 00 49 64 22 94 3e 97 b6', - 496: '91 cb 93 c7 87 96 4e 10 d9 52 7d 99 9c 6f 93 6b', - 512: '49 b1 8b 42 f8 e8 36 7c be b5 ef 10 4b a1 c7 cd', - 752: '87 08 4b 3b a7 00 ba de 95 56 10 67 27 45 b3 74', - 768: 'e7 a7 b9 e9 ec 54 0d 5f f4 3b db 12 79 2d 1b 35', - 1008:'c7 99 b5 96 73 8f 6b 01 8c 76 c7 4b 17 59 bd 90', - 1024:'7f ec 5b fd 9f 9b 89 ce 65 48 30 90 92 d7 e9 58', - 1520:'40 f2 50 b2 6d 1f 09 6a 4a fd 4c 34 0a 58 88 15', - 1536:'3e 34 13 5c 79 db 01 02 00 76 76 51 cf 26 30 73', - 2032:'f6 56 ab cc f8 8d d8 27 02 7b 2c e9 17 d4 64 ec', - 2048:'18 b6 25 03 bf bc 07 7f ba bb 98 f2 0d 98 ab 34', - 3056:'8a ed 95 ee 5b 0d cb fb ef 4e b2 1d 3a 3f 52 f9', - 3072:'62 5a 1a b0 0e e3 9a 53 27 34 6b dd b0 1a 9c 18', - 4080:'a1 3a 7c 79 c7 e1 19 b5 ab 02 96 ab 28 c3 00 b9', - 4096:'f3 e4 c0 a2 e0 2d 1d 01 f7 f0 a7 46 18 af 2b 48' - } - ), - # Page 7 - ( - '833222772a', - { - 0: '80 ad 97 bd c9 73 df 8a 2e 87 9e 92 a4 97 ef da', - 16: '20 f0 60 c2 f2 e5 12 65 01 d3 d4 fe a1 0d 5f c0', - 240: 'fa a1 48 e9 90 46 18 1f ec 6b 20 85 f3 b2 0e d9', - 256: 'f0 da f5 ba b3 d5 96 83 98 57 84 6f 73 fb fe 5a', - 496: '1c 7e 2f c4 63 92 32 fe 29 75 84 b2 96 99 6b c8', - 512: '3d b9 b2 49 40 6c c8 ed ff ac 55 cc d3 22 ba 12', - 752: 'e4 f9 f7 e0 06 61 54 bb d1 25 b7 45 56 9b c8 97', - 768: '75 d5 ef 26 2b 44 c4 1a 9c f6 3a e1 45 68 e1 b9', - 1008:'6d a4 53 db f8 1e 82 33 4a 3d 88 66 cb 50 a1 e3', - 1024:'78 28 d0 74 11 9c ab 5c 22 b2 94 d7 a9 bf a0 bb', - 1520:'ad b8 9c ea 9a 15 fb e6 17 29 5b d0 4b 8c a0 5c', - 1536:'62 51 d8 7f d4 aa ae 9a 7e 4a d5 c2 17 d3 f3 00', - 2032:'e7 11 9b d6 dd 9b 22 af e8 f8 95 85 43 28 81 e2', - 2048:'78 5b 60 fd 7e c4 e9 fc b6 54 5f 35 0d 66 0f ab', - 3056:'af ec c0 37 fd b7 b0 83 8e b3 d7 0b cd 26 83 82', - 3072:'db c1 a7 b4 9d 57 35 8c c9 fa 6d 61 d7 3b 7c f0', - 4080:'63 49 d1 26 a3 7a fc ba 89 79 4f 98 04 91 4f dc', - 4096:'bf 42 c3 01 8c 2f 7c 66 bf de 52 49 75 76 81 15' - } - ), - ( - '1910833222772a', - { - 0: 'bc 92 22 db d3 27 4d 8f c6 6d 14 cc bd a6 69 0b', - 16: '7a e6 27 41 0c 9a 2b e6 93 df 5b b7 48 5a 63 e3', - 240: '3f 09 31 aa 03 de fb 30 0f 06 01 03 82 6f 2a 64', - 256: 'be aa 9e c8 d5 9b b6 81 29 f3 02 7c 96 36 11 81', - 496: '74 e0 4d b4 6d 28 64 8d 7d ee 8a 00 64 b0 6c fe', - 512: '9b 5e 81 c6 2f e0 23 c5 5b e4 2f 87 bb f9 32 b8', - 752: 'ce 17 8f c1 82 6e fe cb c1 82 f5 79 99 a4 61 40', - 768: '8b df 55 cd 55 06 1c 06 db a6 be 11 de 4a 57 8a', - 1008:'62 6f 5f 4d ce 65 25 01 f3 08 7d 39 c9 2c c3 49', - 1024:'42 da ac 6a 8f 9a b9 a7 fd 13 7c 60 37 82 56 82', - 1520:'cc 03 fd b7 91 92 a2 07 31 2f 53 f5 d4 dc 33 d9', - 1536:'f7 0f 14 12 2a 1c 98 a3 15 5d 28 b8 a0 a8 a4 1d', - 2032:'2a 3a 30 7a b2 70 8a 9c 00 fe 0b 42 f9 c2 d6 a1', - 2048:'86 26 17 62 7d 22 61 ea b0 b1 24 65 97 ca 0a e9', - 3056:'55 f8 77 ce 4f 2e 1d db bf 8e 13 e2 cd e0 fd c8', - 3072:'1b 15 56 cb 93 5f 17 33 37 70 5f bb 5d 50 1f c1', - 4080:'ec d0 e9 66 02 be 7f 8d 50 92 81 6c cc f2 c2 e9', - 4096:'02 78 81 fa b4 99 3a 1c 26 20 24 a9 4f ff 3f 61' - } - ), - # Page 8 - ( - '641910833222772a', - { - 0: 'bb f6 09 de 94 13 17 2d 07 66 0c b6 80 71 69 26', - 16: '46 10 1a 6d ab 43 11 5d 6c 52 2b 4f e9 36 04 a9', - 240: 'cb e1 ff f2 1c 96 f3 ee f6 1e 8f e0 54 2c bd f0', - 256: '34 79 38 bf fa 40 09 c5 12 cf b4 03 4b 0d d1 a7', - 496: '78 67 a7 86 d0 0a 71 47 90 4d 76 dd f1 e5 20 e3', - 512: '8d 3e 9e 1c ae fc cc b3 fb f8 d1 8f 64 12 0b 32', - 752: '94 23 37 f8 fd 76 f0 fa e8 c5 2d 79 54 81 06 72', - 768: 'b8 54 8c 10 f5 16 67 f6 e6 0e 18 2f a1 9b 30 f7', - 1008:'02 11 c7 c6 19 0c 9e fd 12 37 c3 4c 8f 2e 06 c4', - 1024:'bd a6 4f 65 27 6d 2a ac b8 f9 02 12 20 3a 80 8e', - 1520:'bd 38 20 f7 32 ff b5 3e c1 93 e7 9d 33 e2 7c 73', - 1536:'d0 16 86 16 86 19 07 d4 82 e3 6c da c8 cf 57 49', - 2032:'97 b0 f0 f2 24 b2 d2 31 71 14 80 8f b0 3a f7 a0', - 2048:'e5 96 16 e4 69 78 79 39 a0 63 ce ea 9a f9 56 d1', - 3056:'c4 7e 0d c1 66 09 19 c1 11 01 20 8f 9e 69 aa 1f', - 3072:'5a e4 f1 28 96 b8 37 9a 2a ad 89 b5 b5 53 d6 b0', - 4080:'6b 6b 09 8d 0c 29 3b c2 99 3d 80 bf 05 18 b6 d9', - 4096:'81 70 cc 3c cd 92 a6 98 62 1b 93 9d d3 8f e7 b9' - } - ), - ( - '8b37641910833222772a', - { - 0: 'ab 65 c2 6e dd b2 87 60 0d b2 fd a1 0d 1e 60 5c', - 16: 'bb 75 90 10 c2 96 58 f2 c7 2d 93 a2 d1 6d 29 30', - 240: 'b9 01 e8 03 6e d1 c3 83 cd 3c 4c 4d d0 a6 ab 05', - 256: '3d 25 ce 49 22 92 4c 55 f0 64 94 33 53 d7 8a 6c', - 496: '12 c1 aa 44 bb f8 7e 75 e6 11 f6 9b 2c 38 f4 9b', - 512: '28 f2 b3 43 4b 65 c0 98 77 47 00 44 c6 ea 17 0d', - 752: 'bd 9e f8 22 de 52 88 19 61 34 cf 8a f7 83 93 04', - 768: '67 55 9c 23 f0 52 15 84 70 a2 96 f7 25 73 5a 32', - 1008:'8b ab 26 fb c2 c1 2b 0f 13 e2 ab 18 5e ab f2 41', - 1024:'31 18 5a 6d 69 6f 0c fa 9b 42 80 8b 38 e1 32 a2', - 1520:'56 4d 3d ae 18 3c 52 34 c8 af 1e 51 06 1c 44 b5', - 1536:'3c 07 78 a7 b5 f7 2d 3c 23 a3 13 5c 7d 67 b9 f4', - 2032:'f3 43 69 89 0f cf 16 fb 51 7d ca ae 44 63 b2 dd', - 2048:'02 f3 1c 81 e8 20 07 31 b8 99 b0 28 e7 91 bf a7', - 3056:'72 da 64 62 83 22 8c 14 30 08 53 70 17 95 61 6f', - 3072:'4e 0a 8c 6f 79 34 a7 88 e2 26 5e 81 d6 d0 c8 f4', - 4080:'43 8d d5 ea fe a0 11 1b 6f 36 b4 b9 38 da 2a 68', - 4096:'5f 6b fc 73 81 58 74 d9 71 00 f0 86 97 93 57 d8' - } - ), - # Page 9 - ( - 'ebb46227c6cc8b37641910833222772a', - { - 0: '72 0c 94 b6 3e df 44 e1 31 d9 50 ca 21 1a 5a 30', - 16: 'c3 66 fd ea cf 9c a8 04 36 be 7c 35 84 24 d2 0b', - 240: 'b3 39 4a 40 aa bf 75 cb a4 22 82 ef 25 a0 05 9f', - 256: '48 47 d8 1d a4 94 2d bc 24 9d ef c4 8c 92 2b 9f', - 496: '08 12 8c 46 9f 27 53 42 ad da 20 2b 2b 58 da 95', - 512: '97 0d ac ef 40 ad 98 72 3b ac 5d 69 55 b8 17 61', - 752: '3c b8 99 93 b0 7b 0c ed 93 de 13 d2 a1 10 13 ac', - 768: 'ef 2d 67 6f 15 45 c2 c1 3d c6 80 a0 2f 4a db fe', - 1008:'b6 05 95 51 4f 24 bc 9f e5 22 a6 ca d7 39 36 44', - 1024:'b5 15 a8 c5 01 17 54 f5 90 03 05 8b db 81 51 4e', - 1520:'3c 70 04 7e 8c bc 03 8e 3b 98 20 db 60 1d a4 95', - 1536:'11 75 da 6e e7 56 de 46 a5 3e 2b 07 56 60 b7 70', - 2032:'00 a5 42 bb a0 21 11 cc 2c 65 b3 8e bd ba 58 7e', - 2048:'58 65 fd bb 5b 48 06 41 04 e8 30 b3 80 f2 ae de', - 3056:'34 b2 1a d2 ad 44 e9 99 db 2d 7f 08 63 f0 d9 b6', - 3072:'84 a9 21 8f c3 6e 8a 5f 2c cf be ae 53 a2 7d 25', - 4080:'a2 22 1a 11 b8 33 cc b4 98 a5 95 40 f0 54 5f 4a', - 4096:'5b be b4 78 7d 59 e5 37 3f db ea 6c 6f 75 c2 9b' - } - ), - ( - 'c109163908ebe51debb46227c6cc8b37641910833222772a', - { - 0: '54 b6 4e 6b 5a 20 b5 e2 ec 84 59 3d c7 98 9d a7', - 16: 'c1 35 ee e2 37 a8 54 65 ff 97 dc 03 92 4f 45 ce', - 240: 'cf cc 92 2f b4 a1 4a b4 5d 61 75 aa bb f2 d2 01', - 256: '83 7b 87 e2 a4 46 ad 0e f7 98 ac d0 2b 94 12 4f', - 496: '17 a6 db d6 64 92 6a 06 36 b3 f4 c3 7a 4f 46 94', - 512: '4a 5f 9f 26 ae ee d4 d4 a2 5f 63 2d 30 52 33 d9', - 752: '80 a3 d0 1e f0 0c 8e 9a 42 09 c1 7f 4e eb 35 8c', - 768: 'd1 5e 7d 5f fa aa bc 02 07 bf 20 0a 11 77 93 a2', - 1008:'34 96 82 bf 58 8e aa 52 d0 aa 15 60 34 6a ea fa', - 1024:'f5 85 4c db 76 c8 89 e3 ad 63 35 4e 5f 72 75 e3', - 1520:'53 2c 7c ec cb 39 df 32 36 31 84 05 a4 b1 27 9c', - 1536:'ba ef e6 d9 ce b6 51 84 22 60 e0 d1 e0 5e 3b 90', - 2032:'e8 2d 8c 6d b5 4e 3c 63 3f 58 1c 95 2b a0 42 07', - 2048:'4b 16 e5 0a bd 38 1b d7 09 00 a9 cd 9a 62 cb 23', - 3056:'36 82 ee 33 bd 14 8b d9 f5 86 56 cd 8f 30 d9 fb', - 3072:'1e 5a 0b 84 75 04 5d 9b 20 b2 62 86 24 ed fd 9e', - 4080:'63 ed d6 84 fb 82 62 82 fe 52 8f 9c 0e 92 37 bc', - 4096:'e4 dd 2e 98 d6 96 0f ae 0b 43 54 54 56 74 33 91' - } - ), - # Page 10 - ( - '1ada31d5cf688221c109163908ebe51debb46227c6cc8b37641910833222772a', - { - 0: 'dd 5b cb 00 18 e9 22 d4 94 75 9d 7c 39 5d 02 d3', - 16: 'c8 44 6f 8f 77 ab f7 37 68 53 53 eb 89 a1 c9 eb', - 240: 'af 3e 30 f9 c0 95 04 59 38 15 15 75 c3 fb 90 98', - 256: 'f8 cb 62 74 db 99 b8 0b 1d 20 12 a9 8e d4 8f 0e', - 496: '25 c3 00 5a 1c b8 5d e0 76 25 98 39 ab 71 98 ab', - 512: '9d cb c1 83 e8 cb 99 4b 72 7b 75 be 31 80 76 9c', - 752: 'a1 d3 07 8d fa 91 69 50 3e d9 d4 49 1d ee 4e b2', - 768: '85 14 a5 49 58 58 09 6f 59 6e 4b cd 66 b1 06 65', - 1008:'5f 40 d5 9e c1 b0 3b 33 73 8e fa 60 b2 25 5d 31', - 1024:'34 77 c7 f7 64 a4 1b ac ef f9 0b f1 4f 92 b7 cc', - 1520:'ac 4e 95 36 8d 99 b9 eb 78 b8 da 8f 81 ff a7 95', - 1536:'8c 3c 13 f8 c2 38 8b b7 3f 38 57 6e 65 b7 c4 46', - 2032:'13 c4 b9 c1 df b6 65 79 ed dd 8a 28 0b 9f 73 16', - 2048:'dd d2 78 20 55 01 26 69 8e fa ad c6 4b 64 f6 6e', - 3056:'f0 8f 2e 66 d2 8e d1 43 f3 a2 37 cf 9d e7 35 59', - 3072:'9e a3 6c 52 55 31 b8 80 ba 12 43 34 f5 7b 0b 70', - 4080:'d5 a3 9e 3d fc c5 02 80 ba c4 a6 b5 aa 0d ca 7d', - 4096:'37 0b 1c 1f e6 55 91 6d 97 fd 0d 47 ca 1d 72 b8' - } - ) - ] - - def test_keystream(self): - for tv in self.rfc6229_data: - key = unhexlify(b((tv[0]))) - cipher = ARC4.new(key) - count = 0 - for offset in range(0,4096+1,16): - ct = cipher.encrypt(b('\x00')*16) - expected = tv[1].get(offset) - if expected: - expected = unhexlify(b(expected.replace(" ",''))) - self.assertEquals(ct, expected) - count += 1 - self.assertEqual(count, len(tv[1])) - -class Drop_Tests(unittest.TestCase): - key = b('\xAA')*16 - data = b('\x00')*5000 - - def setUp(self): - self.cipher = ARC4.new(self.key) - - def test_drop256_encrypt(self): - cipher_drop = ARC4.new(self.key, 256) - ct_drop = cipher_drop.encrypt(self.data[:16]) - ct = self.cipher.encrypt(self.data)[256:256+16] - self.assertEquals(ct_drop, ct) - - def test_drop256_decrypt(self): - cipher_drop = ARC4.new(self.key, 256) - pt_drop = cipher_drop.decrypt(self.data[:16]) - pt = self.cipher.decrypt(self.data)[256:256+16] - self.assertEquals(pt_drop, pt) - def get_tests(config={}): + from Crypto.Cipher import ARC4 from common import make_stream_tests - tests = make_stream_tests(ARC4, "ARC4", test_data) - tests += list_test_cases(RFC6229_Tests) - tests += list_test_cases(Drop_Tests) - return tests + return make_stream_tests(ARC4, "ARC4", test_data) if __name__ == '__main__': import unittest diff --git a/modules/Crypto/SelfTest/Cipher/test_pkcs1_oaep.py b/modules/Crypto/SelfTest/Cipher/test_pkcs1_oaep.py index 86c38a3..accca61 100644 --- a/modules/Crypto/SelfTest/Cipher/test_pkcs1_oaep.py +++ b/modules/Crypto/SelfTest/Cipher/test_pkcs1_oaep.py @@ -31,7 +31,7 @@ from Crypto.SelfTest.st_common import list_test_cases, a2b_hex, b2a_hex from Crypto.Util.py3compat import * from Crypto.PublicKey import RSA from Crypto.Cipher import PKCS1_OAEP as PKCS -from Crypto.Hash import MD2,MD5,SHA1,SHA256,RIPEMD160 +from Crypto.Hash import MD2,MD5,SHA as SHA1,SHA256,RIPEMD from Crypto import Random def rws(t): @@ -314,12 +314,11 @@ class PKCS1_OAEP_Tests(unittest.TestCase): # Encrypt/Decrypt messages of length [0..128-2*20-2] for pt_len in xrange(0,128-2*20-2): pt = self.rng(pt_len) - cipher = PKCS.new(self.key1024) - ct = cipher.encrypt(pt) - pt2 = cipher.decrypt(ct) + ct = PKCS.encrypt(pt, self.key1024) + pt2 = PKCS.decrypt(ct, self.key1024) self.assertEqual(pt,pt2) - def testEncryptDecrypt2(self): + def testEncryptDecrypt1(self): # Helper function to monitor what's requested from RNG global asked def localRng(N): @@ -327,7 +326,7 @@ class PKCS1_OAEP_Tests(unittest.TestCase): asked += N return self.rng(N) # Verify that OAEP is friendly to all hashes - for hashmod in (MD2,MD5,SHA1,SHA256,RIPEMD160): + for hashmod in (MD2,MD5,SHA1,SHA256,RIPEMD): # Verify that encrypt() asks for as many random bytes # as the hash output size asked = 0 @@ -338,7 +337,7 @@ class PKCS1_OAEP_Tests(unittest.TestCase): self.assertEqual(cipher.decrypt(ct), pt) self.failUnless(asked > hashmod.digest_size) - def testEncryptDecrypt3(self): + def testEncryptDecrypt2(self): # Verify that OAEP supports labels pt = self.rng(35) xlabel = self.rng(22) @@ -346,7 +345,7 @@ class PKCS1_OAEP_Tests(unittest.TestCase): ct = cipher.encrypt(pt) self.assertEqual(cipher.decrypt(ct), pt) - def testEncryptDecrypt4(self): + def testEncryptDecrypt3(self): # Verify that encrypt() uses the custom MGF global mgfcalls # Helper function to monitor what's requested from MGF diff --git a/modules/Crypto/SelfTest/Hash/__init__.py b/modules/Crypto/SelfTest/Hash/__init__.py index e2d9cd8..bb19f9b 100644 --- a/modules/Crypto/SelfTest/Hash/__init__.py +++ b/modules/Crypto/SelfTest/Hash/__init__.py @@ -28,14 +28,13 @@ __revision__ = "$Id$" def get_tests(config={}): tests = [] - from Crypto.SelfTest.Hash import test_HMAC; tests += test_HMAC.get_tests(config=config) - from Crypto.SelfTest.Hash import test_CMAC; tests += test_CMAC.get_tests(config=config) - from Crypto.SelfTest.Hash import test_MD2; tests += test_MD2.get_tests(config=config) - from Crypto.SelfTest.Hash import test_MD4; tests += test_MD4.get_tests(config=config) - from Crypto.SelfTest.Hash import test_MD5; tests += test_MD5.get_tests(config=config) - from Crypto.SelfTest.Hash import test_RIPEMD160; tests += test_RIPEMD160.get_tests(config=config) - from Crypto.SelfTest.Hash import test_SHA1; tests += test_SHA1.get_tests(config=config) - from Crypto.SelfTest.Hash import test_SHA256; tests += test_SHA256.get_tests(config=config) + from Crypto.SelfTest.Hash import test_HMAC; tests += test_HMAC.get_tests(config=config) + from Crypto.SelfTest.Hash import test_MD2; tests += test_MD2.get_tests(config=config) + from Crypto.SelfTest.Hash import test_MD4; tests += test_MD4.get_tests(config=config) + from Crypto.SelfTest.Hash import test_MD5; tests += test_MD5.get_tests(config=config) + from Crypto.SelfTest.Hash import test_RIPEMD; tests += test_RIPEMD.get_tests(config=config) + from Crypto.SelfTest.Hash import test_SHA; tests += test_SHA.get_tests(config=config) + from Crypto.SelfTest.Hash import test_SHA256; tests += test_SHA256.get_tests(config=config) try: from Crypto.SelfTest.Hash import test_SHA224; tests += test_SHA224.get_tests(config=config) from Crypto.SelfTest.Hash import test_SHA384; tests += test_SHA384.get_tests(config=config) diff --git a/modules/Crypto/SelfTest/Hash/common.py b/modules/Crypto/SelfTest/Hash/common.py index 2b3cbc3..f77fb0f 100644 --- a/modules/Crypto/SelfTest/Hash/common.py +++ b/modules/Crypto/SelfTest/Hash/common.py @@ -29,10 +29,7 @@ __revision__ = "$Id$" import sys import unittest import binascii -import Crypto.Hash from Crypto.Util.py3compat import * -if sys.version_info[0] == 2 and sys.version_info[1] == 1: - from Crypto.Util.py21compat import * # For compatibility with Python 2.1 and Python 2.2 if sys.hexversion < 0x02030000: @@ -43,7 +40,6 @@ if sys.hexversion < 0x02030000: else: dict = dict -from Crypto.Util.strxor import strxor_c class HashDigestSizeSelfTest(unittest.TestCase): @@ -98,27 +94,11 @@ class HashSelfTest(unittest.TestCase): self.assertEqual(self.expected.decode(), out3) # h = .new(data); h.hexdigest() self.assertEqual(self.expected, out4) # h = .new(data); h.digest() - # Verify that the .new() method produces a fresh hash object, except - # for MD5 and SHA1, which are hashlib objects. (But test any .new() - # method that does exist.) - if self.hashmod.__name__ not in ('Crypto.Hash.MD5', 'Crypto.Hash.SHA1') or hasattr(h, 'new'): - h2 = h.new() - h2.update(self.input) - out5 = binascii.b2a_hex(h2.digest()) - self.assertEqual(self.expected, out5) - - # Verify that Crypto.Hash.new(h) produces a fresh hash object - h3 = Crypto.Hash.new(h) - h3.update(self.input) - out6 = binascii.b2a_hex(h3.digest()) - self.assertEqual(self.expected, out6) - - if hasattr(h, 'name'): - # Verify that Crypto.Hash.new(h.name) produces a fresh hash object - h4 = Crypto.Hash.new(h.name) - h4.update(self.input) - out7 = binascii.b2a_hex(h4.digest()) - self.assertEqual(self.expected, out7) + # Verify that new() object method produces a fresh hash object + h2 = h.new() + h2.update(self.input) + out5 = binascii.b2a_hex(h2.digest()) + self.assertEqual(self.expected, out5) class HashTestOID(unittest.TestCase): def __init__(self, hashmod, oid): @@ -127,105 +107,65 @@ class HashTestOID(unittest.TestCase): self.oid = oid def runTest(self): - from Crypto.Signature import PKCS1_v1_5 h = self.hashmod.new() - self.assertEqual(PKCS1_v1_5._HASH_OIDS[h.name], self.oid) - -class HashDocStringTest(unittest.TestCase): - def __init__(self, hashmod): - unittest.TestCase.__init__(self) - self.hashmod = hashmod - - def runTest(self): - docstring = self.hashmod.__doc__ - self.assert_(hasattr(self.hashmod, '__doc__')) - self.assert_(isinstance(self.hashmod.__doc__, str)) - -class GenericHashConstructorTest(unittest.TestCase): - def __init__(self, hashmod): - unittest.TestCase.__init__(self) - self.hashmod = hashmod - - def runTest(self): - obj1 = self.hashmod.new("foo") - obj2 = self.hashmod.new() - obj3 = Crypto.Hash.new(obj1.name, "foo") - obj4 = Crypto.Hash.new(obj1.name) - obj5 = Crypto.Hash.new(obj1, "foo") - obj6 = Crypto.Hash.new(obj1) - self.assert_(isinstance(self.hashmod, obj1)) - self.assert_(isinstance(self.hashmod, obj2)) - self.assert_(isinstance(self.hashmod, obj3)) - self.assert_(isinstance(self.hashmod, obj4)) - self.assert_(isinstance(self.hashmod, obj5)) - self.assert_(isinstance(self.hashmod, obj6)) + if self.oid==None: + try: + raised = 0 + a = h.oid + except AttributeError: + raised = 1 + self.assertEqual(raised,1) + else: + self.assertEqual(h.oid, self.oid) class MACSelfTest(unittest.TestCase): - def __init__(self, module, description, result, input, key, params): + def __init__(self, hashmod, description, expected_dict, input, key, hashmods): unittest.TestCase.__init__(self) - self.module = module - self.result = result + self.hashmod = hashmod + self.expected_dict = expected_dict self.input = input self.key = key - self.params = params + self.hashmods = hashmods self.description = description def shortDescription(self): return self.description def runTest(self): - key = binascii.a2b_hex(b(self.key)) - data = binascii.a2b_hex(b(self.input)) + for hashname in self.expected_dict.keys(): + hashmod = self.hashmods[hashname] + key = binascii.a2b_hex(b(self.key)) + data = binascii.a2b_hex(b(self.input)) - # Strip whitespace from the expected string (which should be in lowercase-hex) - expected = b("".join(self.result.split())) + # Strip whitespace from the expected string (which should be in lowercase-hex) + expected = b("".join(self.expected_dict[hashname].split())) - h = self.module.new(key, **self.params) - h.update(data) - out1_bin = h.digest() - out1 = binascii.b2a_hex(h.digest()) - out2 = h.hexdigest() + h = self.hashmod.new(key, digestmod=hashmod) + h.update(data) + out1 = binascii.b2a_hex(h.digest()) + out2 = h.hexdigest() - # Verify that correct MAC does not raise any exception - h.hexverify(out1) - h.verify(out1_bin) + h = self.hashmod.new(key, data, hashmod) - # Verify that incorrect MAC does raise ValueError exception - wrong_mac = strxor_c(out1_bin, 255) - self.assertRaises(ValueError, h.verify, wrong_mac) - self.assertRaises(ValueError, h.hexverify, "4556") + out3 = h.hexdigest() + out4 = binascii.b2a_hex(h.digest()) - h = self.module.new(key, data, **self.params) + # Test .copy() + h2 = h.copy() + h.update(b("blah blah blah")) # Corrupt the original hash object + out5 = binascii.b2a_hex(h2.digest()) # The copied hash object should return the correct result - out3 = h.hexdigest() - out4 = binascii.b2a_hex(h.digest()) - - # Test .copy() - h2 = h.copy() - h.update(b("blah blah blah")) # Corrupt the original hash object - out5 = binascii.b2a_hex(h2.digest()) # The copied hash object should return the correct result - - # PY3K: Check that hexdigest() returns str and digest() returns bytes - if sys.version_info[0] > 2: - self.assertTrue(isinstance(h.digest(), type(b("")))) - self.assertTrue(isinstance(h.hexdigest(), type(""))) - - # PY3K: Check that .hexverify() accepts bytes or str - if sys.version_info[0] > 2: - h.hexverify(h.hexdigest()) - h.hexverify(h.hexdigest().encode('ascii')) - - # PY3K: hexdigest() should return str, and digest() should return bytes - self.assertEqual(expected, out1) - if sys.version_info[0] == 2: - self.assertEqual(expected, out2) - self.assertEqual(expected, out3) - else: - self.assertEqual(expected.decode(), out2) - self.assertEqual(expected.decode(), out3) - self.assertEqual(expected, out4) - self.assertEqual(expected, out5) + # PY3K: hexdigest() should return str(), and digest() bytes + self.assertEqual(expected, out1) + if sys.version_info[0] == 2: + self.assertEqual(expected, out2) + self.assertEqual(expected, out3) + else: + self.assertEqual(expected.decode(), out2) + self.assertEqual(expected.decode(), out3) + self.assertEqual(expected, out4) + self.assertEqual(expected, out5) def make_hash_tests(module, module_name, test_data, digest_size, oid=None): tests = [] @@ -238,22 +178,20 @@ def make_hash_tests(module, module_name, test_data, digest_size, oid=None): description = row[2].encode('latin-1') name = "%s #%d: %s" % (module_name, i+1, description) tests.append(HashSelfTest(module, name, expected, input)) + if oid is not None: + oid = b(oid) name = "%s #%d: digest_size" % (module_name, i+1) tests.append(HashDigestSizeSelfTest(module, name, digest_size)) - if oid is not None: - tests.append(HashTestOID(module, oid)) - tests.append(HashDocStringTest(module)) - if getattr(module, 'name', None) is not None: - tests.append(GenericHashConstructorTest(module)) + tests.append(HashTestOID(module, oid)) return tests -def make_mac_tests(module, module_name, test_data): +def make_mac_tests(module, module_name, test_data, hashmods): tests = [] for i in range(len(test_data)): row = test_data[i] - (key, data, results, description, params) = row + (key, data, results, description) = row name = "%s #%d: %s" % (module_name, i+1, description) - tests.append(MACSelfTest(module, name, results, data, key, params)) + tests.append(MACSelfTest(module, name, results, data, key, hashmods)) return tests # vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/modules/Crypto/SelfTest/Hash/test_HMAC.py b/modules/Crypto/SelfTest/Hash/test_HMAC.py index baaea5e..c01c97b 100644 --- a/modules/Crypto/SelfTest/Hash/test_HMAC.py +++ b/modules/Crypto/SelfTest/Hash/test_HMAC.py @@ -29,17 +29,13 @@ __revision__ = "$Id$" from common import dict # For compatibility with Python 2.1 and 2.2 from Crypto.Util.py3compat import * -from Crypto.Hash import MD5, SHA1, SHA224, SHA256, SHA384, SHA512, HMAC - -default_hash = None - # This is a list of (key, data, results, description) tuples. test_data = [ ## Test vectors from RFC 2202 ## # Test that the default hashmod is MD5 ('0b' * 16, '4869205468657265', - dict(default_hash='9294727a3638bb1c13f48ef8158bfc9d'), + dict(default='9294727a3638bb1c13f48ef8158bfc9d'), 'default-is-MD5'), # Test case 1 (MD5) @@ -179,7 +175,9 @@ test_data = [ bfdc63644f0713938a7f51535c3a35e2 '''), 'RFC 4231 #7 (HMAC-SHA256)'), +] +hashlib_test_data = [ # Test case 8 (SHA224) ('4a656665', '7768617420646f2079612077616e74' @@ -205,25 +203,17 @@ test_data = [ def get_tests(config={}): global test_data + from Crypto.Hash import HMAC, MD5, SHA as SHA1, SHA256 from common import make_mac_tests - - # A test vector contains multiple results, each one for a - # different hash algorithm. - # Here we expand each test vector into multiple ones, - # and add the relevant parameters that will be passed to new() - exp_test_data = [] - for row in test_data: - for modname in row[2].keys(): - t = list(row) - t[2] = row[2][modname] - try: - t.append(dict(digestmod=globals()[modname])) - exp_test_data.append(t) - except AttributeError: - import sys - sys.stderr.write("SelfTest: warning: not testing HMAC-%s (not available)\n" % modname) - - return make_mac_tests(HMAC, "HMAC", exp_test_data) + hashmods = dict(MD5=MD5, SHA1=SHA1, SHA256=SHA256, default=None) + try: + from Crypto.Hash import SHA224, SHA384, SHA512 + hashmods.update(dict(SHA224=SHA224, SHA384=SHA384, SHA512=SHA512)) + test_data += hashlib_test_data + except ImportError: + import sys + sys.stderr.write("SelfTest: warning: not testing HMAC-SHA224/384/512 (not available)\n") + return make_mac_tests(HMAC, "HMAC", test_data, hashmods) if __name__ == '__main__': import unittest diff --git a/modules/Crypto/SelfTest/Hash/test_MD2.py b/modules/Crypto/SelfTest/Hash/test_MD2.py index fd03e78..db636d4 100644 --- a/modules/Crypto/SelfTest/Hash/test_MD2.py +++ b/modules/Crypto/SelfTest/Hash/test_MD2.py @@ -54,7 +54,7 @@ def get_tests(config={}): from common import make_hash_tests return make_hash_tests(MD2, "MD2", test_data, digest_size=16, - oid="1.2.840.113549.2.2") + oid="\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x02") if __name__ == '__main__': import unittest diff --git a/modules/Crypto/SelfTest/Hash/test_MD4.py b/modules/Crypto/SelfTest/Hash/test_MD4.py index 7dbf49f..1727bb6 100644 --- a/modules/Crypto/SelfTest/Hash/test_MD4.py +++ b/modules/Crypto/SelfTest/Hash/test_MD4.py @@ -54,7 +54,7 @@ def get_tests(config={}): from common import make_hash_tests return make_hash_tests(MD4, "MD4", test_data, digest_size=16, - oid="1.2.840.113549.2.4") + oid="\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x04") if __name__ == '__main__': import unittest diff --git a/modules/Crypto/SelfTest/Hash/test_MD5.py b/modules/Crypto/SelfTest/Hash/test_MD5.py index 0683113..2e293fc 100644 --- a/modules/Crypto/SelfTest/Hash/test_MD5.py +++ b/modules/Crypto/SelfTest/Hash/test_MD5.py @@ -54,7 +54,7 @@ def get_tests(config={}): from common import make_hash_tests return make_hash_tests(MD5, "MD5", test_data, digest_size=16, - oid="1.2.840.113549.2.5") + oid="\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x05") if __name__ == '__main__': import unittest diff --git a/modules/Crypto/SelfTest/Hash/test_RIPEMD.py b/modules/Crypto/SelfTest/Hash/test_RIPEMD.py new file mode 100644 index 0000000..6673a93 --- /dev/null +++ b/modules/Crypto/SelfTest/Hash/test_RIPEMD.py @@ -0,0 +1,73 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/Hash/test_RIPEMD.py: Self-test for the RIPEMD-160 hash function +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# 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. +# =================================================================== + +#"""Self-test suite for Crypto.Hash.RIPEMD""" + +__revision__ = "$Id$" + +from Crypto.Util.py3compat import * + +# This is a list of (expected_result, input[, description]) tuples. +test_data = [ + # Test vectors downloaded 2008-09-12 from + # http://homes.esat.kuleuven.be/~bosselae/ripemd160.html + ('9c1185a5c5e9fc54612808977ee8f548b2258d31', '', "'' (empty string)"), + ('0bdc9d2d256b3ee9daae347be6f4dc835a467ffe', 'a'), + ('8eb208f7e05d987a9b044a8e98c6b087f15a0bfc', 'abc'), + ('5d0689ef49d2fae572b881b123a85ffa21595f36', 'message digest'), + + ('f71c27109c692c1b56bbdceb5b9d2865b3708dbc', + 'abcdefghijklmnopqrstuvwxyz', + 'a-z'), + + ('12a053384a9c0c88e405a06c27dcf49ada62eb2b', + 'abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq', + 'abcdbcd...pnopq'), + + ('b0e20b6e3116640286ed3a87a5713079b21f5189', + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', + 'A-Z, a-z, 0-9'), + + ('9b752e45573d4b39f4dbd3323cab82bf63326bfb', + '1234567890' * 8, + "'1234567890' * 8"), + + ('52783243c1697bdbe16d37f97f68f08325dc1528', + 'a' * 10**6, + '"a" * 10**6'), +] + +def get_tests(config={}): + from Crypto.Hash import RIPEMD + from common import make_hash_tests + return make_hash_tests(RIPEMD, "RIPEMD", test_data, + digest_size=20, + oid="\x06\x05\x2b\x24\x03\02\x01") + +if __name__ == '__main__': + import unittest + suite = lambda: unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/modules/Crypto/SelfTest/Hash/test_SHA.py b/modules/Crypto/SelfTest/Hash/test_SHA.py new file mode 100644 index 0000000..7d72e77 --- /dev/null +++ b/modules/Crypto/SelfTest/Hash/test_SHA.py @@ -0,0 +1,64 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/Hash/SHA.py: Self-test for the SHA-1 hash function +# +# Written in 2008 by Dwayne C. Litzenberger +# +# =================================================================== +# 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. +# =================================================================== + +"""Self-test suite for Crypto.Hash.SHA""" + +__revision__ = "$Id$" + +from Crypto.Util.py3compat import * + +# Test vectors from various sources +# This is a list of (expected_result, input[, description]) tuples. +test_data = [ + # FIPS PUB 180-2, A.1 - "One-Block Message" + ('a9993e364706816aba3e25717850c26c9cd0d89d', 'abc'), + + # FIPS PUB 180-2, A.2 - "Multi-Block Message" + ('84983e441c3bd26ebaae4aa1f95129e5e54670f1', + 'abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq'), + + # FIPS PUB 180-2, A.3 - "Long Message" +# ('34aa973cd4c4daa4f61eeb2bdbad27316534016f', +# 'a' * 10**6, +# '"a" * 10**6'), + + # RFC 3174: Section 7.3, "TEST4" (multiple of 512 bits) + ('dea356a2cddd90c7a7ecedc5ebb563934f460452', + '01234567' * 80, + '"01234567" * 80'), +] + +def get_tests(config={}): + from Crypto.Hash import SHA + from common import make_hash_tests + return make_hash_tests(SHA, "SHA", test_data, + digest_size=20, + oid="\x06\x05\x2B\x0E\x03\x02\x1A") + +if __name__ == '__main__': + import unittest + suite = lambda: unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/modules/Crypto/SelfTest/Hash/test_SHA224.py b/modules/Crypto/SelfTest/Hash/test_SHA224.py index eb28ebc..a60f35a 100644 --- a/modules/Crypto/SelfTest/Hash/test_SHA224.py +++ b/modules/Crypto/SelfTest/Hash/test_SHA224.py @@ -55,7 +55,7 @@ def get_tests(config={}): from common import make_hash_tests return make_hash_tests(SHA224, "SHA224", test_data, digest_size=28, - oid='2.16.840.1.101.3.4.2.4') + oid='\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x04') if __name__ == '__main__': import unittest diff --git a/modules/Crypto/SelfTest/Hash/test_SHA256.py b/modules/Crypto/SelfTest/Hash/test_SHA256.py index 50bdba8..4b45110 100644 --- a/modules/Crypto/SelfTest/Hash/test_SHA256.py +++ b/modules/Crypto/SelfTest/Hash/test_SHA256.py @@ -81,7 +81,7 @@ def get_tests(config={}): from common import make_hash_tests tests = make_hash_tests(SHA256, "SHA256", test_data, digest_size=32, - oid="2.16.840.1.101.3.4.2.1") + oid="\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01") if config.get('slow_tests'): tests += [LargeSHA256Test()] diff --git a/modules/Crypto/SelfTest/Hash/test_SHA384.py b/modules/Crypto/SelfTest/Hash/test_SHA384.py index 27d16b3..b7a72c0 100644 --- a/modules/Crypto/SelfTest/Hash/test_SHA384.py +++ b/modules/Crypto/SelfTest/Hash/test_SHA384.py @@ -53,7 +53,7 @@ def get_tests(config={}): from common import make_hash_tests return make_hash_tests(SHA384, "SHA384", test_data, digest_size=48, - oid='2.16.840.1.101.3.4.2.2') + oid='\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02') if __name__ == '__main__': import unittest diff --git a/modules/Crypto/SelfTest/Hash/test_SHA512.py b/modules/Crypto/SelfTest/Hash/test_SHA512.py index 04a505e..cb86177 100644 --- a/modules/Crypto/SelfTest/Hash/test_SHA512.py +++ b/modules/Crypto/SelfTest/Hash/test_SHA512.py @@ -50,7 +50,7 @@ def get_tests(config={}): from common import make_hash_tests return make_hash_tests(SHA512, "SHA512", test_data, digest_size=64, - oid="2.16.840.1.101.3.4.2.3") + oid="\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03") if __name__ == '__main__': import unittest diff --git a/modules/Crypto/SelfTest/Protocol/__init__.py b/modules/Crypto/SelfTest/Protocol/__init__.py index 33ff581..a62c670 100644 --- a/modules/Crypto/SelfTest/Protocol/__init__.py +++ b/modules/Crypto/SelfTest/Protocol/__init__.py @@ -31,7 +31,6 @@ def get_tests(config={}): from Crypto.SelfTest.Protocol import test_chaffing; tests += test_chaffing.get_tests(config=config) from Crypto.SelfTest.Protocol import test_rfc1751; tests += test_rfc1751.get_tests(config=config) from Crypto.SelfTest.Protocol import test_AllOrNothing; tests += test_AllOrNothing.get_tests(config=config) - from Crypto.SelfTest.Protocol import test_KDF; tests += test_KDF.get_tests(config=config) return tests if __name__ == '__main__': diff --git a/modules/Crypto/SelfTest/Protocol/test_KDF.py b/modules/Crypto/SelfTest/Protocol/test_KDF.py index de09b41..119836b 100644 --- a/modules/Crypto/SelfTest/Protocol/test_KDF.py +++ b/modules/Crypto/SelfTest/Protocol/test_KDF.py @@ -25,13 +25,10 @@ __revision__ = "$Id$" import unittest from binascii import unhexlify -from Crypto.Util.py3compat import * - from Crypto.SelfTest.st_common import list_test_cases -from Crypto.Hash import SHA1, HMAC -from Crypto.Cipher import AES, DES3 +from Crypto.Hash import SHA as SHA1,HMAC -from Crypto.Protocol.KDF import PBKDF1, PBKDF2, _S2V +from Crypto.Protocol.KDF import * def t2b(t): return unhexlify(b(t)) @@ -48,7 +45,7 @@ class PBKDF1_Tests(unittest.TestCase): # From http://www.di-mgt.com.au/cryptoKDFs.html#examplespbkdf ("password","78578E5A5D63CB06",16,1000,"DC19847E05C64D2FAF10EBFB4A3D2A20"), ) - + def test1(self): v = self._testData[0] res = PBKDF1(v[0], t2b(v[1]), v[2], v[3], SHA1) @@ -74,7 +71,7 @@ class PBKDF2_Tests(unittest.TestCase): 25, 4096, "3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038"), ( 'pass\x00word',"7361006c74",16,4096, "56fa6aa75548099dcc37d7f03425e0c3"), ) - + def test1(self): # Test only for HMAC-SHA1 as PRF @@ -88,67 +85,10 @@ class PBKDF2_Tests(unittest.TestCase): self.assertEqual(res, t2b(v[4])) self.assertEqual(res, res2) -class S2V_Tests(unittest.TestCase): - - # Sequence of test vectors. - # Each test vector is made up by: - # Item #0: a tuple of strings - # Item #1: an AES key - # Item #2: the result - # Item #3: the cipher module S2V is based on - # Everything is hex encoded - _testData = [ - - # RFC5297, A.1 - ( - ( '101112131415161718191a1b1c1d1e1f2021222324252627', - '112233445566778899aabbccddee' ), - 'fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0', - '85632d07c6e8f37f950acd320a2ecc93', - AES - ), - - # RFC5297, A.2 - ( - ( '00112233445566778899aabbccddeeffdeaddadadeaddadaffeeddcc'+ - 'bbaa99887766554433221100', - '102030405060708090a0', - '09f911029d74e35bd84156c5635688c0', - '7468697320697320736f6d6520706c61'+ - '696e7465787420746f20656e63727970'+ - '74207573696e67205349562d414553'), - '7f7e7d7c7b7a79787776757473727170', - '7bdb6e3b432667eb06f4d14bff2fbd0f', - AES - ), - - ] - - def test1(self): - """Verify correctness of test vector""" - for tv in self._testData: - s2v = _S2V.new(t2b(tv[1]), tv[3]) - for s in tv[0]: - s2v.update(t2b(s)) - result = s2v.derive() - self.assertEqual(result, t2b(tv[2])) - - def test2(self): - """Verify that no more than 127(AES) and 63(TDES) - components are accepted.""" - key = bchr(0)*16 - for module in (AES, DES3): - s2v = _S2V.new(key, module) - max_comps = module.block_size*8-1 - for i in xrange(max_comps): - s2v.update(b("XX")) - self.assertRaises(TypeError, s2v.update, b("YY")) - def get_tests(config={}): tests = [] tests += list_test_cases(PBKDF1_Tests) tests += list_test_cases(PBKDF2_Tests) - tests += list_test_cases(S2V_Tests) return tests if __name__ == '__main__': diff --git a/modules/Crypto/SelfTest/PublicKey/__init__.py b/modules/Crypto/SelfTest/PublicKey/__init__.py index 23a13a4..61ba53f 100644 --- a/modules/Crypto/SelfTest/PublicKey/__init__.py +++ b/modules/Crypto/SelfTest/PublicKey/__init__.py @@ -32,13 +32,7 @@ def get_tests(config={}): tests = [] from Crypto.SelfTest.PublicKey import test_DSA; tests += test_DSA.get_tests(config=config) from Crypto.SelfTest.PublicKey import test_RSA; tests += test_RSA.get_tests(config=config) - - from Crypto.SelfTest.PublicKey import test_import_DSA - tests +=test_import_DSA.get_tests(config=config) - - from Crypto.SelfTest.PublicKey import test_import_RSA - tests += test_import_RSA.get_tests(config=config) - + from Crypto.SelfTest.PublicKey import test_importKey; tests += test_importKey.get_tests(config=config) from Crypto.SelfTest.PublicKey import test_ElGamal; tests += test_ElGamal.get_tests(config=config) return tests diff --git a/modules/Crypto/SelfTest/PublicKey/test_ElGamal.py b/modules/Crypto/SelfTest/PublicKey/test_ElGamal.py index 5f33c23..cdee8cf 100644 --- a/modules/Crypto/SelfTest/PublicKey/test_ElGamal.py +++ b/modules/Crypto/SelfTest/PublicKey/test_ElGamal.py @@ -28,7 +28,7 @@ import unittest from Crypto.SelfTest.st_common import list_test_cases, a2b_hex, b2a_hex from Crypto import Random from Crypto.PublicKey import ElGamal -from Crypto.Util.number import bytes_to_long +from Crypto.Util.number import * from Crypto.Util.py3compat import * class ElGamalTest(unittest.TestCase): @@ -151,7 +151,7 @@ class ElGamalTest(unittest.TestCase): tv2['key'] += [tv2[c]] del tv2[c] return tv2 - + def _test_random_key(self, bits): elgObj = ElGamal.generate(bits, Random.new().read) self._check_private_key(elgObj) diff --git a/modules/Crypto/SelfTest/PublicKey/test_RSA.py b/modules/Crypto/SelfTest/PublicKey/test_RSA.py index 2884317..c971042 100644 --- a/modules/Crypto/SelfTest/PublicKey/test_RSA.py +++ b/modules/Crypto/SelfTest/PublicKey/test_RSA.py @@ -28,7 +28,6 @@ __revision__ = "$Id$" import sys import os -import pickle if sys.version_info[0] == 2 and sys.version_info[1] == 1: from Crypto.Util.py21compat import * from Crypto.Util.py3compat import * @@ -88,21 +87,6 @@ class RSATest(unittest.TestCase): ce 33 52 52 4d 04 16 a5 a4 41 e7 00 af 46 15 03 """ - # The same key, in pickled format (from pycrypto 2.3) - # to ensure backward compatibility - pickled_key_2_3 = \ - "(iCrypto.PublicKey.RSA\n_RSAobj\np0\n(dp2\nS'e'\np3\nL17L\nsS'd'\np4"\ - "\nL11646763154293086160147889314553506764606353688284149120983587488"\ - "79382229568306696406525871631480713149376749558222371890533687587223"\ - "51580531956820574156366843733156436163097164007967904900300775223658"\ - "03543233292399245064743971969473468304536714979010219881003396235861"\ - "8370829441895425705728523874962107052993L\nsS'n'\np5\nL1319966490819"\ - "88309815009412231606409998872008467220356704480658206329986017741425"\ - "59273959878490114749026269828326520214759381792655199845793621772998"\ - "40439054838068985140623386496543388290455526885872858516219460533763"\ - "92312680578795692682905599590422046720587710762927130740460442438533"\ - "124053848898103790124491L\nsb." - def setUp(self): global RSA, Random, bytes_to_long from Crypto.PublicKey import RSA @@ -194,31 +178,6 @@ class RSATest(unittest.TestCase): self.assertRaises(ValueError, self.rsa.construct, [self.n, self.e, self.n-1]) - def test_serialization(self): - """RSA (default implementation) serialize/unserialize key""" - rsaObj_orig = self.rsa.generate(1024) - rsaObj = pickle.loads(pickle.dumps(rsaObj_orig)) - self._check_private_key(rsaObj) - self._exercise_primitive(rsaObj) - pub = rsaObj.publickey() - self._check_public_key(pub) - self._exercise_public_primitive(rsaObj) - - plaintext = a2b_hex(self.plaintext) - ciphertext1 = rsaObj_orig.encrypt(plaintext, b("")) - ciphertext2 = rsaObj.encrypt(plaintext, b("")) - self.assertEqual(ciphertext1, ciphertext2) - - if not (3, 0) <= sys.version_info < (3, 1, 2, 'final', 0): - # Unpickling is broken in Python 3 before 3.1.2 due to http://bugs.python.org/issue6137 - def test_serialization_compat(self): - """RSA (default implementation) backward compatibility serialization""" - rsaObj = pickle.loads(b(self.pickled_key_2_3)) - plaintext = a2b_hex(self.plaintext) - ciphertext = a2b_hex(self.ciphertext) - ciphertext_result = rsaObj.encrypt(plaintext, b(""))[0] - self.assertEqual(ciphertext_result, ciphertext) - def _check_private_key(self, rsaObj): # Check capabilities self.assertEqual(1, rsaObj.has_private()) @@ -393,20 +352,6 @@ class RSAFastMathTest(RSATest): def test_factoring(self): RSATest.test_factoring(self) - - def test_serialization(self): - """RSA (_fastmath implementation) serialize/unserialize key - """ - RSATest.test_serialization(self) - - if not (3, 0) <= sys.version_info < (3, 1, 2, 'final', 0): - # Unpickling is broken in Python 3 before 3.1.2 due to http://bugs.python.org/issue6137 - def test_serialization_compat(self): - """RSA (_fastmath implementation) backward compatibility serialization - """ - RSATest.test_serialization_compat(self) - - class RSASlowMathTest(RSATest): def setUp(self): RSATest.setUp(self) @@ -443,17 +388,6 @@ class RSASlowMathTest(RSATest): def test_factoring(self): RSATest.test_factoring(self) - def test_serialization(self): - """RSA (_slowmath implementation) serialize/unserialize key""" - RSATest.test_serialization(self) - - if not (3, 0) <= sys.version_info < (3, 1, 2, 'final', 0): - # Unpickling is broken in Python 3 before 3.1.2 due to http://bugs.python.org/issue6137 - def test_serialization_compat(self): - """RSA (_slowmath implementation) backward compatibility serialization - """ - RSATest.test_serialization_compat(self) - def get_tests(config={}): tests = [] tests += list_test_cases(RSATest) diff --git a/modules/Crypto/SelfTest/PublicKey/test_importKey.py b/modules/Crypto/SelfTest/PublicKey/test_importKey.py new file mode 100644 index 0000000..28a7eee --- /dev/null +++ b/modules/Crypto/SelfTest/PublicKey/test_importKey.py @@ -0,0 +1,345 @@ +# -*- coding: utf-8 -*- +# +# SelfTest/PublicKey/test_importKey.py: Self-test for importing RSA keys +# +# =================================================================== +# 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. +# =================================================================== + +from __future__ import nested_scopes + +__revision__ = "$Id$" + +import unittest + +from Crypto.PublicKey import RSA +from Crypto.SelfTest.st_common import * +from Crypto.Util.py3compat import * +from Crypto.Util.number import inverse +from Crypto.Util import asn1 + +def der2pem(der, text='PUBLIC'): + import binascii + chunks = [ binascii.b2a_base64(der[i:i+48]) for i in range(0, len(der), 48) ] + pem = b('-----BEGIN %s KEY-----\n' % text) + pem += b('').join(chunks) + pem += b('-----END %s KEY-----' % text) + return pem + +class ImportKeyTests(unittest.TestCase): + # 512-bit RSA key generated with openssl + rsaKeyPEM = u'''-----BEGIN RSA PRIVATE KEY----- +MIIBOwIBAAJBAL8eJ5AKoIsjURpcEoGubZMxLD7+kT+TLr7UkvEtFrRhDDKMtuII +q19FrL4pUIMymPMSLBn3hJLe30Dw48GQM4UCAwEAAQJACUSDEp8RTe32ftq8IwG8 +Wojl5mAd1wFiIOrZ/Uv8b963WJOJiuQcVN29vxU5+My9GPZ7RA3hrDBEAoHUDPrI +OQIhAPIPLz4dphiD9imAkivY31Rc5AfHJiQRA7XixTcjEkojAiEAyh/pJHks/Mlr ++rdPNEpotBjfV4M4BkgGAA/ipcmaAjcCIQCHvhwwKVBLzzTscT2HeUdEeBMoiXXK +JACAr3sJQJGxIQIgarRp+m1WSKV1MciwMaTOnbU7wxFs9DP1pva76lYBzgUCIQC9 +n0CnZCJ6IZYqSt0H5N7+Q+2Ro64nuwV/OSQfM6sBwQ== +-----END RSA PRIVATE KEY-----''' + + # As above, but this is actually an unencrypted PKCS#8 key + rsaKeyPEM8 = u'''-----BEGIN PRIVATE KEY----- +MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAvx4nkAqgiyNRGlwS +ga5tkzEsPv6RP5MuvtSS8S0WtGEMMoy24girX0WsvilQgzKY8xIsGfeEkt7fQPDj +wZAzhQIDAQABAkAJRIMSnxFN7fZ+2rwjAbxaiOXmYB3XAWIg6tn9S/xv3rdYk4mK +5BxU3b2/FTn4zL0Y9ntEDeGsMEQCgdQM+sg5AiEA8g8vPh2mGIP2KYCSK9jfVFzk +B8cmJBEDteLFNyMSSiMCIQDKH+kkeSz8yWv6t080Smi0GN9XgzgGSAYAD+KlyZoC +NwIhAIe+HDApUEvPNOxxPYd5R0R4EyiJdcokAICvewlAkbEhAiBqtGn6bVZIpXUx +yLAxpM6dtTvDEWz0M/Wm9rvqVgHOBQIhAL2fQKdkInohlipK3Qfk3v5D7ZGjrie7 +BX85JB8zqwHB +-----END PRIVATE KEY-----''' + + # The same RSA private key as in rsaKeyPEM, but now encrypted + rsaKeyEncryptedPEM=( + + # With DES and passphrase 'test' + ('test', u'''-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: DES-CBC,AF8F9A40BD2FA2FC + +Ckl9ex1kaVEWhYC2QBmfaF+YPiR4NFkRXA7nj3dcnuFEzBnY5XULupqQpQI3qbfA +u8GYS7+b3toWWiHZivHbAAUBPDIZG9hKDyB9Sq2VMARGsX1yW1zhNvZLIiVJzUHs +C6NxQ1IJWOXzTew/xM2I26kPwHIvadq+/VaT8gLQdjdH0jOiVNaevjWnLgrn1mLP +BCNRMdcexozWtAFNNqSzfW58MJL2OdMi21ED184EFytIc1BlB+FZiGZduwKGuaKy +9bMbdb/1PSvsSzPsqW7KSSrTw6MgJAFJg6lzIYvR5F4poTVBxwBX3+EyEmShiaNY +IRX3TgQI0IjrVuLmvlZKbGWP18FXj7I7k9tSsNOOzllTTdq3ny5vgM3A+ynfAaxp +dysKznQ6P+IoqML1WxAID4aGRMWka+uArOJ148Rbj9s= +-----END RSA PRIVATE KEY-----''', + "\xAF\x8F\x9A\x40\xBD\x2F\xA2\xFC"), + + # With Triple-DES and passphrase 'rocking' + ('rocking', u'''-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: DES-EDE3-CBC,C05D6C07F7FC02F6 + +w4lwQrXaVoTTJ0GgwY566htTA2/t1YlimhxkxYt9AEeCcidS5M0Wq9ClPiPz9O7F +m6K5QpM1rxo1RUE/ZyI85gglRNPdNwkeTOqit+kum7nN73AToX17+irVmOA4Z9E+ +4O07t91GxGMcjUSIFk0ucwEU4jgxRvYscbvOMvNbuZszGdVNzBTVddnShKCsy9i7 +nJbPlXeEKYi/OkRgO4PtfqqWQu5GIEFVUf9ev1QV7AvC+kyWTR1wWYnHX265jU5c +sopxQQtP8XEHIJEdd5/p1oieRcWTCNyY8EkslxDSsrf0OtZp6mZH9N+KU47cgQtt +9qGORmlWnsIoFFKcDohbtOaWBTKhkj5h6OkLjFjfU/sBeV1c+7wDT3dAy5tawXjG +YSxC7qDQIT/RECvV3+oQKEcmpEujn45wAnkTi12BH30= +-----END RSA PRIVATE KEY-----''', + "\xC0\x5D\x6C\x07\xF7\xFC\x02\xF6"), + ) + + rsaPublicKeyPEM = u'''-----BEGIN PUBLIC KEY----- +MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAL8eJ5AKoIsjURpcEoGubZMxLD7+kT+T +Lr7UkvEtFrRhDDKMtuIIq19FrL4pUIMymPMSLBn3hJLe30Dw48GQM4UCAwEAAQ== +-----END PUBLIC KEY-----''' + + # Obtained using 'ssh-keygen -i -m PKCS8 -f rsaPublicKeyPEM' + rsaPublicKeyOpenSSH = '''ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAQQC/HieQCqCLI1EaXBKBrm2TMSw+/pE/ky6+1JLxLRa0YQwyjLbiCKtfRay+KVCDMpjzEiwZ94SS3t9A8OPBkDOF comment\n''' + + # The private key, in PKCS#1 format encoded with DER + rsaKeyDER = a2b_hex( + '''3082013b020100024100bf1e27900aa08b23511a5c1281ae6d93312c3efe + 913f932ebed492f12d16b4610c328cb6e208ab5f45acbe2950833298f312 + 2c19f78492dedf40f0e3c190338502030100010240094483129f114dedf6 + 7edabc2301bc5a88e5e6601dd7016220ead9fd4bfc6fdeb75893898ae41c + 54ddbdbf1539f8ccbd18f67b440de1ac30440281d40cfac839022100f20f + 2f3e1da61883f62980922bd8df545ce407c726241103b5e2c53723124a23 + 022100ca1fe924792cfcc96bfab74f344a68b418df578338064806000fe2 + a5c99a023702210087be1c3029504bcf34ec713d877947447813288975ca + 240080af7b094091b12102206ab469fa6d5648a57531c8b031a4ce9db53b + c3116cf433f5a6f6bbea5601ce05022100bd9f40a764227a21962a4add07 + e4defe43ed91a3ae27bb057f39241f33ab01c1 + '''.replace(" ","")) + + # The private key, in unencrypted PKCS#8 format encoded with DER + rsaKeyDER8 = a2b_hex( + '''30820155020100300d06092a864886f70d01010105000482013f3082013 + b020100024100bf1e27900aa08b23511a5c1281ae6d93312c3efe913f932 + ebed492f12d16b4610c328cb6e208ab5f45acbe2950833298f3122c19f78 + 492dedf40f0e3c190338502030100010240094483129f114dedf67edabc2 + 301bc5a88e5e6601dd7016220ead9fd4bfc6fdeb75893898ae41c54ddbdb + f1539f8ccbd18f67b440de1ac30440281d40cfac839022100f20f2f3e1da + 61883f62980922bd8df545ce407c726241103b5e2c53723124a23022100c + a1fe924792cfcc96bfab74f344a68b418df578338064806000fe2a5c99a0 + 23702210087be1c3029504bcf34ec713d877947447813288975ca240080a + f7b094091b12102206ab469fa6d5648a57531c8b031a4ce9db53bc3116cf + 433f5a6f6bbea5601ce05022100bd9f40a764227a21962a4add07e4defe4 + 3ed91a3ae27bb057f39241f33ab01c1 + '''.replace(" ","")) + + rsaPublicKeyDER = a2b_hex( + '''305c300d06092a864886f70d0101010500034b003048024100bf1e27900a + a08b23511a5c1281ae6d93312c3efe913f932ebed492f12d16b4610c328c + b6e208ab5f45acbe2950833298f3122c19f78492dedf40f0e3c190338502 + 03010001 + '''.replace(" ","")) + + n = long('BF 1E 27 90 0A A0 8B 23 51 1A 5C 12 81 AE 6D 93 31 2C 3E FE 91 3F 93 2E BE D4 92 F1 2D 16 B4 61 0C 32 8C B6 E2 08 AB 5F 45 AC BE 29 50 83 32 98 F3 12 2C 19 F7 84 92 DE DF 40 F0 E3 C1 90 33 85'.replace(" ",""),16) + e = 65537L + d = long('09 44 83 12 9F 11 4D ED F6 7E DA BC 23 01 BC 5A 88 E5 E6 60 1D D7 01 62 20 EA D9 FD 4B FC 6F DE B7 58 93 89 8A E4 1C 54 DD BD BF 15 39 F8 CC BD 18 F6 7B 44 0D E1 AC 30 44 02 81 D4 0C FA C8 39'.replace(" ",""),16) + p = long('00 F2 0F 2F 3E 1D A6 18 83 F6 29 80 92 2B D8 DF 54 5C E4 07 C7 26 24 11 03 B5 E2 C5 37 23 12 4A 23'.replace(" ",""),16) + q = long('00 CA 1F E9 24 79 2C FC C9 6B FA B7 4F 34 4A 68 B4 18 DF 57 83 38 06 48 06 00 0F E2 A5 C9 9A 02 37'.replace(" ",""),16) + + # This is q^{-1} mod p). fastmath and slowmath use pInv (p^{-1} + # mod q) instead! + qInv = long('00 BD 9F 40 A7 64 22 7A 21 96 2A 4A DD 07 E4 DE FE 43 ED 91 A3 AE 27 BB 05 7F 39 24 1F 33 AB 01 C1'.replace(" ",""),16) + pInv = inverse(p,q) + + def testImportKey1(self): + """Verify import of RSAPrivateKey DER SEQUENCE""" + key = self.rsa.importKey(self.rsaKeyDER) + self.failUnless(key.has_private()) + self.assertEqual(key.n, self.n) + self.assertEqual(key.e, self.e) + self.assertEqual(key.d, self.d) + self.assertEqual(key.p, self.p) + self.assertEqual(key.q, self.q) + + def testImportKey2(self): + """Verify import of SubjectPublicKeyInfo DER SEQUENCE""" + key = self.rsa.importKey(self.rsaPublicKeyDER) + self.failIf(key.has_private()) + self.assertEqual(key.n, self.n) + self.assertEqual(key.e, self.e) + + def testImportKey3unicode(self): + """Verify import of RSAPrivateKey DER SEQUENCE, encoded with PEM as unicode""" + key = RSA.importKey(self.rsaKeyPEM) + self.assertEqual(key.has_private(),True) # assert_ + self.assertEqual(key.n, self.n) + self.assertEqual(key.e, self.e) + self.assertEqual(key.d, self.d) + self.assertEqual(key.p, self.p) + self.assertEqual(key.q, self.q) + + def testImportKey3bytes(self): + """Verify import of RSAPrivateKey DER SEQUENCE, encoded with PEM as byte string""" + key = RSA.importKey(b(self.rsaKeyPEM)) + self.assertEqual(key.has_private(),True) # assert_ + self.assertEqual(key.n, self.n) + self.assertEqual(key.e, self.e) + self.assertEqual(key.d, self.d) + self.assertEqual(key.p, self.p) + self.assertEqual(key.q, self.q) + + def testImportKey4unicode(self): + """Verify import of RSAPrivateKey DER SEQUENCE, encoded with PEM as unicode""" + key = RSA.importKey(self.rsaPublicKeyPEM) + self.assertEqual(key.has_private(),False) # failIf + self.assertEqual(key.n, self.n) + self.assertEqual(key.e, self.e) + + def testImportKey4bytes(self): + """Verify import of SubjectPublicKeyInfo DER SEQUENCE, encoded with PEM as byte string""" + key = RSA.importKey(b(self.rsaPublicKeyPEM)) + self.assertEqual(key.has_private(),False) # failIf + self.assertEqual(key.n, self.n) + self.assertEqual(key.e, self.e) + + def testImportKey5(self): + """Verifies that the imported key is still a valid RSA pair""" + key = RSA.importKey(self.rsaKeyPEM) + idem = key.encrypt(key.decrypt(b("Test")),0) + self.assertEqual(idem[0],b("Test")) + + def testImportKey6(self): + """Verifies that the imported key is still a valid RSA pair""" + key = RSA.importKey(self.rsaKeyDER) + idem = key.encrypt(key.decrypt(b("Test")),0) + self.assertEqual(idem[0],b("Test")) + + def testImportKey7(self): + """Verify import of OpenSSH public key""" + key = self.rsa.importKey(self.rsaPublicKeyOpenSSH) + self.assertEqual(key.n, self.n) + self.assertEqual(key.e, self.e) + + def testImportKey8(self): + """Verify import of encrypted PrivateKeyInfo DER SEQUENCE""" + for t in self.rsaKeyEncryptedPEM: + key = self.rsa.importKey(t[1], t[0]) + self.failUnless(key.has_private()) + self.assertEqual(key.n, self.n) + self.assertEqual(key.e, self.e) + self.assertEqual(key.d, self.d) + self.assertEqual(key.p, self.p) + self.assertEqual(key.q, self.q) + + def testImportKey9(self): + """Verify import of unencrypted PrivateKeyInfo DER SEQUENCE""" + key = self.rsa.importKey(self.rsaKeyDER8) + self.failUnless(key.has_private()) + self.assertEqual(key.n, self.n) + self.assertEqual(key.e, self.e) + self.assertEqual(key.d, self.d) + self.assertEqual(key.p, self.p) + self.assertEqual(key.q, self.q) + + def testImportKey10(self): + """Verify import of unencrypted PrivateKeyInfo DER SEQUENCE, encoded with PEM""" + key = self.rsa.importKey(self.rsaKeyPEM8) + self.failUnless(key.has_private()) + self.assertEqual(key.n, self.n) + self.assertEqual(key.e, self.e) + self.assertEqual(key.d, self.d) + self.assertEqual(key.p, self.p) + self.assertEqual(key.q, self.q) + + def testImportKey11(self): + """Verify import of RSAPublicKey DER SEQUENCE""" + der = asn1.DerSequence([17, 3]).encode() + key = self.rsa.importKey(der) + self.assertEqual(key.n, 17) + self.assertEqual(key.e, 3) + + def testImportKey12(self): + """Verify import of RSAPublicKey DER SEQUENCE, encoded with PEM""" + der = asn1.DerSequence([17, 3]).encode() + pem = der2pem(der) + key = self.rsa.importKey(pem) + self.assertEqual(key.n, 17) + self.assertEqual(key.e, 3) + + ### + def testExportKey1(self): + key = self.rsa.construct([self.n, self.e, self.d, self.p, self.q, self.pInv]) + derKey = key.exportKey("DER") + self.assertEqual(derKey, self.rsaKeyDER) + + def testExportKey2(self): + key = self.rsa.construct([self.n, self.e]) + derKey = key.exportKey("DER") + self.assertEqual(derKey, self.rsaPublicKeyDER) + + def testExportKey3(self): + key = self.rsa.construct([self.n, self.e, self.d, self.p, self.q, self.pInv]) + pemKey = key.exportKey("PEM") + self.assertEqual(pemKey, b(self.rsaKeyPEM)) + + def testExportKey4(self): + key = self.rsa.construct([self.n, self.e]) + pemKey = key.exportKey("PEM") + self.assertEqual(pemKey, b(self.rsaPublicKeyPEM)) + + def testExportKey5(self): + key = self.rsa.construct([self.n, self.e]) + openssh_1 = key.exportKey("OpenSSH").split() + openssh_2 = self.rsaPublicKeyOpenSSH.split() + self.assertEqual(openssh_1[0], openssh_2[0]) + self.assertEqual(openssh_1[1], openssh_2[1]) + + def testExportKey4(self): + key = self.rsa.construct([self.n, self.e, self.d, self.p, self.q, self.pInv]) + # Tuple with index #1 is encrypted with 3DES + t = map(b,self.rsaKeyEncryptedPEM[1]) + # Force the salt being used when exporting + key._randfunc = lambda N: (t[2]*divmod(N+len(t[2]),len(t[2]))[0])[:N] + pemKey = key.exportKey("PEM", t[0]) + self.assertEqual(pemKey, t[1]) + + def testExportKey5(self): + key = self.rsa.construct([self.n, self.e, self.d, self.p, self.q, self.pInv]) + derKey = key.exportKey("DER", pkcs=8) + self.assertEqual(derKey, self.rsaKeyDER8) + + def testExportKey6(self): + key = self.rsa.construct([self.n, self.e, self.d, self.p, self.q, self.pInv]) + pemKey = key.exportKey("PEM", pkcs=8) + self.assertEqual(pemKey, b(self.rsaKeyPEM8)) + +class ImportKeyTestsSlow(ImportKeyTests): + def setUp(self): + self.rsa = RSA.RSAImplementation(use_fast_math=0) + +class ImportKeyTestsFast(ImportKeyTests): + def setUp(self): + self.rsa = RSA.RSAImplementation(use_fast_math=1) + +if __name__ == '__main__': + unittest.main() + +def get_tests(config={}): + tests = [] + try: + from Crypto.PublicKey import _fastmath + tests += list_test_cases(ImportKeyTestsFast) + except ImportError: + pass + tests += list_test_cases(ImportKeyTestsSlow) + return tests + +if __name__ == '__main__': + suite = lambda: unittest.TestSuite(get_tests()) + unittest.main(defaultTest='suite') + +# vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/modules/Crypto/SelfTest/Random/__init__.py b/modules/Crypto/SelfTest/Random/__init__.py index f972bf0..48d84ff 100644 --- a/modules/Crypto/SelfTest/Random/__init__.py +++ b/modules/Crypto/SelfTest/Random/__init__.py @@ -32,7 +32,6 @@ def get_tests(config={}): from Crypto.SelfTest.Random import OSRNG; tests += OSRNG.get_tests(config=config) from Crypto.SelfTest.Random import test_random; tests += test_random.get_tests(config=config) from Crypto.SelfTest.Random import test_rpoolcompat; tests += test_rpoolcompat.get_tests(config=config) - from Crypto.SelfTest.Random import test__UserFriendlyRNG; tests += test__UserFriendlyRNG.get_tests(config=config) return tests if __name__ == '__main__': diff --git a/modules/Crypto/SelfTest/Random/test_random.py b/modules/Crypto/SelfTest/Random/test_random.py index 3e85c3c..f9ffc66 100644 --- a/modules/Crypto/SelfTest/Random/test_random.py +++ b/modules/Crypto/SelfTest/Random/test_random.py @@ -133,7 +133,7 @@ class SimpleTest(unittest.TestCase): self.assertEqual(b('1') in z, True) self.assertRaises(TypeError, random.shuffle, b('12')) self.assertRaises(TypeError, random.shuffle, 1) - self.assertRaises(TypeError, random.shuffle, "11") + self.assertRaises(TypeError, random.shuffle, "1") self.assertRaises(TypeError, random.shuffle, (1,2)) # 2to3 wraps a list() around it, alas - but I want to shoot # myself in the foot here! :D diff --git a/modules/Crypto/SelfTest/Signature/test_pkcs1_15.py b/modules/Crypto/SelfTest/Signature/test_pkcs1_15.py index cc94262..bc36696 100644 --- a/modules/Crypto/SelfTest/Signature/test_pkcs1_15.py +++ b/modules/Crypto/SelfTest/Signature/test_pkcs1_15.py @@ -26,8 +26,7 @@ import unittest from Crypto.PublicKey import RSA from Crypto.SelfTest.st_common import list_test_cases, a2b_hex, b2a_hex -from Crypto.Hash import MD2, SHA1, MD5, SHA224, SHA256, SHA384, SHA512,\ - RIPEMD160 +from Crypto.Hash import * from Crypto import Random from Crypto.Signature import PKCS1_v1_5 as PKCS from Crypto.Util.py3compat import * @@ -124,7 +123,7 @@ class PKCS1_15_Tests(unittest.TestCase): '''4a700a16432a291a3194646952687d5316458b8b86fb0a25aa30e0dcecdb 442676759ac63d56ec1499c3ae4c0013c2053cabd5b5804848994541ac16 fa243a4d''', - SHA1 + SHA ), # @@ -147,7 +146,7 @@ class PKCS1_15_Tests(unittest.TestCase): A9D20970C54E6651070B0144D43844C899320DD8FA7819F7EBC6A7715287332E C8675C136183B3F8A1F81EF969418267130A756FDBB2C71D9A667446E34E0EAD 9CF31BFB66F816F319D0B7E430A5F2891553986E003720261C7E9022C0D9F11F''', - SHA1 + SHA ) ) @@ -198,7 +197,7 @@ class PKCS1_15_Tests(unittest.TestCase): rng = Random.new().read key = RSA.generate(1024, rng) - for hashmod in (MD2,MD5,SHA1,SHA224,SHA256,SHA384,SHA512,RIPEMD160): + for hashmod in (MD2,MD5,SHA,SHA224,SHA256,SHA384,SHA512,RIPEMD): h = hashmod.new() h.update(b('blah blah blah')) @@ -207,37 +206,10 @@ class PKCS1_15_Tests(unittest.TestCase): result = signer.verify(h, s) self.failUnless(result) -class PKCS1_15_NoParams(unittest.TestCase): - """Verify that PKCS#1 v1.5 signatures pass even without NULL parameters in - the algorithm identifier (bug #1119552).""" - - rsakey = """-----BEGIN RSA PRIVATE KEY----- - MIIBOwIBAAJBAL8eJ5AKoIsjURpcEoGubZMxLD7+kT+TLr7UkvEtFrRhDDKMtuII - q19FrL4pUIMymPMSLBn3hJLe30Dw48GQM4UCAwEAAQJACUSDEp8RTe32ftq8IwG8 - Wojl5mAd1wFiIOrZ/Uv8b963WJOJiuQcVN29vxU5+My9GPZ7RA3hrDBEAoHUDPrI - OQIhAPIPLz4dphiD9imAkivY31Rc5AfHJiQRA7XixTcjEkojAiEAyh/pJHks/Mlr - +rdPNEpotBjfV4M4BkgGAA/ipcmaAjcCIQCHvhwwKVBLzzTscT2HeUdEeBMoiXXK - JACAr3sJQJGxIQIgarRp+m1WSKV1MciwMaTOnbU7wxFs9DP1pva76lYBzgUCIQC9 - n0CnZCJ6IZYqSt0H5N7+Q+2Ro64nuwV/OSQfM6sBwQ== - -----END RSA PRIVATE KEY-----""" - - msg = b("This is a test\x0a") - - # PKCS1 v1.5 signature of the message computed using SHA-1. - # The digestAlgorithm SEQUENCE does NOT contain the NULL parameter. - signature = '''a287a13517f716e72fb14eea8e33a8db4a4643314607e7ca3e3e281893db7401 - 3dda8b855fd99f6fecedcb25fcb7a434f35cd0a101f8b19348e0bd7b6f152dfc''' - - def testVerify(self): - verifier = PKCS.new(RSA.importKey(self.rsakey)) - h = SHA1.new(self.msg) - result = verifier.verify(h, t2b(self.signature)) - self.failUnless(result) def get_tests(config={}): tests = [] tests += list_test_cases(PKCS1_15_Tests) - tests += list_test_cases(PKCS1_15_NoParams) return tests if __name__ == '__main__': diff --git a/modules/Crypto/SelfTest/Signature/test_pkcs1_pss.py b/modules/Crypto/SelfTest/Signature/test_pkcs1_pss.py index e37e7a1..f5256a5 100644 --- a/modules/Crypto/SelfTest/Signature/test_pkcs1_pss.py +++ b/modules/Crypto/SelfTest/Signature/test_pkcs1_pss.py @@ -29,8 +29,7 @@ import unittest from Crypto.PublicKey import RSA from Crypto import Random from Crypto.SelfTest.st_common import list_test_cases, a2b_hex, b2a_hex -from Crypto.Hash import SHA1, MD2, RIPEMD160, SHA224, SHA384, SHA512,\ - SHA256, MD5 +from Crypto.Hash import * from Crypto.Signature import PKCS1_PSS as PKCS from Crypto.Util.py3compat import * @@ -137,7 +136,7 @@ class PKCS1_PSS_Tests(unittest.TestCase): '''e3 b5 d5 d0 02 c1 bc e5 0c 2b 65 ef 88 a1 88 d8 3b ce 7e 61''', # Hash algorithm - SHA1 + SHA ), # @@ -193,7 +192,7 @@ class PKCS1_PSS_Tests(unittest.TestCase): '''de e9 59 c7 e0 64 11 36 14 20 ff 80 18 5e d5 7f 3e 67 76 af''', # Hash - SHA1 + SHA ), # @@ -239,7 +238,7 @@ class PKCS1_PSS_Tests(unittest.TestCase): '''ef 28 69 fa 40 c3 46 cb 18 3d ab 3d 7b ff c9 8f d5 6d f4 2d''', # Hash - SHA1 + SHA ), # @@ -286,7 +285,7 @@ class PKCS1_PSS_Tests(unittest.TestCase): # Salt '''57 bf 16 0b cb 02 bb 1d c7 28 0c f0 45 85 30 b7 d2 83 2f f7''', - SHA1 + SHA ), # @@ -340,7 +339,7 @@ class PKCS1_PSS_Tests(unittest.TestCase): # Salt '''1d 65 49 1d 79 c8 64 b3 73 00 9b e6 f6 f2 46 7b ac 4c 78 fa''', - SHA1 + SHA ) ) @@ -381,12 +380,12 @@ class PKCS1_PSS_Tests(unittest.TestCase): self.failUnless(result) def testSignVerify(self): - h = SHA1.new() + h = SHA.new() h.update(b('blah blah blah')) rng = Random.new().read key = MyKey(RSA.generate(1024,rng)) - + # Helper function to monitor what's request from MGF global mgfcalls def newMGF(seed,maskLen): @@ -395,7 +394,7 @@ class PKCS1_PSS_Tests(unittest.TestCase): return bchr(0x00)*maskLen # Verify that PSS is friendly to all ciphers - for hashmod in (MD2,MD5,SHA1,SHA224,SHA256,SHA384,RIPEMD160): + for hashmod in (MD2,MD5,SHA,SHA224,SHA256,SHA384,RIPEMD): h = hashmod.new() h.update(b('blah blah blah')) @@ -407,7 +406,7 @@ class PKCS1_PSS_Tests(unittest.TestCase): self.failUnless(signer.verify(h, s)) self.assertEqual(key.asked, h.digest_size) - h = SHA1.new() + h = SHA.new() h.update(b('blah blah blah')) # Verify that sign() uses a different salt length diff --git a/modules/Crypto/SelfTest/Util/__init__.py b/modules/Crypto/SelfTest/Util/__init__.py index f404d0b..abd640a 100644 --- a/modules/Crypto/SelfTest/Util/__init__.py +++ b/modules/Crypto/SelfTest/Util/__init__.py @@ -34,7 +34,6 @@ def get_tests(config={}): from Crypto.SelfTest.Util import test_winrandom; tests += test_winrandom.get_tests(config=config) from Crypto.SelfTest.Util import test_number; tests += test_number.get_tests(config=config) from Crypto.SelfTest.Util import test_Counter; tests += test_Counter.get_tests(config=config) - from Crypto.SelfTest.Util import test_Padding; tests += test_Padding.get_tests(config=config) return tests if __name__ == '__main__': diff --git a/modules/Crypto/SelfTest/Util/test_Counter.py b/modules/Crypto/SelfTest/Util/test_Counter.py index 7dbfc97..33c9bd7 100644 --- a/modules/Crypto/SelfTest/Util/test_Counter.py +++ b/modules/Crypto/SelfTest/Util/test_Counter.py @@ -39,24 +39,34 @@ class CounterTests(unittest.TestCase): from Crypto.Util import Counter def test_BE_shortcut(self): - """Big endian""" + """Big endian, shortcut enabled""" c = Counter.new(128) + self.assertEqual(c.__PCT_CTR_SHORTCUT__,True) # assert_ c = Counter.new(128, little_endian=False) + self.assertEqual(c.__PCT_CTR_SHORTCUT__,True) # assert_ + c = Counter.new(128, disable_shortcut=False) + self.assertEqual(c.__PCT_CTR_SHORTCUT__,True) # assert_ + c = Counter.new(128, little_endian=False, disable_shortcut=False) + self.assertEqual(c.__PCT_CTR_SHORTCUT__,True) # assert_ def test_LE_shortcut(self): - """Little endian""" + """Little endian, shortcut enabled""" c = Counter.new(128, little_endian=True) + self.assertEqual(c.__PCT_CTR_SHORTCUT__,True) # assert_ + c = Counter.new(128, little_endian=True, disable_shortcut=False) + self.assertEqual(c.__PCT_CTR_SHORTCUT__,True) # assert_ def test_BE_no_shortcut(self): - """Big endian, with disable_shortcut""" - # Just testing API backward-compatibility. disable_shortcut is now a no-op. + """Big endian, shortcut disabled""" c = Counter.new(128, disable_shortcut=True) + self.assertRaises(AttributeError, getattr, c, '__PCT_CTR_SHORTCUT__') c = Counter.new(128, little_endian=False, disable_shortcut=True) + self.assertRaises(AttributeError, getattr, c, '__PCT_CTR_SHORTCUT__') def test_LE_no_shortcut(self): """Little endian, shortcut disabled""" - # Just testing API backward-compatibility. disable_shortcut is now a no-op. c = Counter.new(128, little_endian=True, disable_shortcut=True) + self.assertRaises(AttributeError, getattr, c, '__PCT_CTR_SHORTCUT__') def test_BE_defaults(self): """128-bit, Big endian, defaults""" diff --git a/modules/Crypto/SelfTest/Util/test_asn1.py b/modules/Crypto/SelfTest/Util/test_asn1.py index e40a46f..578dabe 100644 --- a/modules/Crypto/SelfTest/Util/test_asn1.py +++ b/modules/Crypto/SelfTest/Util/test_asn1.py @@ -28,626 +28,262 @@ import unittest import sys from Crypto.Util.py3compat import * -from Crypto.Util.asn1 import DerObject, DerSetOf, newDerSetOf, DerInteger,\ - DerBitString, newDerBitString, newDerObjectId,\ - DerObjectId, DerNull, DerOctetString,\ - newDerOctetString, DerSequence, newDerSequence,\ - newDerInteger -if sys.version_info[0] == 2 and sys.version_info[1] == 1: - from Crypto.Util.py21compat import * +from Crypto.Util.asn1 import DerSequence, DerObject class DerObjectTests(unittest.TestCase): - def testObjInit1(self): - # Fail with invalid tag format (must be 1 byte) - self.assertRaises(ValueError, DerObject, b('\x00\x99')) - # Fail with invalid implicit tag (must be <0x1F) - self.assertRaises(ValueError, DerObject, 0x1F) + def testObjEncode1(self): + # No payload + der = DerObject(b('\x33')) + self.assertEquals(der.encode(), b('\x33\x00')) + # Small payload + der.payload = b('\x45') + self.assertEquals(der.encode(), b('\x33\x01\x45')) + # Invariant + self.assertEquals(der.encode(), b('\x33\x01\x45')) + # Initialize with numerical tag + der = DerObject(b(0x33)) + der.payload = b('\x45') + self.assertEquals(der.encode(), b('\x33\x01\x45')) - # ------ + def testObjEncode2(self): + # Known types + der = DerObject('SEQUENCE') + self.assertEquals(der.encode(), b('\x30\x00')) + der = DerObject('BIT STRING') + self.assertEquals(der.encode(), b('\x03\x00')) + + def testObjEncode3(self): + # Long payload + der = DerObject(b('\x34')) + der.payload = b("0")*128 + self.assertEquals(der.encode(), b('\x34\x81\x80' + "0"*128)) - def testObjEncode1(self): - # No payload - der = DerObject(b('\x02')) - self.assertEquals(der.encode(), b('\x02\x00')) - # Small payload (primitive) - der.payload = b('\x45') - self.assertEquals(der.encode(), b('\x02\x01\x45')) - # Invariant - self.assertEquals(der.encode(), b('\x02\x01\x45')) - # Initialize with numerical tag - der = DerObject(0x04) - der.payload = b('\x45') - self.assertEquals(der.encode(), b('\x04\x01\x45')) - # Initialize with constructed type - der = DerObject(b('\x10'), constructed=True) - self.assertEquals(der.encode(), b('\x30\x00')) + def testObjDecode1(self): + # Decode short payload + der = DerObject() + der.decode(b('\x20\x02\x01\x02')) + self.assertEquals(der.payload, b("\x01\x02")) + self.assertEquals(der.typeTag, 0x20) - def testObjEncode2(self): - # Initialize with payload - der = DerObject(0x03, b('\x12\x12')) - self.assertEquals(der.encode(), b('\x03\x02\x12\x12')) - - def testObjEncode3(self): - # Long payload - der = DerObject(b('\x10')) - der.payload = b("0")*128 - self.assertEquals(der.encode(), b('\x10\x81\x80' + "0"*128)) - - def testObjEncode4(self): - # Implicit tags (constructed) - der = DerObject(0x10, implicit=1, constructed=True) - der.payload = b('ppll') - self.assertEquals(der.encode(), b('\xa1\x04ppll')) - # Implicit tags (primitive) - der = DerObject(0x02, implicit=0x1E, constructed=False) - der.payload = b('ppll') - self.assertEquals(der.encode(), b('\x9E\x04ppll')) - - # ----- - - def testObjDecode1(self): - # Decode short payload - der = DerObject(0x02) - der.decode(b('\x02\x02\x01\x02')) - self.assertEquals(der.payload, b("\x01\x02")) - self.assertEquals(der._idOctet, 0x02) - - def testObjDecode2(self): - # Decode long payload - der = DerObject(0x02) - der.decode(b('\x02\x81\x80' + "1"*128)) - self.assertEquals(der.payload, b("1")*128) - self.assertEquals(der._idOctet, 0x02) - - def testObjDecode3(self): - # Decode payload with too much data gives error - der = DerObject(0x02) - self.assertRaises(ValueError, der.decode, b('\x02\x02\x01\x02\xFF')) - # Decode payload with too little data gives error - der = DerObject(0x02) - self.assertRaises(EOFError, der.decode, b('\x02\x02\x01')) - - def testObjDecode4(self): - # Decode implicit tag (primitive) - der = DerObject(0x02, constructed=False, implicit=0xF) - self.assertRaises(ValueError, der.decode, b('\x02\x02\x01\x02')) - der.decode(b('\x8F\x01\x00')) - self.assertEquals(der.payload, b('\x00')) - # Decode implicit tag (constructed) - der = DerObject(0x02, constructed=True, implicit=0xF) - self.assertRaises(ValueError, der.decode, b('\x02\x02\x01\x02')) - der.decode(b('\xAF\x01\x00')) - self.assertEquals(der.payload, b('\x00')) - - def testObjDecode5(self): - # Decode payload with unexpected tag gives error - der = DerObject(0x02) - self.assertRaises(ValueError, der.decode, b('\x03\x02\x01\x02')) - - def testObjDecode6(self): - # Arbitrary DER object - der = DerObject() - der.decode(b('\x65\x01\x88')) - self.assertEquals(der._idOctet, 0x65) - self.assertEquals(der.payload, b('\x88')) - -class DerIntegerTests(unittest.TestCase): - - def testInit1(self): - der = newDerInteger(1) - self.assertEquals(der.encode(), b('\x02\x01\x01')) - - def testEncode1(self): - # Single-byte integers - # Value 0 - der = DerInteger(0) - self.assertEquals(der.encode(), b('\x02\x01\x00')) - # Value 1 - der = DerInteger(1) - self.assertEquals(der.encode(), b('\x02\x01\x01')) - # Value 127 - der = DerInteger(127) - self.assertEquals(der.encode(), b('\x02\x01\x7F')) - - def testEncode2(self): - # Multi-byte integers - # Value 128 - der = DerInteger(128) - self.assertEquals(der.encode(), b('\x02\x02\x00\x80')) - # Value 0x180 - der = DerInteger(0x180L) - self.assertEquals(der.encode(), b('\x02\x02\x01\x80')) - # One very long integer - der = DerInteger(2L**2048) - self.assertEquals(der.encode(), - b('\x02\x82\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00')) - - def testEncode3(self): - # Negative integers - # Value -1 - der = DerInteger(-1) - self.assertEquals(der.encode(), b('\x02\x01\xFF')) - # Value -128 - der = DerInteger(-128) - self.assertEquals(der.encode(), b('\x02\x01\x80')) - # Value - der = DerInteger(-87873) - self.assertEquals(der.encode(), b('\x02\x03\xFE\xA8\xBF')) - - # ----- - - def testDecode1(self): - # Single-byte integer - der = DerInteger() - # Value 0 - der.decode(b('\x02\x01\x00')) - self.assertEquals(der.value, 0) - # Value 1 - der.decode(b('\x02\x01\x01')) - self.assertEquals(der.value, 1) - # Value 127 - der.decode(b('\x02\x01\x7F')) - self.assertEquals(der.value, 127) - - def testDecode2(self): - # Multi-byte integer - der = DerInteger() - # Value 0x180L - der.decode(b('\x02\x02\x01\x80')) - self.assertEquals(der.value,0x180L) - # One very long integer - der.decode( - b('\x02\x82\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00')) - self.assertEquals(der.value,2L**2048) - - def testDecode3(self): - # Negative integer - der = DerInteger() - # Value -1 - der.decode(b('\x02\x01\xFF')) - self.assertEquals(der.value, -1) - # Value -32768 - der.decode(b('\x02\x02\x80\x00')) - self.assertEquals(der.value, -32768) - - def testDecode5(self): - # We still accept BER integer format - der = DerInteger() - # Redundant leading zeroes - der.decode(b('\x02\x02\x00\x01')) - self.assertEquals(der.value, 1) - # Redundant leading 0xFF - der.decode(b('\x02\x02\xFF\xFF')) - self.assertEquals(der.value, -1) - # Empty payload - der.decode(b('\x02\x00')) - self.assertEquals(der.value, 0) - - def testErrDecode1(self): - # Wide length field - der = DerInteger() - self.assertRaises(ValueError, der.decode, b('\x02\x81\x01\x01')) + def testObjDecode2(self): + # Decode short payload + der = DerObject() + der.decode(b('\x22\x81\x80' + "1"*128)) + self.assertEquals(der.payload, b("1")*128) + self.assertEquals(der.typeTag, 0x22) class DerSequenceTests(unittest.TestCase): - def testInit1(self): - der = newDerSequence(1, DerInteger(2), '0\x00') - self.assertEquals(der.encode(), b('0\x08\x02\x01\x01\x02\x01\x020\x00')) + def testEncode1(self): + # Empty sequence + der = DerSequence() + self.assertEquals(der.encode(), b('0\x00')) + self.failIf(der.hasOnlyInts()) + # One single-byte integer (zero) + der.append(0) + self.assertEquals(der.encode(), b('0\x03\x02\x01\x00')) + self.failUnless(der.hasOnlyInts()) + # Invariant + self.assertEquals(der.encode(), b('0\x03\x02\x01\x00')) - def testEncode1(self): - # Empty sequence - der = DerSequence() - self.assertEquals(der.encode(), b('0\x00')) - self.failIf(der.hasOnlyInts()) - # One single-byte integer (zero) - der.append(0) - self.assertEquals(der.encode(), b('0\x03\x02\x01\x00')) - self.assertEquals(der.hasInts(),1) - self.assertEquals(der.hasInts(False),1) - self.failUnless(der.hasOnlyInts()) - self.failUnless(der.hasOnlyInts(False)) - # Invariant - self.assertEquals(der.encode(), b('0\x03\x02\x01\x00')) - - def testEncode2(self): - # Indexing - der = DerSequence() - der.append(0) - der[0] = 1 - self.assertEquals(len(der),1) - self.assertEquals(der[0],1) - self.assertEquals(der[-1],1) - self.assertEquals(der.encode(), b('0\x03\x02\x01\x01')) - # - der[:] = [1] - self.assertEquals(len(der),1) - self.assertEquals(der[0],1) - self.assertEquals(der.encode(), b('0\x03\x02\x01\x01')) - - def testEncode3(self): - # One multi-byte integer (non-zero) - der = DerSequence() - der.append(0x180L) - self.assertEquals(der.encode(), b('0\x04\x02\x02\x01\x80')) - - def testEncode4(self): - # One very long integer - der = DerSequence() - der.append(2L**2048) - self.assertEquals(der.encode(), b('0\x82\x01\x05')+ - b('\x02\x82\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + def testEncode2(self): + # One single-byte integer (non-zero) + der = DerSequence() + der.append(127) + self.assertEquals(der.encode(), b('0\x03\x02\x01\x7f')) + # Indexing + der[0] = 1 + self.assertEquals(len(der),1) + self.assertEquals(der[0],1) + self.assertEquals(der[-1],1) + self.assertEquals(der.encode(), b('0\x03\x02\x01\x01')) + # + der[:] = [1] + self.assertEquals(len(der),1) + self.assertEquals(der[0],1) + self.assertEquals(der.encode(), b('0\x03\x02\x01\x01')) + + def testEncode3(self): + # One multi-byte integer (non-zero) + der = DerSequence() + der.append(0x180L) + self.assertEquals(der.encode(), b('0\x04\x02\x02\x01\x80')) + + def testEncode4(self): + # One very long integer + der = DerSequence() + der.append(2**2048) + self.assertEquals(der.encode(), b('0\x82\x01\x05')+ + b('\x02\x82\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00')) + + def testEncode5(self): + # One single-byte integer (looks negative) + der = DerSequence() + der.append(0xFFL) + self.assertEquals(der.encode(), b('0\x04\x02\x02\x00\xff')) + + def testEncode6(self): + # Two integers + der = DerSequence() + der.append(0x180L) + der.append(0xFFL) + self.assertEquals(der.encode(), b('0\x08\x02\x02\x01\x80\x02\x02\x00\xff')) + self.failUnless(der.hasOnlyInts()) + # + der.append(0x01) + der[1:] = [9,8] + self.assertEquals(len(der),3) + self.assertEqual(der[1:],[9,8]) + self.assertEqual(der[1:-1],[9]) + self.assertEquals(der.encode(), b('0\x0A\x02\x02\x01\x80\x02\x01\x09\x02\x01\x08')) + + def testEncode6(self): + # One integer and another type (no matter what it is) + der = DerSequence() + der.append(0x180L) + der.append(b('\x00\x02\x00\x00')) + self.assertEquals(der.encode(), b('0\x08\x02\x02\x01\x80\x00\x02\x00\x00')) + self.failIf(der.hasOnlyInts()) + + #### + + def testDecode1(self): + # Empty sequence + der = DerSequence() + der.decode(b('0\x00')) + self.assertEquals(len(der),0) + # One single-byte integer (zero) + der.decode(b('0\x03\x02\x01\x00')) + self.assertEquals(len(der),1) + self.assertEquals(der[0],0) + # Invariant + der.decode(b('0\x03\x02\x01\x00')) + self.assertEquals(len(der),1) + self.assertEquals(der[0],0) + + def testDecode2(self): + # One single-byte integer (non-zero) + der = DerSequence() + der.decode(b('0\x03\x02\x01\x7f')) + self.assertEquals(len(der),1) + self.assertEquals(der[0],127) + + def testDecode3(self): + # One multi-byte integer (non-zero) + der = DerSequence() + der.decode(b('0\x04\x02\x02\x01\x80')) + self.assertEquals(len(der),1) + self.assertEquals(der[0],0x180L) + + def testDecode4(self): + # One very long integer + der = DerSequence() + der.decode(b('0\x82\x01\x05')+ + b('\x02\x82\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00')) + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ + b('\x00\x00\x00\x00\x00\x00\x00\x00\x00')) + self.assertEquals(len(der),1) + self.assertEquals(der[0],2**2048) - def testEncode5(self): - der = DerSequence() - der += 1 - der += b('\x30\x00') - self.assertEquals(der.encode(), b('\x30\x05\x02\x01\x01\x30\x00')) + def testDecode5(self): + # One single-byte integer (looks negative) + der = DerSequence() + der.decode(b('0\x04\x02\x02\x00\xff')) + self.assertEquals(len(der),1) + self.assertEquals(der[0],0xFFL) - def testEncode6(self): - # Two positive integers - der = DerSequence() - der.append(0x180L) - der.append(0xFFL) - self.assertEquals(der.encode(), b('0\x08\x02\x02\x01\x80\x02\x02\x00\xff')) - self.failUnless(der.hasOnlyInts()) - self.failUnless(der.hasOnlyInts(False)) - # Two mixed integers - der = DerSequence() - der.append(2) - der.append(-2) - self.assertEquals(der.encode(), b('0\x06\x02\x01\x02\x02\x01\xFE')) - self.assertEquals(der.hasInts(), 1) - self.assertEquals(der.hasInts(False), 2) - self.failIf(der.hasOnlyInts()) - self.failUnless(der.hasOnlyInts(False)) - # - der.append(0x01) - der[1:] = [9,8] - self.assertEquals(len(der),3) - self.assertEqual(der[1:],[9,8]) - self.assertEqual(der[1:-1],[9]) - self.assertEquals(der.encode(), b('0\x09\x02\x01\x02\x02\x01\x09\x02\x01\x08')) + def testDecode6(self): + # Two integers + der = DerSequence() + der.decode(b('0\x08\x02\x02\x01\x80\x02\x02\x00\xff')) + self.assertEquals(len(der),2) + self.assertEquals(der[0],0x180L) + self.assertEquals(der[1],0xFFL) - def testEncode7(self): - # One integer and another type (no matter what it is) - der = DerSequence() - der.append(0x180L) - der.append(b('\x00\x02\x00\x00')) - self.assertEquals(der.encode(), b('0\x08\x02\x02\x01\x80\x00\x02\x00\x00')) - self.failIf(der.hasOnlyInts()) + def testDecode7(self): + # One integer and 2 other types + der = DerSequence() + der.decode(b('0\x0A\x02\x02\x01\x80\x24\x02\xb6\x63\x12\x00')) + self.assertEquals(len(der),3) + self.assertEquals(der[0],0x180L) + self.assertEquals(der[1],b('\x24\x02\xb6\x63')) + self.assertEquals(der[2],b('\x12\x00')) - #### + def testDecode8(self): + # Only 2 other types + der = DerSequence() + der.decode(b('0\x06\x24\x02\xb6\x63\x12\x00')) + self.assertEquals(len(der),2) + self.assertEquals(der[0],b('\x24\x02\xb6\x63')) + self.assertEquals(der[1],b('\x12\x00')) - def testDecode1(self): - # Empty sequence - der = DerSequence() - der.decode(b('0\x00')) - self.assertEquals(len(der),0) - # One single-byte integer (zero) - der.decode(b('0\x03\x02\x01\x00')) - self.assertEquals(len(der),1) - self.assertEquals(der[0],0) - # Invariant - der.decode(b('0\x03\x02\x01\x00')) - self.assertEquals(len(der),1) - self.assertEquals(der[0],0) + def testErrDecode1(self): + # Not a sequence + der = DerSequence() + self.assertRaises(ValueError, der.decode, b('')) + self.assertRaises(ValueError, der.decode, b('\x00')) + self.assertRaises(ValueError, der.decode, b('\x30')) - def testDecode2(self): - # One single-byte integer (non-zero) - der = DerSequence() - der.decode(b('0\x03\x02\x01\x7f')) - self.assertEquals(len(der),1) - self.assertEquals(der[0],127) + def testErrDecode2(self): + # Wrong payload type + der = DerSequence() + self.assertRaises(ValueError, der.decode, b('\x30\x00\x00'), True) - def testDecode4(self): - # One very long integer - der = DerSequence() - der.decode(b('0\x82\x01\x05')+ - b('\x02\x82\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')+ - b('\x00\x00\x00\x00\x00\x00\x00\x00\x00')) - self.assertEquals(len(der),1) - self.assertEquals(der[0],2L**2048) + def testErrDecode3(self): + # Wrong length format + der = DerSequence() + self.assertRaises(ValueError, der.decode, b('\x30\x04\x02\x01\x01\x00')) + self.assertRaises(ValueError, der.decode, b('\x30\x81\x03\x02\x01\x01')) + self.assertRaises(ValueError, der.decode, b('\x30\x04\x02\x81\x01\x01')) - def testDecode6(self): - # Two integers - der = DerSequence() - der.decode(b('0\x08\x02\x02\x01\x80\x02\x02\x00\xff')) - self.assertEquals(len(der),2) - self.assertEquals(der[0],0x180L) - self.assertEquals(der[1],0xFFL) - - def testDecode7(self): - # One integer and 2 other types - der = DerSequence() - der.decode(b('0\x0A\x02\x02\x01\x80\x24\x02\xb6\x63\x12\x00')) - self.assertEquals(len(der),3) - self.assertEquals(der[0],0x180L) - self.assertEquals(der[1],b('\x24\x02\xb6\x63')) - self.assertEquals(der[2],b('\x12\x00')) - - def testDecode8(self): - # Only 2 other types - der = DerSequence() - der.decode(b('0\x06\x24\x02\xb6\x63\x12\x00')) - self.assertEquals(len(der),2) - self.assertEquals(der[0],b('\x24\x02\xb6\x63')) - self.assertEquals(der[1],b('\x12\x00')) - self.assertEquals(der.hasInts(), 0) - self.assertEquals(der.hasInts(False), 0) - self.failIf(der.hasOnlyInts()) - self.failIf(der.hasOnlyInts(False)) - - def testErrDecode1(self): - # Not a sequence - der = DerSequence() - self.assertRaises(EOFError, der.decode, b('')) - self.assertRaises(ValueError, der.decode, b('\x00')) - self.assertRaises(EOFError, der.decode, b('\x30')) - - def testErrDecode2(self): - der = DerSequence() - # Too much data - self.assertRaises(ValueError, der.decode, b('\x30\x00\x00')) - - def testErrDecode3(self): - # Wrong length format - der = DerSequence() - # Missing length in sub-item - self.assertRaises(EOFError, der.decode, b('\x30\x04\x02\x01\x01\x00')) - # Valid BER, but invalid DER length - self.assertRaises(ValueError, der.decode, b('\x30\x81\x03\x02\x01\x01')) - self.assertRaises(ValueError, der.decode, b('\x30\x04\x02\x81\x01\x01')) - -class DerOctetStringTests(unittest.TestCase): - - def testInit1(self): - der = newDerOctetString(b('\xFF')) - self.assertEquals(der.encode(), b('\x04\x01\xFF')) - - def testEncode1(self): - # Empty sequence - der = DerOctetString() - self.assertEquals(der.encode(), b('\x04\x00')) - # Small payload - der.payload = b('\x01\x02') - self.assertEquals(der.encode(), b('\x04\x02\x01\x02')) - - #### - - def testDecode1(self): - # Empty sequence - der = DerOctetString() - der.decode(b('\x04\x00')) - self.assertEquals(der.payload, b('')) - # Small payload - der.decode(b('\x04\x02\x01\x02')) - self.assertEquals(der.payload, b('\x01\x02')) - - def testErrDecode1(self): - # No leftovers allowed - der = DerOctetString() - self.assertRaises(ValueError, der.decode, b('\x04\x01\x01\xff')) - -class DerNullTests(unittest.TestCase): - - def testEncode1(self): - der = DerNull() - self.assertEquals(der.encode(), b('\x05\x00')) - - #### - - def testDecode1(self): - # Empty sequence - der = DerNull() - der.decode(b('\x05\x00')) - -class DerObjectIdTests(unittest.TestCase): - - def testInit1(self): - der = newDerObjectId("1.1") - self.assertEquals(der.encode(), b('\x06\x01)')) - - def testEncode1(self): - der = DerObjectId('1.2.840.113549.1.1.1') - self.assertEquals(der.encode(), b('\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01')) - # - der = DerObjectId() - der.value = '1.2.840.113549.1.1.1' - self.assertEquals(der.encode(), b('\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01')) - - #### - - def testDecode1(self): - # Empty sequence - der = DerObjectId() - der.decode(b('\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01')) - self.assertEquals(der.value, '1.2.840.113549.1.1.1') - -class DerBitStringTests(unittest.TestCase): - - def testInit1(self): - der = newDerBitString(b("\xFF")) - self.assertEquals(der.encode(), b('\x03\x02\x00\xFF')) - - def testEncode1(self): - # Empty sequence - der = DerBitString() - self.assertEquals(der.encode(), b('\x03\x01\x00')) - # Small payload - der = DerBitString(b('\x01\x02')) - self.assertEquals(der.encode(), b('\x03\x03\x00\x01\x02')) - # Small payload - der = DerBitString() - der.value = b('\x01\x02') - self.assertEquals(der.encode(), b('\x03\x03\x00\x01\x02')) - - #### - - def testDecode1(self): - # Empty sequence - der = DerBitString() - der.decode(b('\x03\x00')) - self.assertEquals(der.value, b('')) - # Small payload - der.decode(b('\x03\x03\x00\x01\x02')) - self.assertEquals(der.value, b('\x01\x02')) - -class DerSetOfTests(unittest.TestCase): - - def testInit1(self): - der = newDerSetOf(DerInteger(1), DerInteger(2)) - self.assertEquals(der.encode(), b('1\x06\x02\x01\x01\x02\x01\x02')) - - def testEncode1(self): - # Empty set - der = DerSetOf() - self.assertEquals(der.encode(), b('1\x00')) - # One single-byte integer (zero) - der.add(0) - self.assertEquals(der.encode(), b('1\x03\x02\x01\x00')) - # Invariant - self.assertEquals(der.encode(), b('1\x03\x02\x01\x00')) - - def testEncode2(self): - # Two integers - der = DerSetOf() - der.add(0x180L) - der.add(0xFFL) - self.assertEquals(der.encode(), b('1\x08\x02\x02\x00\xff\x02\x02\x01\x80')) - # Initialize with integers - der = DerSetOf([0x180L, 0xFFL]) - self.assertEquals(der.encode(), b('1\x08\x02\x02\x00\xff\x02\x02\x01\x80')) - - def testEncode3(self): - # One integer and another type (no matter what it is) - der = DerSetOf() - der.add(0x180L) - self.assertRaises(ValueError, der.add, b('\x00\x02\x00\x00')) - - def testEncode4(self): - # Only non integers - der = DerSetOf() - der.add(b('\x01\x00')) - der.add(b('\x01\x01\x01')) - self.assertEquals(der.encode(), b('1\x05\x01\x00\x01\x01\x01')) - - #### - - def testDecode1(self): - # Empty sequence - der = DerSetOf() - der.decode(b('1\x00')) - self.assertEquals(len(der),0) - # One single-byte integer (zero) - der.decode(b('1\x03\x02\x01\x00')) - self.assertEquals(len(der),1) - self.assertEquals(list(der),[0]) - - def testDecode2(self): - # Two integers - der = DerSetOf() - der.decode(b('1\x08\x02\x02\x01\x80\x02\x02\x00\xff')) - self.assertEquals(len(der),2) - l = list(der) - self.failUnless(0x180 in l) - self.failUnless(0xFF in l) - - def testDecode3(self): - # One integer and 2 other types - der = DerSetOf() - #import pdb; pdb.set_trace() - self.assertRaises(ValueError, der.decode, - b('0\x0A\x02\x02\x01\x80\x24\x02\xb6\x63\x12\x00')) - - def testErrDecode1(self): - # No leftovers allowed - der = DerSetOf() - self.assertRaises(ValueError, der.decode, - b('1\x08\x02\x02\x01\x80\x02\x02\x00\xff\xAA')) + def testErrDecode4(self): + # Wrong integer format + der = DerSequence() + # Multi-byte encoding for zero + #self.assertRaises(ValueError, der.decode, '\x30\x04\x02\x02\x00\x00') + # Negative integer + self.assertRaises(ValueError, der.decode, b('\x30\x04\x02\x01\xFF')) def get_tests(config={}): from Crypto.SelfTest.st_common import list_test_cases listTests = [] listTests += list_test_cases(DerObjectTests) - listTests += list_test_cases(DerIntegerTests) listTests += list_test_cases(DerSequenceTests) - listTests += list_test_cases(DerOctetStringTests) - listTests += list_test_cases(DerNullTests) - listTests += list_test_cases(DerObjectIdTests) - listTests += list_test_cases(DerBitStringTests) - listTests += list_test_cases(DerSetOfTests) return listTests if __name__ == '__main__': diff --git a/modules/Crypto/SelfTest/Util/test_number.py b/modules/Crypto/SelfTest/Util/test_number.py index 2201a93..0502e9e 100644 --- a/modules/Crypto/SelfTest/Util/test_number.py +++ b/modules/Crypto/SelfTest/Util/test_number.py @@ -32,9 +32,6 @@ if sys.version_info[0] == 2 and sys.version_info[1] == 1: import unittest -class MyError(Exception): - """Dummy exception used for tests""" - # NB: In some places, we compare tuples instead of just output values so that # if any inputs cause a test failure, we'll be able to tell which ones. @@ -279,11 +276,6 @@ class MiscTests(unittest.TestCase): self.assertEqual(number.size(0xa2ba40),8*3) self.assertEqual(number.size(0xa2ba40ee07e3b2bd2f02ce227f36a195024486e49c19cb41bbbdfbba98b22b0e577c2eeaffa20d883a76e65e394c69d4b3c05a1e8fadda27edb2a42bc000fe888b9b32c22d15add0cd76b3e7936e19955b220dd17d4ea904b1ec102b2e4de7751222aa99151024c7cb41cc5ea21d00eeb41f7c800834d2c6e06bce3bce7ea9a5L), 1024) -class FastmathTests(unittest.TestCase): - def setUp(self): - global number - from Crypto.Util import number - def test_negative_number_roundtrip_mpzToLongObj_longObjToMPZ(self): """Test that mpzToLongObj and longObjToMPZ (internal functions) roundtrip negative numbers correctly.""" n = -100000000000000000000000000000000000L @@ -292,49 +284,9 @@ class FastmathTests(unittest.TestCase): self.assertEqual(n, k.n) self.assertEqual(e, k.e) - def test_isPrime_randfunc_exception(self): - """Test that when isPrime is called, an exception raised in randfunc is propagated.""" - def randfunc(n): - raise MyError - prime = 3536384141L # Needs to be large enough so that rabinMillerTest will be invoked - self.assertRaises(MyError, number._fastmath.isPrime, prime, randfunc=randfunc) - - def test_getStrongPrime_randfunc_exception(self): - """Test that when getStrongPrime is called, an exception raised in randfunc is propagated.""" - def randfunc(n): - raise MyError - self.assertRaises(MyError, number._fastmath.getStrongPrime, 512, randfunc=randfunc) - - def test_isPrime_randfunc_bogus(self): - """Test that when isPrime is called, an exception is raised if randfunc returns something bogus.""" - def randfunc(n): - return None - prime = 3536384141L # Needs to be large enough so that rabinMillerTest will be invoked - self.assertRaises(TypeError, number._fastmath.isPrime, prime, randfunc=randfunc) - - def test_getStrongPrime_randfunc_bogus(self): - """Test that when getStrongPrime is called, an exception is raised if randfunc returns something bogus.""" - def randfunc(n): - return None - self.assertRaises(TypeError, number._fastmath.getStrongPrime, 512, randfunc=randfunc) - def get_tests(config={}): from Crypto.SelfTest.st_common import list_test_cases - tests = list_test_cases(MiscTests) - try: - from Crypto.PublicKey import _fastmath - tests += list_test_cases(FastmathTests) - except ImportError: - from distutils.sysconfig import get_config_var - import inspect, os.path - _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) - return tests + return list_test_cases(MiscTests) if __name__ == '__main__': suite = lambda: unittest.TestSuite(get_tests()) diff --git a/modules/Crypto/SelfTest/__init__.py b/modules/Crypto/SelfTest/__init__.py index cb5782f..40b3969 100644 --- a/modules/Crypto/SelfTest/__init__.py +++ b/modules/Crypto/SelfTest/__init__.py @@ -66,13 +66,11 @@ def run(module=None, verbosity=0, stream=None, tests=None, config=None, **kwargs raise ValueError("'module' and 'tests' arguments are mutually exclusive") if stream is None: kwargs['stream'] = StringIO() - else: - kwargs['stream'] = stream runner = unittest.TextTestRunner(verbosity=verbosity, **kwargs) result = runner.run(suite) if not result.wasSuccessful(): if stream is None: - sys.stderr.write(kwargs['stream'].getvalue()) + sys.stderr.write(stream.getvalue()) raise SelfTestError("Self-test failed", result) return result @@ -85,7 +83,6 @@ def get_tests(config={}): from Crypto.SelfTest import Random; tests += Random.get_tests(config=config) from Crypto.SelfTest import Util; tests += Util.get_tests(config=config) from Crypto.SelfTest import Signature; tests += Signature.get_tests(config=config) - from Crypto.SelfTest import IO; tests += IO.get_tests(config=config) return tests if __name__ == '__main__': diff --git a/modules/Crypto/Signature/PKCS1_PSS.py b/modules/Crypto/Signature/PKCS1_PSS.py index 3840959..4f50eb8 100644 --- a/modules/Crypto/Signature/PKCS1_PSS.py +++ b/modules/Crypto/Signature/PKCS1_PSS.py @@ -30,22 +30,22 @@ For example, a sender may authenticate a message using SHA-1 and PSS like this: >>> from Crypto.Signature import PKCS1_PSS - >>> from Crypto.Hash import SHA1 - >>> from Crypto.PublicKey import RSA1 + >>> from Crypto.Hash import SHA + >>> from Crypto.PublicKey import RSA >>> from Crypto import Random >>> >>> message = 'To be signed' >>> key = RSA.importKey(open('privkey.der').read()) - >>> h = SHA1.new() + >>> h = SHA.new() >>> h.update(message) >>> signer = PKCS1_PSS.new(key) - >>> signature = signer.sign(key) + >>> signature = PKCS1_PSS.sign(key) At the receiver side, verification can be done like using the public part of the RSA key: >>> key = RSA.importKey(open('pubkey.der').read()) - >>> h = SHA1.new() + >>> h = SHA.new() >>> h.update(message) >>> verifier = PKCS1_PSS.new(key) >>> if verifier.verify(h, signature): @@ -72,7 +72,6 @@ if sys.version_info[0] == 2 and sys.version_info[1] == 1: import Crypto.Util.number from Crypto.Util.number import ceil_shift, ceil_div, long_to_bytes from Crypto.Util.strxor import strxor -from Crypto.Hash import new as Hash_new class PSS_SigScheme: """This signature scheme can perform PKCS#1 PSS RSA signature or verification.""" @@ -204,11 +203,7 @@ def MGF1(mgfSeed, maskLen, hash): T = b("") for counter in xrange(ceil_div(maskLen, hash.digest_size)): c = long_to_bytes(counter, 4) - try: - T = T + hash.new(mgfSeed + c).digest() - except AttributeError: - # hash object doesn't have a "new" method. Use Crypto.Hash.new() to instantiate it - T = T + Hash_new(hash, mgfSeed + c).digest() + T = T + hash.new(mgfSeed + c).digest() assert(len(T)>=maskLen) return T[:maskLen] @@ -258,11 +253,7 @@ def EMSA_PSS_ENCODE(mhash, emBits, randFunc, mgf, sLen): if randFunc and sLen>0: salt = randFunc(sLen) # Step 5 and 6 - try: - h = mhash.new(bchr(0x00)*8 + mhash.digest() + salt) - except AttributeError: - # hash object doesn't have a "new" method. Use Crypto.Hash.new() to instantiate it - h = Hash_new(mhash, bchr(0x00)*8 + mhash.digest() + salt) + h = mhash.new(bchr(0x00)*8 + mhash.digest() + salt) # Step 7 and 8 db = bchr(0x00)*(emLen-sLen-mhash.digest_size-2) + bchr(0x01) + salt # Step 9 @@ -337,11 +328,7 @@ def EMSA_PSS_VERIFY(mhash, em, emBits, mgf, sLen): salt = b("") if sLen: salt = db[-sLen:] # Step 12 and 13 - try: - hp = mhash.new(bchr(0x00)*8 + mhash.digest() + salt).digest() - except AttributeError: - # hash object doesn't have a "new" method. Use Crypto.Hash.new() to instantiate it - hp = Hash_new(mhash, bchr(0x00)*8 + mhash.digest() + salt).digest() + hp = mhash.new(bchr(0x00)*8 + mhash.digest() + salt).digest() # Step 14 if h!=hp: return False diff --git a/modules/Crypto/Signature/PKCS1_v1_5.py b/modules/Crypto/Signature/PKCS1_v1_5.py index 6a2e0d0..73ac251 100644 --- a/modules/Crypto/Signature/PKCS1_v1_5.py +++ b/modules/Crypto/Signature/PKCS1_v1_5.py @@ -60,13 +60,9 @@ the RSA key: __revision__ = "$Id$" __all__ = [ 'new', 'PKCS115_SigScheme' ] -import sys - import Crypto.Util.number from Crypto.Util.number import ceil_div -from Crypto.Util.asn1 import DerSequence, DerNull, DerOctetString, DerObjectId -if sys.version_info[0] == 2 and sys.version_info[1] == 1: - from Crypto.Util.py21compat import * +from Crypto.Util.asn1 import DerSequence, DerNull, DerOctetString from Crypto.Util.py3compat import * class PKCS115_SigScheme: @@ -154,13 +150,7 @@ class PKCS115_SigScheme: em1 = bchr(0x00)*(k-len(m)) + m # Step 3 try: - em2_with_params = EMSA_PKCS1_V1_5_ENCODE(mhash, k, True) - # MD hashes always require NULL params in AlgorithmIdentifier. - # For all others, it is optional. - if _HASH_OIDS[mhash.name].startswith('1.2.840.113549.2.'): # MD2/MD4/MD5 - em2_without_params = em2_with_params - else: - em2_without_params = EMSA_PKCS1_V1_5_ENCODE(mhash, k, False) + em2 = EMSA_PKCS1_V1_5_ENCODE(mhash, k) except ValueError: return 0 # Step 4 @@ -168,9 +158,9 @@ class PKCS115_SigScheme: # of its components one at a time) we avoid attacks to the padding # scheme like Bleichenbacher's (see http://www.mail-archive.com/cryptography@metzdowd.com/msg06537). # - return em1==em2_with_params or em1==em2_without_params + return em1==em2 -def EMSA_PKCS1_V1_5_ENCODE(hash, emLen, with_hash_parameters=True): +def EMSA_PKCS1_V1_5_ENCODE(hash, emLen): """ Implement the ``EMSA-PKCS1-V1_5-ENCODE`` function, as defined in PKCS#1 v2.1 (RFC3447, 9.2). @@ -184,9 +174,6 @@ def EMSA_PKCS1_V1_5_ENCODE(hash, emLen, with_hash_parameters=True): The hash object that holds the digest of the message being signed. emLen : int The length the final encoding must have, in bytes. - with_hash_parameters: - If True (default), include NULL parameters for the hash - algorithm in the ``digestAlgorithm`` SEQUENCE. :attention: the early standard (RFC2313) stated that ``DigestInfo`` had to be BER-encoded. This means that old signatures @@ -194,6 +181,11 @@ def EMSA_PKCS1_V1_5_ENCODE(hash, emLen, with_hash_parameters=True): is not supported in DER. Such encoding cannot be reproduced by this function. + :attention: the same standard defined ``DigestAlgorithm`` to be + of ``AlgorithmIdentifier`` type, where the PARAMETERS + item is optional. Encodings for ``MD2/4/5`` without + ``PARAMETERS`` cannot be reproduced by this function. + :Return: An ``emLen`` byte long string that encodes the hash. """ @@ -216,19 +208,7 @@ def EMSA_PKCS1_V1_5_ENCODE(hash, emLen, with_hash_parameters=True): # { OID id-sha512 PARAMETERS NULL } # } # - # Appendix B.1 also says that for SHA-1/-2 algorithms, the parameters - # should be omitted. They may be present, but when they are, they shall - # have NULL value. - - if with_hash_parameters: - digestAlgo = DerSequence([ - DerObjectId(_HASH_OIDS[hash.name]).encode(), - DerNull().encode() - ]) - else: - digestAlgo = DerSequence([ - DerObjectId(_HASH_OIDS[hash.name]).encode(), - ]) + digestAlgo = DerSequence([hash.oid, DerNull().encode()]) digest = DerOctetString(hash.digest()) digestInfo = DerSequence([ digestAlgo.encode(), @@ -238,7 +218,7 @@ def EMSA_PKCS1_V1_5_ENCODE(hash, emLen, with_hash_parameters=True): # We need at least 11 bytes for the remaining data: 3 fixed bytes and # at least 8 bytes of padding). if emLen>> from Crypto.Cipher import AES >>> from Crypto.Util import Counter >>> - >>> pt = b'X'*1000000 + >>> pt = b'\x00'*1000000 >>> ctr = Counter.new(128) - >>> key = b'AES-128 symm key' - >>> cipher = AES.new(key, AES.MODE_CTR, counter=ctr) + >>> cipher = AES.new(b'\x00'*16, AES.MODE_CTR, counter=ctr) >>> ct = cipher.encrypt(pt) :undocumented: __package__ @@ -57,15 +56,11 @@ if sys.version_info[0] == 2 and sys.version_info[1] == 1: from Crypto.Util.py21compat import * from Crypto.Util.py3compat import * -from Crypto.pct_warnings import DisableShortcut_DeprecationWarning from Crypto.Util import _counter import struct -import warnings - # Factory function -_deprecated = "deprecated" -def new(nbits, prefix=b(""), suffix=b(""), initial_value=1, overflow=0, little_endian=False, allow_wraparound=False, disable_shortcut=_deprecated): +def new(nbits, prefix=b(""), suffix=b(""), initial_value=1, overflow=0, little_endian=False, allow_wraparound=False, disable_shortcut=False): """Create a stateful counter block function suitable for CTR encryption modes. Each call to the function returns the next counter block. @@ -73,7 +68,7 @@ def new(nbits, prefix=b(""), suffix=b(""), initial_value=1, overflow=0, little_e prefix || counter value || postfix - The counter value is incremented by 1 at each call. + The counter value is incremented by one at each call. :Parameters: nbits : integer @@ -86,18 +81,17 @@ def new(nbits, prefix=b(""), suffix=b(""), initial_value=1, overflow=0, little_e used. initial_value : integer The initial value of the counter. Default value is 1. - overflow : integer - This value is currently ignored. little_endian : boolean - If *True*, the counter number will be encoded in little endian format. - If *False* (default), in big endian format. + If True, the counter number will be encoded in little endian format. + If False (default), in big endian format. allow_wraparound : boolean - If *True*, the counter will automatically restart from zero after - reaching the maximum value (``2**nbits-1``). - If *False* (default), the object will raise an *OverflowError*. - disable_shortcut : deprecated - This option is a no-op for backward compatibility. It will be removed - in a future version. Don't use it. + If True, the function will raise an *OverflowError* exception as soon + as the counter wraps around. If False (default), the counter will + simply restart from zero. + disable_shortcut : boolean + If True, do not make ciphers from `Crypto.Cipher` bypass the Python + layer when invoking the counter block function. + If False (default), bypass the Python layer. :Returns: The counter block function. """ @@ -114,13 +108,10 @@ def new(nbits, prefix=b(""), suffix=b(""), initial_value=1, overflow=0, little_e initval = _encode(initial_value, nbytes, little_endian) - if disable_shortcut is not _deprecated: # exact object comparison - warnings.warn("disable_shortcut has no effect and is deprecated", DisableShortcut_DeprecationWarning) - if little_endian: - return _counter._newLE(bstr(prefix), bstr(suffix), initval, allow_wraparound=allow_wraparound) + return _counter._newLE(bstr(prefix), bstr(suffix), initval, allow_wraparound=allow_wraparound, disable_shortcut=disable_shortcut) else: - return _counter._newBE(bstr(prefix), bstr(suffix), initval, allow_wraparound=allow_wraparound) + return _counter._newBE(bstr(prefix), bstr(suffix), initval, allow_wraparound=allow_wraparound, disable_shortcut=disable_shortcut) def _encode(n, nbytes, little_endian=False): retval = [] diff --git a/modules/Crypto/Util/__init__.py b/modules/Crypto/Util/__init__.py index b2030c0..a3bef8a 100644 --- a/modules/Crypto/Util/__init__.py +++ b/modules/Crypto/Util/__init__.py @@ -23,22 +23,15 @@ Contains useful modules that don't belong into any of the other Crypto.* subpackages. -======================== ============================================= -Module Description -======================== ============================================= -`Crypto.Util.number` Number-theoretic functions (primality testing, etc.) -`Crypto.Util.Counter` Fast counter functions for CTR cipher modes. -`Crypto.Util.randpool` Random number generation -`Crypto.Util.RFC1751` Converts between 128-bit keys and human-readable - strings of words. -`Crypto.Util.asn1` Minimal support for ASN.1 DER encoding -`Crypto.Util.Padding` Set of functions for adding and removing padding. -======================== ============================================= +Crypto.Util.number Number-theoretic functions (primality testing, etc.) +Crypto.Util.randpool Random number generation +Crypto.Util.RFC1751 Converts between 128-bit keys and human-readable + strings of words. +Crypto.Util.asn1 Minimal support for ASN.1 DER encoding """ -__all__ = ['randpool', 'RFC1751', 'number', 'strxor', 'asn1', 'Counter', - 'Padding' ] +__all__ = ['randpool', 'RFC1751', 'number', 'strxor', 'asn1' ] __revision__ = "$Id$" diff --git a/modules/Crypto/Util/_counter.so b/modules/Crypto/Util/_counter.so new file mode 100755 index 0000000..b161b47 Binary files /dev/null and b/modules/Crypto/Util/_counter.so differ diff --git a/modules/Crypto/Util/asn1.py b/modules/Crypto/Util/asn1.py index 0e471a3..dd5ec31 100644 --- a/modules/Crypto/Util/asn1.py +++ b/modules/Crypto/Util/asn1.py @@ -19,349 +19,148 @@ # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. # =================================================================== -""" ASN.1 DER encoding and decoding - -This module provides minimal support for encoding and decoding `ASN.1`_ DER -objects. - -.. _`ASN.1`: ftp://ftp.rsasecurity.com/pub/pkcs/ascii/layman.asc - -""" - -from __future__ import nested_scopes - -import sys - -if sys.version_info[0] == 2 and sys.version_info[1] == 1: - from Crypto.Util.py21compat import * -from Crypto.Util.py3compat import * -if sys.version_info[0] == 2 and sys.version_info[1] == 1: - from Crypto.Util.py21compat import * from Crypto.Util.number import long_to_bytes, bytes_to_long +import sys +from Crypto.Util.py3compat import * -__all__ = [ 'DerObject', 'DerInteger', 'DerOctetString', 'DerNull', - 'DerSequence', 'DerObjectId', 'DerBitString', 'DerSetOf', - 'newDerInteger', 'newDerOctetString', 'newDerSequence', - 'newDerObjectId', 'newDerBitString', 'newDerSetOf' ] +__all__ = [ 'DerObject', 'DerInteger', 'DerOctetString', 'DerNull', 'DerSequence', 'DerObjectId' ] -def _isInt(x, onlyNonNegative=False): - test = 0 - try: - test += x - except TypeError: - return False - return not onlyNonNegative or x>=0 - -class BytesIO_EOF(BytesIO): - """This class differs from BytesIO in that an EOFError exception is - raised whenever EOF is reached.""" - - def __init__(self, *params): - BytesIO.__init__(self, *params) - self.setRecord(False) - - def setRecord(self, record): - self._record = record - self._recording = b("") - - def read(self, length): - s = BytesIO.read(self, length) - if len(s)127: encoding = long_to_bytes(payloadLen) return bchr(len(encoding)+128) + encoding return bchr(payloadLen) def encode(self): - """Return this DER element, fully encoded as a binary byte string.""" - # Concatenate identifier octets, length octets, - # and contents octets - return bchr(self._idOctet) + self._lengthOctets() + self.payload + """Return a complete DER element, fully encoded as a TLV.""" + return bchr(self.typeTag) + self._lengthOctets(len(self.payload)) + self.payload - def _decodeLen(self, s): - """Decode DER length octets from a file.""" + 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). - length = bord(s.read_byte()) + Raises a ValueError exception if the DER length is invalid. + Raises an IndexError exception if the DER element is too short. + """ + length = bord(der[idx]) if length<=127: - return length - payloadLength = bytes_to_long(s.read(length & 0x7F)) - # According to DER (but not BER) the long form is used - # only when the length doesn't fit into 7 bits. + return (length,idx+1) + payloadLength = bytes_to_long(der[idx+1:idx+1+(length & 0x7F)]) if payloadLength<=127: - raise ValueError("Not a DER length tag (but still valid BER).") - return payloadLength + raise ValueError("Not a DER length tag.") + return (payloadLength, idx+1+(length & 0x7F)) - def decode(self, derEle): + def decode(self, derEle, noLeftOvers=0): """Decode a complete DER element, and re-initializes this object with it. - :Parameters: - derEle : byte string - A complete DER element. - - :Raise ValueError: - In case of parsing errors. - :Raise EOFError: - If the DER element is too short. - """ + @param derEle A complete DER element. It must start with a DER T + tag. + @param noLeftOvers Indicate whether it is acceptable to complete the + parsing of the DER element and find that not all + bytes in derEle have been used. + @return Index of the first unused byte in the given DER element. - s = BytesIO_EOF(derEle) - self._decodeFromStream(s) - # There shouldn't be other bytes left + Raises a ValueError exception in case of parsing errors. + Raises an IndexError exception if the DER element is too short. + """ try: - b = s.read_byte() - raise ValueError("Unexpected extra data after the DER structure") - except EOFError: - pass - - def _decodeFromStream(self, s): - """Decode a complete DER element from a file.""" - - try: - idOctet = bord(s.read_byte()) - except EOFError: - raise NoDerElementError - if self._idOctet != None: - if idOctet != self._idOctet: - raise ValueError("Unexpected DER tag") - else: - self._idOctet = idOctet - length = self._decodeLen(s) - self.payload = s.read(length) + 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): + raise ValueError("Not a DER structure") + self.payload = derEle[idx:idx+length] + except IndexError: + raise ValueError("Not a valid DER SEQUENCE.") + return idx+length class DerInteger(DerObject): - """Class to model a DER INTEGER. - - An example of encoding is: + def __init__(self, value = 0): + """Class to model an INTEGER DER element. - >>> from Crypto.Util.asn1 import DerInteger - >>> from binascii import hexlify, unhexlify - >>> int_der = DerInteger(9) - >>> print hexlify(int_der.encode()) - - which will show ``020109``, the DER encoding of 9. - - And for decoding: - - >>> s = unhexlify(b'020109') - >>> try: - >>> int_der = DerInteger() - >>> int_der.decode(s) - >>> print int_der.value - >>> except (ValueError, EOFError): - >>> print "Not a valid DER INTEGER" - - the output will be ``9``. - """ - - def __init__(self, value=0, implicit=None): - """Initialize the DER object as an INTEGER. - - :Parameters: - value : integer - The value of the integer. - - implicit : integer - The IMPLICIT tag to use for the encoded object. - It overrides the universal tag for INTEGER (2). + Limitation: only non-negative values are supported. """ - - DerObject.__init__(self, 0x02, b(''), implicit, False) - self.value = value #: The integer value + DerObject.__init__(self, 'INTEGER') + self.value = value def encode(self): - """Return the DER INTEGER, fully encoded as a - binary string.""" - - number = self.value - self.payload = b('') - while True: - self.payload = bchr(number&255) + self.payload - if 128 <= number <= 255: + """Return a complete INTEGER DER element, fully encoded as a TLV.""" + self.payload = long_to_bytes(self.value) + if bord(self.payload[0])>127: self.payload = bchr(0x00) + self.payload - if -128 <= number <= 255: - break - number >>= 8 return DerObject.encode(self) - def decode(self, derEle): - """Decode a complete DER INTEGER DER, and re-initializes this + def decode(self, derEle, noLeftOvers=0): + """Decode a complete INTEGER DER element, and re-initializes this object with it. - :Parameters: - derEle : byte string - A complete INTEGER DER element. - - :Raise ValueError: - In case of parsing errors. - :Raise EOFError: - If the DER element is too short. + @param derEle A complete INTEGER DER element. It must start with a DER + INTEGER tag. + @param noLeftOvers Indicate whether it is acceptable to complete the + parsing of the DER element and find that not all + bytes in derEle have been used. + @return Index of the first unused byte in the given DER element. + + Raises a ValueError exception if the DER element is not a + valid non-negative INTEGER. + Raises an IndexError exception if the DER element is too short. """ - DerObject.decode(self, derEle) - - def _decodeFromStream(self, s): - """Decode a complete DER INTEGER from a file.""" - - # Fill up self.payload - DerObject._decodeFromStream(self, s) - - # Derive self.value from self.payload - self.value = 0L - bits = 1 - for i in self.payload: - self.value *= 256 - self.value += bord(i) - bits <<= 8 - if self.payload and bord(self.payload[0]) & 0x80: - self.value -= bits - -def newDerInteger(number): - """Create a DerInteger object, already initialized with an integer.""" - - der = DerInteger(number) - return der + tlvLength = DerObject.decode(self, derEle, noLeftOvers) + if self.typeTag!=self.typeTags['INTEGER']: + raise ValueError ("Not a DER INTEGER.") + if bord(self.payload[0])>127: + raise ValueError ("Negative INTEGER.") + self.value = bytes_to_long(self.payload) + return tlvLength class DerSequence(DerObject): - """Class to model a DER SEQUENCE. + """Class to model a SEQUENCE DER element. - This object behaves like a dynamic Python sequence. - - Sub-elements that are INTEGERs behave like Python integers. - - Any other sub-element is a binary string encoded as a complete DER + This object behave like a dynamic Python sequence. + Sub-elements that are INTEGERs, look like Python integers. + Any other sub-element is a binary string encoded as the complete DER sub-element (TLV). - - An example of encoding is: - - >>> from Crypto.Util.asn1 import DerSequence, DerInteger - >>> from binascii import hexlify, unhexlify - >>> obj_der = unhexlify('070102') - >>> seq_der = DerSequence([4]) - >>> seq_der.append(9) - >>> seq_der.append(obj_der.encode()) - >>> print hexlify(seq_der.encode()) - - which will show ``3009020104020109070102``, the DER encoding of the - sequence containing ``4``, ``9``, and the object with payload ``02``. - - For decoding: - - >>> s = unhexlify(b'3009020104020109070102') - >>> try: - >>> seq_der = DerSequence() - >>> seq_der.decode(s) - >>> print len(seq_der) - >>> print seq_der[0] - >>> print seq_der[:] - >>> except (ValueError, EOFError): - >>> print "Not a valid DER SEQUENCE" - - the output will be:: - - 3 - 4 - [4L, 9L, b'\x07\x01\x02'] - """ - def __init__(self, startSeq=None, implicit=None): - """Initialize the DER object as a SEQUENCE. - - :Parameters: - startSeq : Python sequence - A sequence whose element are either integers or - other DER objects. - - implicit : integer - The IMPLICIT tag to use for the encoded object. - It overrides the universal tag for SEQUENCE (16). - """ - - DerObject.__init__(self, 0x10, b(''), implicit, True) + def __init__(self, startSeq=None): + """Initialize the SEQUENCE DER object. Always empty + initially.""" + DerObject.__init__(self, 'SEQUENCE') if startSeq==None: self._seq = [] else: @@ -383,517 +182,105 @@ class DerSequence(DerObject): return self._seq[max(0, i):max(0, j)] def __len__(self): return len(self._seq) - def __iadd__(self, item): - self._seq.append(item) - return self def append(self, item): - self._seq.append(item) - return self + return self._seq.append(item) - def hasInts(self, onlyNonNegative=True): - """Return the number of items in this sequence that are - integers. - - :Parameters: - onlyNonNegative : boolean - If True, negative integers are not counted in. - """ - def _isInt2(x): - return _isInt(x, onlyNonNegative) - return len(filter(_isInt2, self._seq)) + def hasInts(self): + """Return the number of items in this sequence that are numbers.""" + return len(filter(isInt, self._seq)) - def hasOnlyInts(self, onlyNonNegative=True): - """Return True if all items in this sequence are integers - or non-negative integers. - - This function returns False is the sequence is empty, - or at least one member is not an integer. - - :Parameters: - onlyNonNegative : boolean - If True, the presence of negative integers - causes the method to return False.""" - return self._seq and self.hasInts(onlyNonNegative)==len(self._seq) + def hasOnlyInts(self): + """Return True if all items in this sequence are numbers.""" + return self._seq and self.hasInts()==len(self._seq) def encode(self): - """Return this DER SEQUENCE, fully encoded as a - binary string. - - :Raises ValueError: - If some elements in the sequence are neither integers - nor byte strings. + """Return the DER encoding for the ASN.1 SEQUENCE, containing + the non-negative integers and longs added to this object. + + Limitation: Raises a ValueError exception if it some elements + in the sequence are neither Python integers nor complete DER INTEGERs. """ self.payload = b('') for item in self._seq: - try: - self.payload += item - except TypeError: try: - self.payload += DerInteger(item).encode() - except TypeError: - raise ValueError("Trying to DER encode an unknown object") + self.payload += item + except: + try: + self.payload += DerInteger(item).encode() + except: + raise ValueError("Trying to DER encode an unknown object") return DerObject.encode(self) - def decode(self, derEle): - """Decode a complete DER SEQUENCE, and re-initializes this + def decode(self, derEle, noLeftOvers=0): + """Decode a complete SEQUENCE DER element, and re-initializes this object with it. - :Parameters: - derEle : byte string - A complete SEQUENCE DER element. - - :Raise ValueError: - In case of parsing errors. - :Raise EOFError: - If the DER element is too short. + @param derEle A complete SEQUENCE DER element. It must start with a DER + SEQUENCE tag. + @param noLeftOvers Indicate whether it is acceptable to complete the + parsing of the DER element and find that not all + bytes in derEle have been used. + @return Index of the first unused byte in the given DER element. DER INTEGERs are decoded into Python integers. Any other DER element is not decoded. Its validity is not checked. + + Raises a ValueError exception if the DER element is not a + valid DER SEQUENCE. + Raises an IndexError exception if the DER element is too short. """ - DerObject.decode(self, derEle) - def _decodeFromStream(self, s): - """Decode a complete DER SEQUENCE from a file.""" - self._seq = [] - - # Fill up self.payload - DerObject._decodeFromStream(self, s) - - # Add one item at a time to self.seq, by scanning self.payload - p = BytesIO_EOF(self.payload) - while True: - try: - p.setRecord(True) - der = DerObject() - der._decodeFromStream(p) - - # Parse INTEGERs differently - if der._idOctet != 0x02: - self._seq.append(p._recording) - else: - derInt = DerInteger() - derInt.decode(p._recording) - self._seq.append(derInt.value) - - except NoDerElementError: - break - # end - -def newDerSequence(*der_objs): - """Create a DerSequence object, already initialized with all objects - passed as parameters.""" - - der = DerSequence() - for obj in der_objs: - if isinstance(obj, DerObject): - der += obj.encode() - else: - der += obj - return der + try: + tlvLength = DerObject.decode(self, derEle, noLeftOvers) + if self.typeTag!=self.typeTags['SEQUENCE']: + raise ValueError("Not a DER SEQUENCE.") + # Scan one TLV at once + idx = 0 + while idx>> from Crypto.Util.asn1 import DerOctetString - >>> from binascii import hexlify, unhexlify - >>> os_der = DerOctetString(b'\\xaa') - >>> os_der.payload += b'\\xbb' - >>> print hexlify(os_der.encode()) - - which will show ``0402aabb``, the DER encoding for the byte string - ``b'\\xAA\\xBB'``. - - For decoding: - - >>> s = unhexlify(b'0402aabb') - >>> try: - >>> os_der = DerOctetString() - >>> os_der.decode(s) - >>> print hexlify(os_der.payload) - >>> except (ValueError, EOFError): - >>> print "Not a valid DER OCTET STRING" - - the output will be ``aabb``. - """ - - def __init__(self, value=b(''), implicit=None): - """Initialize the DER object as an OCTET STRING. - - :Parameters: - value : byte string - The initial payload of the object. - If not specified, the payload is empty. - - implicit : integer - The IMPLICIT tag to use for the encoded object. - It overrides the universal tag for OCTET STRING (4). - """ - DerObject.__init__(self, 0x04, value, implicit, False) - -def newDerOctetString(binstring): - """Create a DerOctetString object, already initialized with the binary - string.""" - - if isinstance(binstring, DerObject): - der = DerOctetString(binstring.encode()) - else: - der = DerOctetString(binstring) - return der + def decode(self, derEle, noLeftOvers=0): + p = DerObject.decode(derEle, noLeftOvers) + if not self.isType("OCTET STRING"): + raise ValueError("Not a valid OCTET STRING.") + return p class DerNull(DerObject): - """Class to model a DER NULL element.""" - def __init__(self): - """Initialize the DER object as a NULL.""" - - DerObject.__init__(self, 0x05, b(''), False) + DerObject.__init__(self, 'NULL') class DerObjectId(DerObject): - """Class to model a DER OBJECT ID. - - An example of encoding is: + def __init__(self): + DerObject.__init__(self, 'OBJECT IDENTIFIER') - >>> from Crypto.Util.asn1 import DerObjectId - >>> from binascii import hexlify, unhexlify - >>> oid_der = DerObjectId("1.2") - >>> oid_der.value += ".840.113549.1.1.1" - >>> print hexlify(oid_der.encode()) + def decode(self, derEle, noLeftOvers=0): + p = DerObject.decode(derEle, noLeftOvers) + if not self.isType("OBJECT IDENTIFIER"): + raise ValueError("Not a valid OBJECT IDENTIFIER.") + return p - which will show ``06092a864886f70d010101``, the DER encoding for the - RSA Object Identifier ``1.2.840.113549.1.1.1``. +def isInt(x): + test = 0 + try: + test += x + except TypeError: + return 0 + return 1 - For decoding: - - >>> s = unhexlify(b'06092a864886f70d010101') - >>> try: - >>> oid_der = DerObjectId() - >>> oid_der.decode(s) - >>> print oid_der.value - >>> except (ValueError, EOFError): - >>> print "Not a valid DER OBJECT ID" - - the output will be ``1.2.840.113549.1.1.1``. - """ - - def __init__(self, value='', implicit=None): - """Initialize the DER object as an OBJECT ID. - - :Parameters: - value : string - The initial Object Identifier (e.g. "1.2.0.0.6.2"). - implicit : integer - The IMPLICIT tag to use for the encoded object. - It overrides the universal tag for OBJECT ID (6). - """ - DerObject.__init__(self, 0x06, b(''), implicit, False) - self.value = value #: The Object ID, a dot separated list of integers - - def encode(self): - """Return the DER OBJECT ID, fully encoded as a - binary string.""" - - comps = map(int,self.value.split(".")) - if len(comps)<2: - raise ValueError("Not a valid Object Identifier string") - self.payload = bchr(40*comps[0]+comps[1]) - for v in comps[2:]: - enc = [] - while v: - enc.insert(0, (v & 0x7F) | 0x80) - v >>= 7 - enc[-1] &= 0x7F - self.payload += b('').join(map(bchr, enc)) - return DerObject.encode(self) - - def decode(self, derEle): - """Decode a complete DER OBJECT ID, and re-initializes this - object with it. - - :Parameters: - derEle : byte string - A complete DER OBJECT ID. - - :Raise ValueError: - In case of parsing errors. - :Raise EOFError: - If the DER element is too short. - """ - - DerObject.decode(self, derEle) - - def _decodeFromStream(self, s): - """Decode a complete DER OBJECT ID from a file.""" - - # Fill up self.payload - DerObject._decodeFromStream(self, s) - - # Derive self.value from self.payload - p = BytesIO_EOF(self.payload) - comps = list(map(str, divmod(bord(p.read_byte()),40))) - v = 0 - try: - while True: - c = p.read_byte() - v = v*128 + (bord(c) & 0x7F) - if not (bord(c) & 0x80): - comps.append(str(v)) - v = 0 - except EOFError: - pass - self.value = '.'.join(comps) - -def newDerObjectId(dottedstring): - """Create a DerObjectId object, already initialized with the given Object - Identifier (a dotted string).""" - - der = DerObjectId(dottedstring) - return der - -class DerBitString(DerObject): - """Class to model a DER BIT STRING. - - An example of encoding is: - - >>> from Crypto.Util.asn1 import DerBitString - >>> from binascii import hexlify, unhexlify - >>> bs_der = DerBitString(b'\\xaa') - >>> bs_der.value += b'\\xbb' - >>> print hexlify(bs_der.encode()) - - which will show ``040300aabb``, the DER encoding for the bit string - ``b'\\xAA\\xBB'``. - - For decoding: - - >>> s = unhexlify(b'040300aabb') - >>> try: - >>> bs_der = DerBitString() - >>> bs_der.decode(s) - >>> print hexlify(bs_der.value) - >>> except (ValueError, EOFError): - >>> print "Not a valid DER OCTET STRING" - - the output will be ``aabb``. - """ - - def __init__(self, value=b(''), implicit=None): - """Initialize the DER object as a BIT STRING. - - :Parameters: - value : byte string - The initial, packed bit string. - If not specified, the bit string is empty. - implicit : integer - The IMPLICIT tag to use for the encoded object. - It overrides the universal tag for OCTET STRING (3). - """ - DerObject.__init__(self, 0x03, b(''), implicit, False) - self.value = value #: The bitstring value (packed) - - def encode(self): - """Return the DER BIT STRING, fully encoded as a - binary string.""" - - # Add padding count byte - self.payload = b('\x00') + self.value - return DerObject.encode(self) - - def decode(self, derEle): - """Decode a complete DER BIT STRING, and re-initializes this - object with it. - - :Parameters: - derEle : byte string - A complete DER BIT STRING. - - :Raise ValueError: - In case of parsing errors. - :Raise EOFError: - If the DER element is too short. - """ - - DerObject.decode(self, derEle) - - def _decodeFromStream(self, s): - """Decode a complete DER BIT STRING DER from a file.""" - - # Fill-up self.payload - DerObject._decodeFromStream(self, s) - - if self.payload and bord(self.payload[0])!=0: - raise ValueError("Not a valid BIT STRING") - - # Fill-up self.value - self.value = b('') - # Remove padding count byte - if self.payload: - self.value = self.payload[1:] - -def newDerBitString(binstring): - """Create a DerStringString object, already initialized with the binary - string.""" - - if isinstance(binstring, DerObject): - der = DerBitString(binstring.encode()) - else: - der = DerBitString(binstring) - return der - -class DerSetOf(DerObject): - """Class to model a DER SET OF. - - An example of encoding is: - - >>> from Crypto.Util.asn1 import DerBitString - >>> from binascii import hexlify, unhexlify - >>> so_der = DerSetOf([4,5]) - >>> so_der.add(6) - >>> print hexlify(so_der.encode()) - - which will show ``3109020104020105020106``, the DER encoding - of a SET OF with items 4,5, and 6. - - For decoding: - - >>> s = unhexlify(b'3109020104020105020106') - >>> try: - >>> so_der = DerSetOf() - >>> so_der.decode(s) - >>> print [x for x in so_der] - >>> except (ValueError, EOFError): - >>> print "Not a valid DER SET OF" - - the output will be ``[4L, 5L, 6L]``. - """ - - def __init__(self, startSet=None, implicit=None): - """Initialize the DER object as a SET OF. - - :Parameters: - startSet : container - The initial set of integers or DER encoded objects. - implicit : integer - The IMPLICIT tag to use for the encoded object. - It overrides the universal tag for SET OF (17). - """ - DerObject.__init__(self, 0x11, b(''), implicit, True) - self._seq = [] - self._elemOctet = None - if startSet: - for e in startSet: - self.add(e) - - def __getitem__(self, n): - return self._seq[n] - - def __iter__(self): - return iter(self._seq) - - def __len__(self): - return len(self._seq) - - def add(self, elem): - """Add an element to the set. - - :Parameters: - elem : byte string or integer - An element of the same type of objects already in the set. - It can be an integer or a DER encoded object. - """ - if _isInt(elem): - eo = 0x02 - else: - eo = bord(elem[0]) - if self._elemOctet != eo: - if self._elemOctet: - raise ValueError("New element does not belong to the set") - self._elemOctet = eo - if not elem in self._seq: - self._seq.append(elem) - - def decode(self, derEle): - """Decode a complete SET OF DER element, and re-initializes this - object with it. - - DER INTEGERs are decoded into Python integers. Any other DER - element is left undecoded; its validity is not checked. - - :Parameters: - derEle : byte string - A complete DER BIT SET OF. - - :Raise ValueError: - In case of parsing errors. - :Raise EOFError: - If the DER element is too short. - """ - - DerObject.decode(self, derEle) - - def _decodeFromStream(self, s): - """Decode a complete DER SET OF from a file.""" - - self._seq = [] - - # Fill up self.payload - DerObject._decodeFromStream(self, s) - - # Add one item at a time to self.seq, by scanning self.payload - p = BytesIO_EOF(self.payload) - setIdOctet = -1 - while True: - try: - p.setRecord(True) - der = DerObject() - der._decodeFromStream(p) - - # Verify that all members are of the same type - if setIdOctet < 0: - setIdOctet = der._idOctet - else: - if setIdOctet != der._idOctet: - raise ValueError("Not all elements are of the same DER type") - - # Parse INTEGERs differently - if setIdOctet != 0x02: - self._seq.append(p._recording) - else: - derInt = DerInteger() - derInt.decode(p._recording) - self._seq.append(derInt.value) - - except NoDerElementError: - break - # end - - def encode(self): - """Return this SET OF DER element, fully encoded as a - binary string. - """ - - # Elements in the set must be ordered in lexicographic order - ordered = [] - for item in self._seq: - if _isInt(item): - bys = DerInteger(item).encode() - else: - bys = item - ordered.append(bys) - ordered.sort() - self.payload = b('').join(ordered) - return DerObject.encode(self) - -def newDerSetOf(*der_objs): - """Create a DerSequence object, already initialized with all objects - passed as parameters.""" - - der = DerSetOf() - for obj in der_objs: - if isinstance(obj, DerObject): - der.add(obj.encode()) - else: - der.add(obj) - return der diff --git a/modules/Crypto/Util/py21compat.py b/modules/Crypto/Util/py21compat.py index 658fd36..624408b 100644 --- a/modules/Crypto/Util/py21compat.py +++ b/modules/Crypto/Util/py21compat.py @@ -81,21 +81,4 @@ except TypeError: return True return False -# -# Python 2.2 introduces the built-in staticmethod(). Python 2.4 turns -# it into a function decorator (@staticmethod). -# -# The following recipe for achieving the same thing in Python 2.1 comes -# from the Python Cookbok ("Implementanting Static Methods"). -# -try: - class A: - def a(): pass - a = staticmethod(a) -except NameError: - class staticmethod: - def __init__(self, anycallable): - self.__call__ = anycallable - __all__ += ['staticmethod'] - # vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/modules/Crypto/Util/py3compat.py b/modules/Crypto/Util/py3compat.py index b8b89bf..34e5224 100644 --- a/modules/Crypto/Util/py3compat.py +++ b/modules/Crypto/Util/py3compat.py @@ -77,18 +77,12 @@ if sys.version_info[0] == 2: return s.encode('latin-1') except: return ''.join(s) - def tostr(bs): - return unicode(bs, 'latin-1') else: def tobytes(s): if isinstance(s, unicode): return s.encode("latin-1") else: return ''.join(s) - def tostr(bs): - return bs.decode('latin-1') - # In Pyton 2.x, StringIO is a stand-alone module - from StringIO import StringIO as BytesIO else: def b(s): return s.encode("latin-1") # utf-8 would cause some side-effects we don't want @@ -109,9 +103,5 @@ else: return s.encode("latin-1") else: return bytes(s) - def tostr(bs): - return bs.decode("latin-1") - # In Pyton 3.x, StringIO is a sub-module of io - from io import BytesIO - + # vim:set ts=4 sw=4 sts=4 expandtab: diff --git a/modules/Crypto/Util/strxor.so b/modules/Crypto/Util/strxor.so new file mode 100755 index 0000000..8785f2b Binary files /dev/null and b/modules/Crypto/Util/strxor.so differ diff --git a/modules/Crypto/__init__.py b/modules/Crypto/__init__.py index 2834731..cea887c 100644 --- a/modules/Crypto/__init__.py +++ b/modules/Crypto/__init__.py @@ -35,17 +35,17 @@ Crypto.Protocol Crypto.PublicKey Public-key encryption and signature algorithms (RSA, DSA) Crypto.Signature - Public-key signature algorithms (RSA PKCS#1) + Public-key signature algorithms (RSA PKCS#1) Crypto.Util Various useful modules and functions (long-to-string conversion, random number generation, number theoretic functions) """ -__all__ = ['Cipher', 'Hash', 'Protocol', 'PublicKey', 'Util', 'Signature', 'IO'] +__all__ = ['Cipher', 'Hash', 'Protocol', 'PublicKey', 'Util', 'Signature'] -__version__ = '2.7a1' # See also below and setup.py +__version__ = '2.6' # See also below and setup.py __revision__ = "$Id$" # New software should look at this instead of at __version__ above. -version_info = (2, 7, 0, 'alpha', 1) # See also above and setup.py +version_info = (2, 6, 0, 'final', 0) # See also above and setup.py diff --git a/modules/Crypto/pct_warnings.py b/modules/Crypto/pct_warnings.py index d6adc5b..9b4361e 100644 --- a/modules/Crypto/pct_warnings.py +++ b/modules/Crypto/pct_warnings.py @@ -49,9 +49,6 @@ class ClockRewindWarning(CryptoRuntimeWarning): class GetRandomNumber_DeprecationWarning(CryptoDeprecationWarning): """Issued when Crypto.Util.number.getRandomNumber is invoked.""" -class DisableShortcut_DeprecationWarning(CryptoDeprecationWarning): - """Issued when Counter.new(disable_shortcut=...) is invoked.""" - class PowmInsecureWarning(CryptoRuntimeWarning): """Warning for when _fastmath is built without mpz_powm_sec""" diff --git a/modules/encryptedscreenshot.py b/modules/encryptedscreenshot.py new file mode 100644 index 0000000..825acc8 --- /dev/null +++ b/modules/encryptedscreenshot.py @@ -0,0 +1,199 @@ +import bson, scrypt + +from Crypto.Cipher import AES +from Crypto.Util import Counter +from Crypto.PublicKey import RSA +from Crypto.PublicKey import DSA +from salsa20 import Salsa20_keystream +import os, struct, time, hashlib, hashlib, random + + +class EncryptedScreenshot: + def __init__(self, metadata,id=None,signer=None,password_encryptor=None): + def rand(len): + return ''.join( + random.choice("1234567890ABCDEFGHIJKLMNOPQRSTUWVXYZabcdefghijklmnopqrstuwvxyz") for _ in range(len)) + + self.password = rand(16) + print "Passphrase %s" % str(bytearray(self.passphrase())).encode("hex") + self.id = id + if id is None: + self.id = rand(8) + self.metadata_encryption = True + self.metadata = metadata + self.signer = signer + self.password_encryptor = password_encryptor + + def caesar_encrypted_password(self): + caesar_key = reduce(lambda sum,charcode: sum+charcode, map(ord,list(self.id))) + caesar_plaintext = map(ord, self.password) + caesar_ciphertext = Caesar().encrypt(caesar_plaintext,caesar_key) + return ''.join(map(chr,caesar_ciphertext)) + + def passphrase(self): + # new ScryptParameters(64, 8, 1,32, new Uint8List.fromList(new List())) + print "Password units: %s" % (map(ord, self.password.encode("utf-8")),) + sha = hashlib.sha256() + sha.update(self.password) + return sha.digest() # scrypt.hash(self.password.encode("utf-8"), '', 64, 8, 1, 32) + + def assemble(self, file): + (image_digest, encrypted_image) = self.encrypt(file) + self.metadata["hash"] = image_digest + unencrypted_metadata = bson.dumps(self.metadata) + if len(unencrypted_metadata) % 16 != 0: + unencrypted_metadata += ' ' * (16 - len(unencrypted_metadata) % 16) + (encryptor, iv) = self.encryptor(len(unencrypted_metadata)) + encrypted_metadata = [] + + encrypted_metadata += encryptor(unencrypted_metadata) + + encrypted_metadata = iv + str(bytearray(encrypted_metadata)) + + print "Metadata: %s" % str(encrypted_metadata).encode("base64").replace("\n", "") + print "%s %s" % (str(encrypted_metadata[:16]).encode("hex"), str(encrypted_metadata[16:]).encode("hex")) + print "Unencrypted: %s" % (unencrypted_metadata.encode("hex")) + print "Password %s" % self.password + + print bson.loads(unencrypted_metadata) + + fields = { + "image": encrypted_image, + "metadata_encryption": self.metadata_encryption, + "metadata": encrypted_metadata if self.metadata_encryption else self.metadata + } + + if self.signer is not None: + fields["signature"] = self.signer.signature(encrypted_metadata if self.metadata_encryption else bson.dumps(self.metadata)) + + if self.password_encryptor is not None: + fields["password"] = self.password_encryptor.encrypt_password(self.password) + + return bson.dumps(fields) + + def encryptor(self,length=0): + iv = os.urandom(16) + ctr = Counter.new(128, initial_value=long(iv.encode("hex"), 16)) + print "IV: %s" % iv.encode("hex") + cipher = AES.new(self.passphrase(), AES.MODE_CTR, counter=ctr) + + #salsa + # keysstream = bytearray(Salsa20_keystream(length,iv[:8],self.passphrase())) + # offset={"offset": 0} + def encrypt(data): + #Salsa20 + # array = bytearray(data) + # for i in range(len(array)): + # array[i] ^= keysstream[i+offset["offset"]] + # offset["offset"] += len(array) + # return str(array) + return cipher.encrypt(data) + + return (encrypt, iv) + + def encrypt(self, file): + filesize = os.path.getsize(file) + (encryptor, iv) = self.encryptor(filesize) + binary = [] + digest = hashlib.sha256() + with open(file, 'rb') as infile: + # binary += struct.pack('= len(self.charset) or key < 0: + key = key % len(self.charset) + + def addKey(charcode): + pos = self.charset.index(charcode) + if pos + key >= len(self.charset): + return self.charset[pos+key - len(self.charset)] + else: + return self.charset[pos+key] + return map(addKey,plaintext) + + def decrypt(self,ciphertext,key): + return self.encrypt(ciphertext,key*-1) + + +if __name__ == "__main__": + privateKey = "LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlKS0FJQkFBS0NBZ0VBdVRFSFZXanR3RnlkRy9WbUhLcjE1cXltSWt3WklhTm5RZUttQnp6bituODdaamRnCmtFRHRDRVlTcTNXMVZ4bGdVdzFEaDhLVFRyM0sxTFF3Y0RmeDh2ZEFyazk5T1g0bXhxcFlXUmJkZkVRazF3VUQKN2N0cjJFNWVLUWNDY2JBbC9PS1lGYS9Qa1kzS3NCdW5zMDZSbGY4NXNIU1o3Q2ZpcnFINkZvZWZyWERaVkFKTwptclFoVGgvMHFHbEJ5MWYxM2MyYTEyc0ZOT21uY0xzOFZwNGZ0SnhMcjlHb0U0dnNRdlgvNm9Rdmk3UHFXWW12Cmx3bVVVdmhDUFMvSlNGOGtDdXZtdmZnY0Y4T1hPajFRNml0T0xaRHQ4L1p1WmVvazduUzhKeGhSNzNIekhSNGEKSFducUM4VWtJeDFDUi9WdmxaaHAvN2diUy95MUVPUlJhVmxPM1lUa0YyVU1UdEYyQm9Dc1k1Z2xwZWFsRFY2RgpoOVlad3E2OEFQYjUvY2xldVh1OHRlYllwbzV5dXdIOUc4bTBicDdtSXJWVW5hQlBDRS9Xc2o2SEZVTG5pVWk4CjBOODE0OWNNMmtMbW03YytKTkM0UVV1cGMzaXkzcXlscWgxS0ZsVTBTV1BBaW5EYk9EU0hxblBpV1YwSUhqQzkKbVZlQW1NRGpnLzZSN3VhQXViTHowckpzby9vZGswa2ZXWU85OEQ0M0h2N0MrVEcvSWY5b0U3SmRzZ1NDQ2ZlVAp1YUtFNVhIM0MzR2VnY2lET3lyNlNkLzA4S3ByUXd2eTduWnBKSnh6cHlyMk1vSi9uOE82TzRJcUR0NzZFQVp6Cjc2blV2SnpQWFNvVUd5Nzk0MlFPd3l3cFJZQjZOYnJWcFBVZW1VQ3NVdHRVSnpoZ0hVNzBFbHFFMmhzQ0F3RUEKQVFLQ0FnQlZ5TVpTNUJZdDR6akx1TE1pV0hjcjhaSExsV0NXMm5COGJwYm1CMnFWK05YMUZYWGZuSk93SDNXSwpneTRMT0xBTUNMcmRhbDUrUFNCMXd6b2s0V1ZzWE9oVFZuTDFiT0JkYW84ZVJaODNid0Y1c05NUEZnblEyd3gzCkNNNHpCNXV4TnUyb0xoYUNvQ0hoemcyeTI1UkkzVDlGNDkxaExXQ3Q4RTQrblhoMENUQkM0QWExVnZlT2p2THcKQ0VINEJiallrK05FSWtqNUo3cnVWMjB0Mm5WcFhuVVk3bzlpN28vMHZLdGRZUG45UnN3cHljUFN2UmMrRDgzdgpOZkdSV3JFQmRpRWVvbzJ3M0dOekVQQW9hUUwveklETlg3dm43YnhKMFQxUEJzNzlrZFZDQ2EyakxtckNKUUlHCldnbFg5L05HWXFISFdPc2NHZVZ0KzdWU2paSWY4K3EzMmtoWGNoZUJVUCsrd3NEQzFZb3hpVDJhRHdWN3pYOGQKQmhCNk1NaTdRVXFra3dudXZYZHlVSVV5NEo3bEpIRHNDNWw0RVlEbWJoVmlUYXg4UXA0Mk1vYkozMnRIb1MxYwpoZk41dzRVc3F4N3lHUkwwdlVRbWJHcmg3SmJ5YXlENkNGWWlaT2luaE4wbWE3ZE1xZ2xRNGNoM0hQZU1jdDlUCkxza0YrZ1Nsb2wyZ1RDTVlqbDlad2Iyckc4dVN0aGpYRmQyQ0RDSGJpcXp6Umhza0hHMnZXdWh4dkFTUDR5ZUQKTmJtODVIOFpLcWhlamNXTDRYbEpJWksyWmJXVWpkTURQQ0REZDNpcHdRL0dVbVNvNklhTnhFRk1DRkxzay94bAoyQkdnOTl4RHVhY2xkS05VdGYrYU12NEhnbFc2YXQrVGFzVnNtV1hzRi9jV1IxSnVzUUtDQVFFQTg4SkVDUVRFCm04QUlPYUhZNXJPV3hrWUNzVnpWdzl6UkhPZklYYnZNTXI5azNPVEUvOWdOeTM1UVRlZTVVWVZFbmpSdVdQL3IKMmhSSmhiVGZ3UDVMbWN2c3hOM2NFM1NrcmxOQ3VsbzBUV0Q0Tzc4VytQbkVrU3pOKzFoMzlRT2ZBL1RUTjlwVgpucWg3QmY1dTlrQmlvb29QZVVlNnlVL2VNbFZhK216d05mREM5b2dTN28xLzFaeTR1aTRiZW8wWHBJdjVLTnRWClU1SzJ1RWN4MGpuQXpoY2QrSUt6RE5tN0h1VTgyS0NzWU5VQ2RISUpPcG9xMFdxUFRxYTM1MExQV0hxYlpBOW4KT2ErNVlvMUFETW4vck1Gb2hFcldxMnpqVWxPM3FGanlHQmFWQVFDWUNlcHJtazU2dms0Z1A0NFgyWWtXNTI0RwpIZy9DV1lJVEY0Mlc3UUtDQVFFQXduM1VEelMxTGVDWnE4MlRTbEZQV08zZG9qaDRmTXdzc1FXekprcmtKZzROCk5sQUdHdGVoN0syZUlGakFDazdiTGUwMzdGMlJoUHhrK1JaRUl5NEFkcjVvQWNOTEtYTUljYUF4OVhjYStKclgKUmpMQUdzQnVvT0pGWUJWbSsvWmx3b1ZraE9uWDFWT0o0WnQ0VE5PWWNJbXJoWnllVmpWZkpJN2lqVFU2MXY1TQoyKzZFa2MycHE2V0k3LzJobU0rVU93Nnh2cm9OdkN4MG5lV1ZnQVZkQ1dmV1dtQ2tVcWpzUzhGSjJ6OXEvOG9yCm5GUEtlQVQva2NEZzJOKzhMaVBlVmZBcWRsNkdaM1NZM2xaK1ErSkVKTUFaRXNGc2JoMHg1T1Q0NzUwWS9NQnoKcVFGMW9VMkhrVjV4YWg1dEFxRUNwMzcvRlNJeFBHMmUvcWtsR3FiTUp3S0NBUUEvcWtTVXltOERjdk56QWdlaApzZFEyY3dDdkRGMDBxODdlS3RlVlM4QjRuSzZlVnczdytLQVp2eU1BUmY2TUFTKzcxbHZmMk8vajVKcVRWbnQrCmI2eVZQY0xZWkUzZnN2NTBIWUQzSlNvMTc1YkdIQkhHODQxWWtZZzNGcE0ySmp6NURvL0FMY0ZoRUFZWnVhdUgKbTZkblpvSEdIbXIxdTJ1QVgvVEVKWUhUeE05WERwRkxSeC9WVGZxZGZmOWxVY2FVRG84RUVjMUdaN0pialJUQgpJSjV2OVcrNWdZRjhZTDNUd3ZIU2JweTNLY3RiV0pGNmdkdWZHS0xYV3duN1YrTEo3RFA3dlY0cm5lN2dnNTN2CitTNVRFNnpWcUtwSzJjVEJzdVhzRFZjbDNYZ0xhL2xKMWJWRzl5cHQyLy9tT1ptWEVvd3Q0L21yRGxpbmdpYTAKWnlHOUFvSUJBQXZCUVNUZUlha3hMdmlFUjBleG5HTWFsVSt0ckJLVzRJaWVlSkp5NmxFbWZrUHZHRDlkRXd0VAovditqZlkvaDdqdGJZQXJvS05ocHpNeUZQUDhIVTczWmlqMjVDUHpyVi9KT0pWYlpxUWFualNWUUFlNWIvUk82Ck9Va3lZZlFPazEzclpBVG9nVVFDZFkzdU1Kb3NHOFdKZkpWcHZ1UWsrNmdnL3E1Y0xqODZSWGp4ZEkrL1oyYloKYldwYjRQMTBabGdmYmp6QnoveTBqeVBQOFhZNFhNUXB3M25ZNTNXYmdoU2VEUkcwUm9wSk5OT1ErT2JDSHBRMwpuUjllZm1nbmdOT2UzV0lrR21sOFVaVDJrMUVWQnR0VnhPNHVSSmh2S1VuVlVQcGllMVVqYUpvcFhXdXZYT2M3ClZ3MTc0MGpLWkhldEpFdXBzbUlkbWdONFE2YlZ0L2NDZ2dFQkFJTG04YmZjMk9HYlpnTjRSVDQzMTVFa1dla2IKTW5nTUZFVEREZ242cDJqbWtWalhER2t1MW9YNTAyWDdCS3RlWitnRDRPcXk4QnRydnNoeXp2em9CZXJ0aHJEagpKNE0zdDd1RkMxNG12UmZkZWxSY0t5ZWQxM1NHelpOSnh4eEw3dk5xMk1lODF5ekJhTVBpQVpwRElONGF3RXpWClBRd1dNbW9nbzZzRXAyTkQyTmh2YlA0SHhsOFdYb0krM2hINWhldEUzMWt2QnpEbFdlazRXajA1OUJlZUloakYKQk5VeEl3YnFsU3d0KzJWdVkxN0xCekNlNFRucDNOcytwdEQ5Rm4ydm0rQXpsOXFCWTJWdnFPY2RwMFI4dG1lbwp3cGpyV0t3UDlMa3dDdDlaeFZ6Q1FCaUlGWXRwWjhhMkVhaHFUYk9sRkVzdmNEYlRmUWxmL2x5a2hqQT0KLS0tLS1FTkQgUlNBIFBSSVZBVEUgS0VZLS0tLS0K=" + publicKey = "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQ0lqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FnOEFNSUlDQ2dLQ0FnRUF1VEVIVldqdHdGeWRHL1ZtSEtyMQo1cXltSWt3WklhTm5RZUttQnp6bituODdaamRna0VEdENFWVNxM1cxVnhsZ1V3MURoOEtUVHIzSzFMUXdjRGZ4Cjh2ZEFyazk5T1g0bXhxcFlXUmJkZkVRazF3VUQ3Y3RyMkU1ZUtRY0NjYkFsL09LWUZhL1BrWTNLc0J1bnMwNlIKbGY4NXNIU1o3Q2ZpcnFINkZvZWZyWERaVkFKT21yUWhUaC8wcUdsQnkxZjEzYzJhMTJzRk5PbW5jTHM4VnA0Zgp0SnhMcjlHb0U0dnNRdlgvNm9Rdmk3UHFXWW12bHdtVVV2aENQUy9KU0Y4a0N1dm12ZmdjRjhPWE9qMVE2aXRPCkxaRHQ4L1p1WmVvazduUzhKeGhSNzNIekhSNGFIV25xQzhVa0l4MUNSL1Z2bFpocC83Z2JTL3kxRU9SUmFWbE8KM1lUa0YyVU1UdEYyQm9Dc1k1Z2xwZWFsRFY2Rmg5WVp3cTY4QVBiNS9jbGV1WHU4dGViWXBvNXl1d0g5RzhtMApicDdtSXJWVW5hQlBDRS9Xc2o2SEZVTG5pVWk4ME44MTQ5Y00ya0xtbTdjK0pOQzRRVXVwYzNpeTNxeWxxaDFLCkZsVTBTV1BBaW5EYk9EU0hxblBpV1YwSUhqQzltVmVBbU1EamcvNlI3dWFBdWJMejBySnNvL29kazBrZldZTzkKOEQ0M0h2N0MrVEcvSWY5b0U3SmRzZ1NDQ2ZlVHVhS0U1WEgzQzNHZWdjaURPeXI2U2QvMDhLcHJRd3Z5N25acApKSnh6cHlyMk1vSi9uOE82TzRJcUR0NzZFQVp6NzZuVXZKelBYU29VR3k3OTQyUU93eXdwUllCNk5iclZwUFVlCm1VQ3NVdHRVSnpoZ0hVNzBFbHFFMmhzQ0F3RUFBUT09Ci0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQo=" + encrypted = EncryptedScreenshot({"owner": u"test", "format": u"jpg", "title": u"5x5 Red", "timestamp": int(time.time()*1000)}, + signer=Signer.default(), + password_encryptor=EncryptedPassword(RSA.importKey(publicKey.decode("base64")),"PUBLIC")) + fixed_id = encrypted.id #"W9u9Zm0u" + print len(encrypted.passphrase()) + out = open("/home/marvin/Dokumente/IdeaProjects/EncryptedScreencloud/Frontend/WebApp/web/data/" + fixed_id, 'wb') + assembled = encrypted.assemble("5x5red.png") + out.write(assembled) + b = bson.loads(assembled) + del b["image"] + print b + #print assembled.encode("base64").replace("\n","") + print "http://localhost:8080/#%s%s" % (fixed_id, encrypted.password) + caesar_key = reduce(lambda sum,charcode: sum+charcode, map(ord,list(fixed_id))) + caesar_plaintext = map(ord,encrypted.password) + caesar_ciphertext = Caesar().encrypt(caesar_plaintext,caesar_key) + assert caesar_plaintext == Caesar().decrypt(caesar_ciphertext,caesar_key) + print "caesar_key=%s %s -> %s" % (caesar_key,encrypted.password,''.join(map(chr,caesar_ciphertext))) + print "http://localhost:8080/#%s%sC" % (fixed_id, encrypted.caesar_encrypted_password()) + print "http://localhost:8080/#%s" % (fixed_id, ) + out.close() diff --git a/modules/seafapi.py b/modules/seafapi.py index d9ea92a..7a5ff42 100644 --- a/modules/seafapi.py +++ b/modules/seafapi.py @@ -52,31 +52,43 @@ class SeafileLibrary: self.name = name self.owner = owner - def upload(self,file,file_name,directory): + def api_endpoint(self): + return "%s/repos/%s" % (self.client.api_endpoint(),self.id) + + def upload(self,file,file_name,directory,link=None): def obtain_link(): #curl -H "Authorization: Token f2210dacd9c6ccb8133606d94ff8e61d99b477fd" https://cloud.seafile.com/api2/repos/99b758e6-91ab-4265-b705-925367374cf0/upload-link/ - quoted = requests.get("%s/repos/%s/upload-link" % (self.client.api_endpoint(),self.id), headers = {'Authorization': "Token %s" % self.client.token.token,}).text + print "%s/upload-link" % self.api_endpoint() + quoted = requests.get("%s/upload-link" % self.api_endpoint(), headers = {'Authorization': "Token %s" % self.client.token.token,}).text return quoted[1:-1] #curl -H "Authorization: Token f2210dacd9c6ccb8133606d94ff8e61d99b477fd" -F file=@test.txt -F filename=test.txt -F parent_dir=/ http://cloud.seafile.com:8082/upload-api/73c5d117-3bcf-48a0-aa2a-3f48d5274ae3 - resp = requests.post(obtain_link(), - files={'file': (file_name,open(file, 'rb')), 'parent_dir': directory}, + resp = requests.post(link or obtain_link() , + files={'file': (file_name,open(file, 'rb')), 'parent_dir': directory, 'target_file': "%s/%s" % (directory,file_name)}, headers = {'Authorization': "Token %s" % self.client.token.token,} ) if resp.status_code == 200: return SeafileFile(self,("%s/%s" % (directory,file_name)).replace('//','/').replace('//','/'),resp.text) + return False - + #curl -H 'Authorization: Token f2210dacd3606d94ff8e61d99b477fd' -H 'Accept: application/json; charset=utf-8; indent=4' https://cloud.seafile.com/api2/repos/dae8cecc-2359-4d33-aa42-01b7846c4b32/file/detail/?p=/foo.c + def file_info(self,path): + resp=requests.get("%s/file/detail/?p=%s" % (self.api_endpoint(),path), headers = {'Authorization': "Token %s" % self.token.token, 'Accept': 'application/json; indent=4' }) + if resp.status_code == 200: + json = resp.json() + return SeafileFile(self,path,json['id'],json['size']) + return False def __str__(self): return "%s on %s by %s" % (self.name,self.client.server,self.owner) class SeafileFile: - def __init__(self,library,path,id): + def __init__(self,library,path,id,size=0): self.id = id self.path = path self.library = library + self.size = size #curl -v -X PUT -d "p=/foo.md" -H 'Authorization: Token f2210dacd9c6ccb8133606d94ff8e61d99b477fd' -H 'Accept: application/json; indent=4' https://cloud.seafile.com/api2/repos/afc3b694-7d4c-4b8a-86a4-89c9f3261b12/file/shared-link/ def share(self): @@ -86,6 +98,14 @@ class SeafileFile: ) return resp.headers.get("location") + def update(self,file): + def obtain_link(): + #curl -H "Authorization: Token f2210dacd9c6ccb8133606d94ff8e61d99b477fd" https://cloud.seafile.com/api2/repos/99b758e6-91ab-4265-b705-925367374cf0/upload-link/ + quoted = requests.get("%s/update-link" % self.library.api_endpoint(), headers = {'Authorization': "Token %s" % self.library.client.token.token,}).text + return quoted[1:-1] + directory, name = os.path.split(self.path) + return self.library.upload(file,name,directory,obtain_link()) + class SeafileToken: def __init__(self,username,token):