summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDwayne C. Litzenberger <dlitz@dlitz.net>2012-01-13 09:57:33 -0500
committerDwayne C. Litzenberger <dlitz@dlitz.net>2012-01-13 09:57:33 -0500
commitf34f3d3c1b26c0d163876e0927c3bb74b0f32a41 (patch)
treecef2413cfa8045c1a2f10a643a672d1b514d4a4b
parent0c2bb473529795d29ad43ce0d14162d1e2c19027 (diff)
parentb29a859ba577967a49950570f0b61d227c63ca86 (diff)
downloadpycrypto-f34f3d3c1b26c0d163876e0927c3bb74b0f32a41.tar.gz
Merge commit 'pkcs_20111222'
-rw-r--r--Doc/epydoc-config1
-rw-r--r--Doc/pycrypt.rst48
-rw-r--r--lib/Crypto/Cipher/PKCS1_OAEP.py255
-rw-r--r--lib/Crypto/Cipher/PKCS1_v1_5.py226
-rw-r--r--lib/Crypto/Cipher/__init__.py51
-rw-r--r--lib/Crypto/Hash/MD2.py49
-rw-r--r--lib/Crypto/Hash/MD4.py48
-rw-r--r--lib/Crypto/Hash/MD5.py31
-rw-r--r--lib/Crypto/Hash/RIPEMD.py31
-rw-r--r--lib/Crypto/Hash/SHA.py31
-rw-r--r--lib/Crypto/Hash/SHA224.py30
-rw-r--r--lib/Crypto/Hash/SHA256.py55
-rw-r--r--lib/Crypto/Hash/SHA384.py31
-rw-r--r--lib/Crypto/Hash/SHA512.py31
-rw-r--r--lib/Crypto/Hash/__init__.py24
-rw-r--r--lib/Crypto/Protocol/KDF.py120
-rw-r--r--lib/Crypto/Protocol/__init__.py19
-rw-r--r--lib/Crypto/PublicKey/RSA.py424
-rw-r--r--lib/Crypto/PublicKey/_RSA.py16
-rw-r--r--lib/Crypto/PublicKey/_slowmath.py54
-rw-r--r--lib/Crypto/Random/Fortuna/SHAd256.py7
-rw-r--r--lib/Crypto/SelfTest/Cipher/__init__.py2
-rw-r--r--lib/Crypto/SelfTest/Cipher/test_pkcs1_15.py174
-rw-r--r--lib/Crypto/SelfTest/Cipher/test_pkcs1_oaep.py372
-rw-r--r--lib/Crypto/SelfTest/Hash/common.py58
-rw-r--r--lib/Crypto/SelfTest/Hash/test_MD2.py4
-rw-r--r--lib/Crypto/SelfTest/Hash/test_MD4.py4
-rw-r--r--lib/Crypto/SelfTest/Hash/test_MD5.py4
-rw-r--r--lib/Crypto/SelfTest/Hash/test_RIPEMD.py4
-rw-r--r--lib/Crypto/SelfTest/Hash/test_SHA.py4
-rw-r--r--lib/Crypto/SelfTest/Hash/test_SHA224.py15
-rw-r--r--lib/Crypto/SelfTest/Hash/test_SHA256.py14
-rw-r--r--lib/Crypto/SelfTest/Hash/test_SHA384.py13
-rw-r--r--lib/Crypto/SelfTest/Hash/test_SHA512.py10
-rw-r--r--lib/Crypto/SelfTest/Protocol/test_KDF.py98
-rw-r--r--lib/Crypto/SelfTest/PublicKey/test_RSA.py26
-rw-r--r--lib/Crypto/SelfTest/PublicKey/test_importKey.py160
-rw-r--r--lib/Crypto/SelfTest/Random/Fortuna/test_FortunaAccumulator.py6
-rw-r--r--lib/Crypto/SelfTest/Random/Fortuna/test_SHAd256.py2
-rw-r--r--lib/Crypto/SelfTest/Signature/__init__.py40
-rw-r--r--lib/Crypto/SelfTest/Signature/test_pkcs1_15.py219
-rw-r--r--lib/Crypto/SelfTest/Signature/test_pkcs1_pss.py446
-rw-r--r--lib/Crypto/SelfTest/Util/test_asn1.py15
-rw-r--r--lib/Crypto/SelfTest/Util/test_number.py6
-rw-r--r--lib/Crypto/SelfTest/__init__.py1
-rw-r--r--lib/Crypto/Signature/PKCS1_PSS.py355
-rw-r--r--lib/Crypto/Signature/PKCS1_v1_5.py236
-rw-r--r--lib/Crypto/Signature/__init__.py31
-rw-r--r--lib/Crypto/Util/asn1.py386
-rw-r--r--lib/Crypto/Util/py3compat.py53
-rw-r--r--lib/Crypto/Util/wrapper.py47
-rw-r--r--lib/Crypto/__init__.py27
-rw-r--r--setup.py69
-rw-r--r--src/MD2.c13
-rw-r--r--src/MD4.c2
-rw-r--r--src/RIPEMD160.c2
-rw-r--r--src/SHA224.c74
-rw-r--r--src/SHA256.c251
-rw-r--r--src/SHA384.c80
-rw-r--r--src/SHA512.c80
-rw-r--r--src/_fastmath.c75
-rw-r--r--src/hash_SHA2.h104
-rw-r--r--src/hash_SHA2_template.c199
-rw-r--r--src/hash_template.c20
64 files changed, 4775 insertions, 608 deletions
diff --git a/Doc/epydoc-config b/Doc/epydoc-config
index ea7f343..018e461 100644
--- a/Doc/epydoc-config
+++ b/Doc/epydoc-config
@@ -16,4 +16,5 @@ link: <a href="http://www.pycrypto.org/">PyCrypto.org</a>
# The documentation is usually built on a Linux machine; nt.py tries to
# import the winrandom module.
exclude-introspect: ^Crypto\.Random\.OSRNG\.nt|Crypto\.Util\.winrandom$
+exclude-introspect: ^Crypto\.Util\.osentropy\.nt$
exclude: ^Crypto\.SelfTest
diff --git a/Doc/pycrypt.rst b/Doc/pycrypt.rst
index 5551071..9c6d770 100644
--- a/Doc/pycrypt.rst
+++ b/Doc/pycrypt.rst
@@ -709,7 +709,7 @@ Constructs a key object from a tuple of data. This is
algorithm-specific; look at the source code for the details. (To be
documented later.)
-**generate(size, randfunc, progress_func=None)**:
+**generate(size, randfunc, progress_func=None, e=65537)**:
Generate a fresh public/private key pair. ``size`` is a
algorithm-dependent size parameter, usually measured in bits; the
larger it is, the more difficult it will be to break the key. Safe
@@ -729,6 +729,12 @@ string containing the key parameter currently being generated; it's
useful for interactive applications where a user is waiting for a key
to be generated.
+``e`` is the public RSA exponent, and must be an odd positive integer.
+It is typically a small number with very few ones in its binary representation.
+The default value 65537 (=0b10000000000000001) is a safe choice: other
+common values are 5, 7, 17, and 257. Exponent 3 is also widely used,
+but it requires very special care when padding the message.
+
If you want to interface with some other program, you will have to know
the details of the algorithm being used; this isn't a big loss. If you
don't care about working with non-Python software, simply use the
@@ -736,10 +742,48 @@ don't care about working with non-Python software, simply use the
file. It's portable across all the architectures that Python supports,
and it's simple to use.
+In case interoperability were important, RSA key objects can be exported
+and imported in two standard formats: the DER binary encoding specified in
+PKCS#1 (see RFC3447) or the ASCII textual encoding specified by the
+old Privacy Enhanced Mail services (PEM, see RFC1421).
+
+
+The RSA module makes the following function available for importing keys:
+
+**importKey(externKey)**:
+Import an RSA key (pubic or private) encoded as a string ``externKey``.
+The key can follow either the PKCS#1/DER format (binary) or the PEM format
+(7-bit ASCII).
+
+For instance:
+ >>> from Crypto.PublicKey import RSA
+ >>> f = open("mykey.pem")
+ >>> RSAkey = RSA.importKey(f.read())
+ >>> if RSAkey.has_private(): print "Private key"
+
+Every RSA object supports the following method to export itself:
+
+**exportKey(format='PEM')**:
+Return the key encoded as a string, according to the specified ``format``:
+``'PEM'`` (default) or ``'DER'`` (also known as PKCS#1).
+
+For instance:
+ >>> from Crypto.PublicKey import RSA
+ >>> from Crypto import Random
+ >>> rng = Random.new().read
+ >>> RSAkey = RSA.generate(1024, rng)
+ >>> f = open("keyPrivate.der","w+")
+ >>> f.write(RSAkey.exportKey("DER"))
+ >>> f.close()
+ >>> f = open("keyPublic.pem","w+")
+ >>> f.write(RSAkey.publickey().exportKey("PEM"))
+ >>> f.close()
+
Public-key objects always support the following methods. Some of them
may raise exceptions if their functionality is not supported by the
algorithm.
+
**can_blind()**:
Returns true if the algorithm is capable of blinding data;
returns false otherwise.
@@ -876,6 +920,8 @@ levels of security: low-security commercial, high-security commercial,
and military-grade. For RSA, these three levels correspond roughly to
768, 1024, and 2048-bit keys.
+When exporting private keys you should always carefully ensure that the
+chosen storage location cannot be accessed by adversaries.
Crypto.Util: Odds and Ends
--------------------------------------------------
diff --git a/lib/Crypto/Cipher/PKCS1_OAEP.py b/lib/Crypto/Cipher/PKCS1_OAEP.py
new file mode 100644
index 0000000..f02609d
--- /dev/null
+++ b/lib/Crypto/Cipher/PKCS1_OAEP.py
@@ -0,0 +1,255 @@
+# -*- coding: utf-8 -*-
+#
+# Cipher/PKCS1_OAEP.py : PKCS#1 OAEP
+#
+# ===================================================================
+# 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.
+# ===================================================================
+
+"""RSA encryption protocol according to PKCS#1 OAEP
+
+See RFC3447__ or the `original RSA Labs specification`__ .
+
+This scheme is more properly called ``RSAES-OAEP``.
+
+As an example, a sender may encrypt a message in this way:
+
+ >>> from Crypto.Cipher import PKCS1_OAEP
+ >>> from Crypto.PublicKey import RSA
+ >>>
+ >>> message = 'To be encrypted'
+ >>> key = RSA.importKey(open('pubkey.der').read())
+ >>> cipher = PKCS1_OAEP.new(key)
+ >>> ciphertext = cipher.encrypt(message)
+
+At the receiver side, decryption can be done using the private part of
+the RSA key:
+
+ >>> key = RSA.importKey(open('privkey.der').read())
+ >>> cipher = PKCS1_OAP.new(key)
+ >>> message = cipher.decrypt(ciphertext)
+
+:undocumented: __revision__, __package__
+
+.. __: http://www.ietf.org/rfc/rfc3447.txt
+.. __: http://www.rsa.com/rsalabs/node.asp?id=2125.
+"""
+
+from __future__ import nested_scopes
+
+__revision__ = "$Id$"
+__all__ = [ 'new' ]
+
+import Crypto.Signature.PKCS1_PSS
+import Crypto.Hash.SHA
+
+from Crypto.Util.py3compat import *
+import Crypto.Util.number
+from Crypto.Util.number import ceil_div
+from Crypto.Util.strxor import strxor
+
+class PKCS1OAEP_Cipher:
+ """This cipher can perform PKCS#1 v1.5 OAEP encryption or decryption."""
+
+ def __init__(self, key, hashAlgo, mgfunc, label):
+ """Initialize this PKCS#1 OAEP cipher object.
+
+ :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.
+ 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.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 : string
+ A label to apply to this particular encryption. If not specified,
+ an empty string is used. Specifying a label does not improve
+ security.
+
+ :attention: Modify the mask generation function only if you know what you are doing.
+ Sender and receiver must use the same one.
+ """
+ self._key = key
+
+ if hashAlgo:
+ self._hashObj = hashAlgo
+ else:
+ self._hashObj = Crypto.Hash.SHA
+
+ if mgfunc:
+ self._mgf = mgfunc
+ else:
+ self._mgf = lambda x,y: Crypto.Signature.PKCS1_PSS.MGF1(x,y,self._hashObj)
+
+ self._label = label
+
+ def can_encrypt(self):
+ """Return True/1 if this cipher object can be used for encryption."""
+ return self._key.can_encrypt()
+
+ def can_decrypt(self):
+ """Return True/1 if this cipher object can be used for decryption."""
+ return self._key.can_decrypt()
+
+ def encrypt(self, message):
+ """Produce the PKCS#1 OAEP encryption of a message.
+
+ This function is named ``RSAES-OAEP-ENCRYPT``, and is specified in
+ section 7.1.1 of RFC3447.
+
+ :Parameters:
+ 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 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
+ message.
+ """
+ # TODO: Verify the key is RSA
+
+ randFunc = self._key._randfunc
+
+ # See 7.1.1 in RFC3447
+ modBits = Crypto.Util.number.size(self._key.n)
+ k = ceil_div(modBits,8) # Convert from bits to bytes
+ hLen = self._hashObj.digest_size
+ mLen = len(message)
+
+ # Step 1b
+ ps_len = k-mLen-2*hLen-2
+ if ps_len<0:
+ raise ValueError("Plaintext is too long.")
+ # Step 2a
+ lHash = self._hashObj.new(self._label).digest()
+ # Step 2b
+ ps = bchr(0x00)*ps_len
+ # Step 2c
+ db = lHash + ps + bchr(0x01) + message
+ # Step 2d
+ ros = randFunc(hLen)
+ # Step 2e
+ dbMask = self._mgf(ros, k-hLen-1)
+ # Step 2f
+ maskedDB = strxor(db, dbMask)
+ # Step 2g
+ seedMask = self._mgf(maskedDB, hLen)
+ # Step 2h
+ maskedSeed = strxor(ros, seedMask)
+ # Step 2i
+ em = bchr(0x00) + maskedSeed + maskedDB
+ # Step 3a (OS2IP), step 3b (RSAEP), part of step 3c (I2OSP)
+ m = self._key.encrypt(em, 0)[0]
+ # Complete step 3c (I2OSP)
+ c = bchr(0x00)*(k-len(m)) + m
+ return c
+
+ def decrypt(self, ct):
+ """Decrypt a PKCS#1 OAEP ciphertext.
+
+ This function is named ``RSAES-OAEP-DECRYPT``, and is specified in
+ section 7.1.2 of RFC3447.
+
+ :Parameters:
+ ct : string
+ The ciphertext that contains the message to recover.
+
+ :Return: A string, the original message.
+ :Raise ValueError:
+ If the ciphertext length is incorrect, or if the decryption does not
+ succeed.
+ :Raise TypeError:
+ If the RSA key has no private half.
+ """
+ # TODO: Verify the key is RSA
+
+ # See 7.1.2 in RFC3447
+ modBits = Crypto.Util.number.size(self._key.n)
+ k = ceil_div(modBits,8) # Convert from bits to bytes
+ hLen = self._hashObj.digest_size
+
+ # Step 1b and 1c
+ if len(ct) != k or k<hLen+2:
+ raise ValueError("Ciphertext with incorrect length.")
+ # Step 2a (O2SIP), 2b (RSADP), and part of 2c (I2OSP)
+ m = self._key.decrypt(ct)
+ # Complete step 2c (I2OSP)
+ em = bchr(0x00)*(k-len(m)) + m
+ # Step 3a
+ lHash = self._hashObj.new(self._label).digest()
+ # Step 3b
+ y = em[0]
+ # y must be 0, but we MUST NOT check it here in order not to
+ # allow attacks like Manger's (http://dl.acm.org/citation.cfm?id=704143)
+ maskedSeed = em[1:hLen+1]
+ maskedDB = em[hLen+1:]
+ # Step 3c
+ seedMask = self._mgf(maskedDB, hLen)
+ # Step 3d
+ seed = strxor(maskedSeed, seedMask)
+ # Step 3e
+ dbMask = self._mgf(seed, k-hLen-1)
+ # Step 3f
+ db = strxor(maskedDB, dbMask)
+ # Step 3g
+ valid = 1
+ one = db[hLen:].find(bchr(0x01))
+ lHash1 = db[:hLen]
+ if lHash1!=lHash:
+ valid = 0
+ if one<0:
+ valid = 0
+ if bord(y)!=0:
+ valid = 0
+ if not valid:
+ raise ValueError("Incorrect decryption.")
+ # Step 4
+ return db[hLen+one+1:]
+
+def new(key, hashAlgo=None, mgfunc=None, label=b('')):
+ """Return a cipher object `PKCS1OAEP_Cipher` that can be used to perform PKCS#1 OAEP encryption or decryption.
+
+ :Parameters:
+ key : RSA key object
+ The key to use to encrypt or decrypt the message. This is a `Crypto.PublicKey.RSA` object.
+ Decryption is only possible if *key* is a private RSA key.
+ 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.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 : string
+ A label to apply to this particular encryption. If not specified,
+ an empty string is used. Specifying a label does not improve
+ security.
+
+ :attention: Modify the mask generation function only if you know what you are doing.
+ Sender and receiver must use the same one.
+ """
+ return PKCS1OAEP_Cipher(key, hashAlgo, mgfunc, label)
+
diff --git a/lib/Crypto/Cipher/PKCS1_v1_5.py b/lib/Crypto/Cipher/PKCS1_v1_5.py
new file mode 100644
index 0000000..3f860ee
--- /dev/null
+++ b/lib/Crypto/Cipher/PKCS1_v1_5.py
@@ -0,0 +1,226 @@
+# -*- coding: utf-8 -*-
+#
+# Cipher/PKCS1-v1_5.py : PKCS#1 v1.5
+#
+# ===================================================================
+# 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.
+# ===================================================================
+
+"""RSA encryption protocol according to PKCS#1 v1.5
+
+See RFC3447__ or the `original RSA Labs specification`__ .
+
+This scheme is more properly called ``RSAES-PKCS1-v1_5``.
+
+**If you are designing a new protocol, consider using the more robust PKCS#1 OAEP.**
+
+As an example, a sender may encrypt a message in this way:
+
+ >>> from Crypto.Cipher import PKCS1_v1_5
+ >>> from Crypto.PublicKey import RSA
+ >>> from Crypto.Hash import SHA
+ >>>
+ >>> message = 'To be encrypted'
+ >>> h = SHA.new(message)
+ >>>
+ >>> key = RSA.importKey(open('pubkey.der').read())
+ >>> cipher = PKCS1_v1_5.new(key)
+ >>> ciphertext = cipher.encrypt(message+h.digest())
+
+At the receiver side, decryption can be done using the private part of
+the RSA key:
+
+ >>> From Crypto.Hash import SHA
+ >>> from Crypto import Random
+ >>>
+ >>> key = RSA.importKey(open('privkey.der').read())
+ >>>
+ >>> dsize = SHA.digest_size
+ >>> sentinel = Random.new().read(15+dsize) # Let's assume that average data length is 15
+ >>>
+ >>> cipher = PKCS1_v1_5.new(key)
+ >>> message = cipher.decrypt(ciphertext, sentinel)
+ >>>
+ >>> digest = SHA.new(message[:-dsize]).digest()
+ >>> if digest==message[-dsize:]: # Note how we DO NOT look for the sentinel
+ >>> print "Encryption was correct."
+ >>> else:
+ >>> print "Encryption was not correct."
+
+:undocumented: __revision__, __package__
+
+.. __: http://www.ietf.org/rfc/rfc3447.txt
+.. __: http://www.rsa.com/rsalabs/node.asp?id=2125.
+"""
+
+__revision__ = "$Id$"
+__all__ = [ 'new' ]
+
+from Crypto.Util.number import ceil_div
+from Crypto.Util.py3compat import *
+import Crypto.Util.number
+
+class PKCS115_Cipher:
+ """This cipher can perform PKCS#1 v1.5 RSA encryption or decryption."""
+
+ def __init__(self, key):
+ """Initialize this PKCS#1 v1.5 cipher object.
+
+ :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.
+ """
+ self._key = key
+
+ def can_encrypt(self):
+ """Return True if this cipher object can be used for encryption."""
+ return self._key.can_encrypt()
+
+ def can_decrypt(self):
+ """Return True if this cipher object can be used for decryption."""
+ return self._key.can_decrypt()
+
+ def encrypt(self, message):
+ """Produce the PKCS#1 v1.5 encryption of a message.
+
+ This function is named ``RSAES-PKCS1-V1_5-ENCRYPT``, and is specified in
+ section 7.2.1 of RFC3447.
+ For a complete example see `Crypto.Cipher.PKCS1_v1_5`.
+
+ :Parameters:
+ message : byte 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 11.
+
+ :Return: A byte 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
+ message.
+
+ """
+ # TODO: Verify the key is RSA
+
+ randFunc = self._key._randfunc
+
+ # See 7.2.1 in RFC3447
+ modBits = Crypto.Util.number.size(self._key.n)
+ k = ceil_div(modBits,8) # Convert from bits to bytes
+ mLen = len(message)
+
+ # Step 1
+ if mLen > k-11:
+ raise ValueError("Plaintext is too long.")
+ # Step 2a
+ class nonZeroRandByte:
+ def __init__(self, rf): self.rf=rf
+ def __call__(self, c):
+ while bord(c)==0x00: c=self.rf(1)[0]
+ return c
+ ps = tobytes(map(nonZeroRandByte(randFunc), randFunc(k-mLen-3)))
+ # Step 2b
+ em = b('\x00\x02') + ps + bchr(0x00) + message
+ # Step 3a (OS2IP), step 3b (RSAEP), part of step 3c (I2OSP)
+ m = self._key.encrypt(em, 0)[0]
+ # Complete step 3c (I2OSP)
+ c = bchr(0x00)*(k-len(m)) + m
+ return c
+
+ def decrypt(self, ct, sentinel):
+ """Decrypt a PKCS#1 v1.5 ciphertext.
+
+ This function is named ``RSAES-PKCS1-V1_5-DECRYPT``, and is specified in
+ section 7.2.2 of RFC3447.
+ For a complete example see `Crypto.Cipher.PKCS1_v1_5`.
+
+ :Parameters:
+ ct : byte string
+ The ciphertext that contains the message to recover.
+ sentinel : any type
+ The object to return to indicate that an error was detected during decryption.
+
+ :Return: A byte string. It is either the original message or the ``sentinel`` (in case of an error).
+ :Raise ValueError:
+ If the ciphertext length is incorrect
+ :Raise TypeError:
+ If the RSA key has no private half.
+
+ :attention:
+ You should **never** let the party who submitted the ciphertext know that
+ this function returned the ``sentinel`` value.
+ Armed with such knowledge (for a fair amount of carefully crafted but invalid ciphertexts),
+ an attacker is able to recontruct the plaintext of any other encryption that were carried out
+ with the same RSA public key (see `Bleichenbacher's`__ attack).
+
+ In general, it should not be possible for the other party to distinguish
+ whether processing at the server side failed because the value returned
+ was a ``sentinel`` as opposed to a random, invalid message.
+
+ In fact, the second option is not that unlikely: encryption done according to PKCS#1 v1.5
+ embeds no good integrity check. There is roughly one chance
+ in 2^16 for a random ciphertext to be returned as a valid message
+ (although random looking).
+
+ It is therefore advisabled to:
+
+ 1. Select as ``sentinel`` a value that resembles a plausable random, invalid message.
+ 2. Not report back an error as soon as you detect a ``sentinel`` value.
+ Put differently, you should not explicitly check if the returned value is the ``sentinel`` or not.
+ 3. Cover all possible errors with a single, generic error indicator.
+ 4. Embed into the definition of ``message`` (at the protocol level) a digest (e.g. ``SHA-1``).
+ It is recommended for it to be the rightmost part ``message``.
+ 5. Where possible, monitor the number of errors due to ciphertexts originating from the same party,
+ and slow down the rate of the requests from such party (or even blacklist it altogether).
+
+ **If you are designing a new protocol, consider using the more robust PKCS#1 OAEP.**
+
+ .. __: http://www.bell-labs.com/user/bleichen/papers/pkcs.ps
+
+ """
+
+ # TODO: Verify the key is RSA
+
+ # See 7.2.1 in RFC3447
+ modBits = Crypto.Util.number.size(self._key.n)
+ k = ceil_div(modBits,8) # Convert from bits to bytes
+
+ # Step 1
+ if len(ct) != k:
+ raise ValueError("Ciphertext with incorrect length.")
+ # Step 2a (O2SIP), 2b (RSADP), and part of 2c (I2OSP)
+ m = self._key.decrypt(ct)
+ # Complete step 2c (I2OSP)
+ em = bchr(0x00)*(k-len(m)) + m
+ # Step 3
+ sep = em.find(bchr(0x00),2)
+ if not em.startswith(b('\x00\x02')) or sep<10:
+ return sentinel
+ # Step 4
+ return em[sep+1:]
+
+def new(key):
+ """Return a cipher object `PKCS115_Cipher` that can be used to perform PKCS#1 v1.5 encryption or decryption.
+
+ :Parameters:
+ key : RSA key object
+ The key to use to encrypt or decrypt the message. This is a `Crypto.PublicKey.RSA` object.
+ Decryption is only possible if *key* is a private RSA key.
+
+ """
+ return PKCS115_Cipher(key)
+
diff --git a/lib/Crypto/Cipher/__init__.py b/lib/Crypto/Cipher/__init__.py
index d8ceed9..70b35c2 100644
--- a/lib/Crypto/Cipher/__init__.py
+++ b/lib/Crypto/Cipher/__init__.py
@@ -18,32 +18,63 @@
# SOFTWARE.
# ===================================================================
-"""Secret-key encryption algorithms.
+"""Symmetric- and asymmetric-key encryption algorithms.
-Secret-key encryption algorithms transform plaintext in some way that
-is dependent on a key, producing ciphertext. This transformation can
-easily be reversed, if (and, hopefully, only if) one knows the key.
+Encryption algorithms transform plaintext in some way that
+is dependent on a key or key pair, producing ciphertext.
-The encryption modules here all support the interface described in PEP
+Symmetric algorithms
+--------------------
+
+Encryption can easily be reversed, if (and, hopefully, only if)
+one knows the same key.
+In other words, sender and receiver share the same key.
+
+The symmetric encryption modules here all support the interface described in PEP
272, "API for Block Encryption Algorithms".
If you don't know which algorithm to choose, use AES because it's
standard and has undergone a fair bit of examination.
+====================== ====================
+Module name Description
+====================== ====================
Crypto.Cipher.AES Advanced Encryption Standard
Crypto.Cipher.ARC2 Alleged RC2
Crypto.Cipher.ARC4 Alleged RC4
-Crypto.Cipher.Blowfish
-Crypto.Cipher.CAST
-Crypto.Cipher.DES The Data Encryption Standard. Very commonly used
- in the past, but today its 56-bit keys are too small.
+Crypto.Cipher.Blowfish Blowfish
+Crypto.Cipher.CAST CAST
+Crypto.Cipher.DES The Data Encryption Standard.
+ Very commonly used in the past,
+ but today its 56-bit keys are too small.
Crypto.Cipher.DES3 Triple DES.
Crypto.Cipher.XOR The simple XOR cipher.
+====================== ====================
+
+
+Asymmetric algorithms
+---------------------
+
+For asymmetric algorithms, the key to be used for decryption is totally
+different and cannot be derived in a feasible way from the key used
+for encryption. Put differently, sender and receiver each own one half
+of a key pair. The encryption key is often called ``public`` whereas
+the decryption key is called ``private``.
+
+======================== =======================
+Module name Description
+======================== =======================
+Crypto.Cipher.PKCS1_v1.5 PKCS#1 v1.5 encryption, based on RSA key pairs
+Crypto.Cipher.PKCS1_OAEP PKCS#1 OAEP encryption, based on RSA key pairs
+======================== =======================
+
+:undocumented: __revision__, __package__
"""
__all__ = ['AES', 'ARC2', 'ARC4',
'Blowfish', 'CAST', 'DES', 'DES3',
- 'XOR'
+ 'XOR',
+ 'PKCS1_v1_5', 'PKCS1_OAEP'
]
__revision__ = "$Id$"
diff --git a/lib/Crypto/Hash/MD2.py b/lib/Crypto/Hash/MD2.py
new file mode 100644
index 0000000..953f763
--- /dev/null
+++ b/lib/Crypto/Hash/MD2.py
@@ -0,0 +1,49 @@
+# -*- 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.
+# ===================================================================
+
+__revision__ = "$Id$"
+
+__all__ = ['new', 'digest_size']
+
+from Crypto.Util.wrapper import Wrapper
+from Crypto.Util.py3compat import *
+
+# The OID for MD2 is:
+#
+# id-md2 OBJECT IDENTIFIER ::= {
+# iso(1) member-body(2) us(840) rsadsi(113549)
+# digestAlgorithm(2) 2
+# }
+
+oid = b('\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x02')
+
+def new(data=b("")):
+ obj = Wrapper(hashFactory, data)
+ obj.oid = oid
+ obj.new = globals()['new']
+ if not hasattr(obj, 'digest_size'):
+ obj.digest_size = digest_size
+ return obj
+
+import Crypto.Hash._MD2 as _MD2
+hashFactory = _MD2
+
+digest_size = 16
+
diff --git a/lib/Crypto/Hash/MD4.py b/lib/Crypto/Hash/MD4.py
new file mode 100644
index 0000000..8a9f595
--- /dev/null
+++ b/lib/Crypto/Hash/MD4.py
@@ -0,0 +1,48 @@
+# -*- 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.
+# ===================================================================
+
+__revision__ = "$Id$"
+
+__all__ = ['new', 'digest_size']
+
+from Crypto.Util.wrapper import Wrapper
+from Crypto.Util.py3compat import *
+
+# The OID for MD4 is:
+#
+# id-md2 OBJECT IDENTIFIER ::= {
+# iso(1) member-body(2) us(840) rsadsi(113549)
+# digestAlgorithm(2) 4
+# }
+
+oid = b('\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x04')
+
+def new(data=b("")):
+ obj = Wrapper(hashFactory, data)
+ obj.oid = oid
+ obj.new = globals()['new']
+ if not hasattr(obj, 'digest_size'):
+ obj.digest_size = digest_size
+ return obj
+
+import Crypto.Hash._MD4 as _MD4
+hashFactory = _MD4
+
+digest_size = 16
diff --git a/lib/Crypto/Hash/MD5.py b/lib/Crypto/Hash/MD5.py
index abc0b1d..91e5da7 100644
--- a/lib/Crypto/Hash/MD5.py
+++ b/lib/Crypto/Hash/MD5.py
@@ -24,21 +24,32 @@ __revision__ = "$Id$"
__all__ = ['new', 'digest_size']
+from Crypto.Util.wrapper import Wrapper
from Crypto.Util.py3compat import *
+# The OID for MD5 is:
+#
+# id-md5 OBJECT IDENTIFIER ::= {
+# iso(1) member-body(2) us(840) rsadsi(113549)
+# digestAlgorithm(2) 5
+# }
+oid = b('\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x05')
+
+def new(data=b("")):
+ obj = Wrapper(hashFactory, data)
+ obj.oid = oid
+ obj.new = globals()['new']
+ if not hasattr(obj, 'digest_size'):
+ obj.digest_size = digest_size
+ return obj
+
try:
# The md5 module is deprecated in Python 2.6, so use hashlib when possible.
import hashlib
-
- def new(data=b("")):
- return hashlib.md5(data)
- digest_size = new().digest_size
+ hashFactory = hashlib.md5
except ImportError:
- from md5 import *
-
import md5
- if hasattr(md5, 'digestsize'):
- digest_size = digestsize
- del digestsize
- del md5
+ hashFactory = md5
+
+digest_size = 16
diff --git a/lib/Crypto/Hash/RIPEMD.py b/lib/Crypto/Hash/RIPEMD.py
index 5e81ad3..4a6c7bf 100644
--- a/lib/Crypto/Hash/RIPEMD.py
+++ b/lib/Crypto/Hash/RIPEMD.py
@@ -18,4 +18,33 @@
# SOFTWARE.
# ===================================================================
-from Crypto.Hash.RIPEMD160 import new, digest_size
+__revision__ = "$Id$"
+
+__all__ = ['new', 'digest_size']
+
+from Crypto.Util.wrapper import Wrapper
+from Crypto.Util.py3compat import *
+
+#
+# See http://homes.esat.kuleuven.be/~bosselae/ripemd160.html#More
+#
+# id-ripemd160 OBJECT IDENTIFIER ::= {
+# iso(1) identified-organization(3) teletrust(36)
+# algorithm(3) hashAlgorithm(2) ripemd160(1)
+# }
+
+oid = b("\x06\x05\x2b\x24\x03\x02\x01")
+
+def new(data=b("")):
+ obj = Wrapper(hashFactory, data)
+ obj.oid = oid
+ obj.new = globals()['new']
+ if not hasattr(obj, 'digest_size'):
+ obj.digest_size = digest_size
+ return obj
+
+import Crypto.Hash._RIPEMD160 as _RIPEMD160
+hashFactory = _RIPEMD160
+
+digest_size = 20
+
diff --git a/lib/Crypto/Hash/SHA.py b/lib/Crypto/Hash/SHA.py
index 12758f5..e9cd118 100644
--- a/lib/Crypto/Hash/SHA.py
+++ b/lib/Crypto/Hash/SHA.py
@@ -25,19 +25,32 @@ __revision__ = "$Id$"
__all__ = ['new', 'digest_size']
from Crypto.Util.py3compat import *
+from Crypto.Util.wrapper import Wrapper
+
+# The OID for SHA-1 is:
+#
+# id-sha1 OBJECT IDENTIFIER ::= {
+# iso(1) identified-organization(3) oiw(14) secsig(3)
+# algorithms(2) 26
+# }
+oid = b('\x06\x05\x2b\x0e\x03\x02\x1a')
+
+def new(data=b("")):
+ obj = Wrapper(hashFactory, data)
+ obj.oid = oid
+ obj.new = globals()['new']
+ if not hasattr(obj, 'digest_size'):
+ obj.digest_size = digest_size
+ return obj
try:
- # The md5 module is deprecated in Python 2.6, so use hashlib when possible.
+ # The sha module is deprecated in Python 2.6, so use hashlib when possible.
import hashlib
- def new(data=b("")):
- return hashlib.sha1(data)
- digest_size = new().digest_size
+ hashFactory = hashlib.sha1
except ImportError:
- from sha import *
import sha
- if hasattr(sha, 'digestsize'):
- digest_size = digestsize
- del digestsize
- del sha
+ hashFactory = sha
+
+digest_size = 20
block_size = 64
diff --git a/lib/Crypto/Hash/SHA224.py b/lib/Crypto/Hash/SHA224.py
index 7499a85..872ed81 100644
--- a/lib/Crypto/Hash/SHA224.py
+++ b/lib/Crypto/Hash/SHA224.py
@@ -24,9 +24,33 @@ __revision__ = "$Id$"
__all__ = ['new', 'digest_size']
-from hashlib import sha224 # This will only work in versions of Python that support SHA224
+from Crypto.Util.wrapper import Wrapper
from Crypto.Util.py3compat import *
+
+# The OID for SHA-224 is:
+#
+# id-sha224 OBJECT IDENTIFIER ::= {
+# joint-iso-itu-t(2)
+# country(16) us(840) organization(1) gov(101) csor(3)
+# nistalgorithm(4) hashalgs(2) 4
+# }
+oid = b('\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x04')
+
def new(data=b("")):
- return sha224(data)
-digest_size = new().digest_size
+ obj = Wrapper(hashFactory, data)
+ obj.oid = oid
+ obj.new = globals()['new']
+ if not hasattr(obj, 'digest_size'):
+ obj.digest_size = digest_size
+ return obj
+
+try:
+ import hashlib
+ hashFactory = hashlib.sha224
+
+except ImportError:
+ from Crypto.Hash import _SHA224
+ hashFactory = _SHA224
+
+digest_size = 28
block_size = 64
diff --git a/lib/Crypto/Hash/SHA256.py b/lib/Crypto/Hash/SHA256.py
new file mode 100644
index 0000000..f583ed9
--- /dev/null
+++ b/lib/Crypto/Hash/SHA256.py
@@ -0,0 +1,55 @@
+# -*- 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.
+# ===================================================================
+
+__revision__ = "$Id$"
+
+__all__ = ['new', 'digest_size']
+
+from Crypto.Util.wrapper import Wrapper
+from Crypto.Util.py3compat import *
+
+# The OID for SHA-256 is:
+#
+# id-sha256 OBJECT IDENTIFIER ::= {
+# joint-iso-itu-t(2) country(16) us(840) organization(1)
+# gov(101) csor(3) nistalgorithm(4) hashalgs(2) 1
+# }
+#
+oid = b('\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01')
+
+def new(data=b("")):
+ obj = Wrapper(hashFactory, data)
+ obj.oid = oid
+ obj.new = globals()['new']
+ if not hasattr(obj, 'digest_size'):
+ obj.digest_size = digest_size
+ return obj
+
+try:
+ import hashlib
+ hashFactory = hashlib.sha256
+
+except ImportError:
+ from Crypto.Hash import _SHA256
+ hashFactory = _SHA256
+
+digest_size = 32
+block_size = 64
+
diff --git a/lib/Crypto/Hash/SHA384.py b/lib/Crypto/Hash/SHA384.py
index 248955d..1549e8e 100644
--- a/lib/Crypto/Hash/SHA384.py
+++ b/lib/Crypto/Hash/SHA384.py
@@ -24,9 +24,34 @@ __revision__ = "$Id$"
__all__ = ['new', 'digest_size']
-from hashlib import sha384 # This will only work in versions of Python that support SHA384
+from Crypto.Util.wrapper import Wrapper
from Crypto.Util.py3compat import *
+
+# The OID for SHA-384 is:
+#
+# id-sha384 OBJECT IDENTIFIER ::= {
+# joint-iso-itu-t(2)
+# country(16) us(840) organization(1) gov(101) csor(3)
+# nistalgorithm(4) hashalgs(2) 2
+# }
+oid = b('\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02')
+
def new(data=b("")):
- return sha384(data)
-digest_size = new().digest_size
+ obj = Wrapper(hashFactory, data)
+ obj.oid = oid
+ obj.new = globals()['new']
+ if not hasattr(obj, 'digest_size'):
+ obj.digest_size = digest_size
+ return obj
+
+try:
+ import hashlib
+ hashFactory = hashlib.sha384
+
+except ImportError:
+ from Crypto.Hash import _SHA384
+ hashFactory = _SHA384
+
+digest_size = 48
block_size = 128
+
diff --git a/lib/Crypto/Hash/SHA512.py b/lib/Crypto/Hash/SHA512.py
index 38bd957..182ec74 100644
--- a/lib/Crypto/Hash/SHA512.py
+++ b/lib/Crypto/Hash/SHA512.py
@@ -24,9 +24,34 @@ __revision__ = "$Id$"
__all__ = ['new', 'digest_size']
-from hashlib import sha512 # This will only work in versions of Python that support SHA512
+from Crypto.Util.wrapper import Wrapper
from Crypto.Util.py3compat import *
+
+# The OID for SHA-512 is:
+#
+# id-sha512 OBJECT IDENTIFIER ::= {
+# joint-iso-itu-t(2)
+# country(16) us(840) organization(1) gov(101) csor(3)
+# nistalgorithm(4) hashalgs(2) 3
+# }
+oid = b('\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03')
+
def new(data=b("")):
- return sha512(data)
-digest_size = new().digest_size
+ obj = Wrapper(hashFactory, data)
+ obj.oid = oid
+ obj.new = globals()['new']
+ if not hasattr(obj, 'digest_size'):
+ obj.digest_size = digest_size
+ return obj
+
+try:
+ import hashlib
+ hashFactory = hashlib.sha512
+
+except ImportError:
+ from Crypto.Hash import _SHA512
+ hashFactory = _SHA512
+
+digest_size = 64
block_size = 128
+
diff --git a/lib/Crypto/Hash/__init__.py b/lib/Crypto/Hash/__init__.py
index 16dec73..f704b51 100644
--- a/lib/Crypto/Hash/__init__.py
+++ b/lib/Crypto/Hash/__init__.py
@@ -31,14 +31,32 @@ The hashing modules here all support the interface described in PEP
247, "API for Cryptographic Hash Functions".
Submodules:
-Crypto.Hash.HMAC RFC 2104: Keyed-Hashing for Message Authentication
+
+Crypto.Hash.HMAC
+ RFC 2104. Keyed-Hashing for Message Authentication.
Crypto.Hash.MD2
+ RFC1319. Rivest's Message Digest algorithm, with a 128 bit digest. This algorithm is both slow and insecure.
Crypto.Hash.MD4
+ RFC1320. Rivest's Message Digest algorithm, with a 128 bit digest. This algorithm is insecure.
Crypto.Hash.MD5
-Crypto.Hash.RIPEMD160
+ RFC1321. Rivest's Message Digest algorithm, with a 128 bit digest. This algorithm is insecure.
+Crypto.Hash.RIPEMD
+ RACE Integrity Primitives Evaluation Message Digest algorithm, with a 160 bit digest.
Crypto.Hash.SHA
+ Secure Hash Algorithm 1 (SHA-1), with a 160 bit digest. Published in FIPS PUB 180-1/2/3.
+Crypto.Hash.SHA224
+ Secure Hash Algorithm 2 (SHA-2 family), with a 224 bit digest. Published in FIPS PUB 180-2/3.
+Crypto.Hash.SHA256
+ Secure Hash Algorithm 2 (SHA-2 family), with a 256 bit digest. Published in FIPS PUB 180-2/3.
+Crypto.Hash.SHA384
+ Secure Hash Algorithm 2 (SHA-2 family), with a 384 bit digest. Published in FIPS PUB 180-2/3.
+Crypto.Hash.SHA512
+ Secure Hash Algorithm 2 (SHA-2 family), with a 512 bit digest. Published in FIPS PUB 180-2/3.
+
"""
-__all__ = ['HMAC', 'MD2', 'MD4', 'MD5', 'RIPEMD', 'RIPEMD160', 'SHA', 'SHA256']
+__all__ = ['HMAC', 'MD2', 'MD4', 'MD5', 'RIPEMD', 'SHA',
+ 'SHA224', 'SHA256', 'SHA384', 'SHA512']
__revision__ = "$Id$"
+
diff --git a/lib/Crypto/Protocol/KDF.py b/lib/Crypto/Protocol/KDF.py
new file mode 100644
index 0000000..c6979c8
--- /dev/null
+++ b/lib/Crypto/Protocol/KDF.py
@@ -0,0 +1,120 @@
+#
+# KDF.py : a collection of Key Derivation Functions
+#
+# Part of the Python Cryptography Toolkit
+#
+# ===================================================================
+# 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.
+# ===================================================================
+
+"""This file contains a collection of standard key derivation functions.
+
+A key derivation function derives one or more secondary secret keys from
+one primary secret (a master key or a pass phrase).
+
+This is typically done to insulate the secondary keys from each other,
+to avoid that leakage of a secondary key compromises the security of the
+master key, or to thwart attacks on pass phrases (e.g. via rainbow tables).
+
+:undocumented: __revision__
+"""
+
+__revision__ = "$Id$"
+
+import math
+import struct
+
+from Crypto.Util.py3compat import *
+from Crypto.Hash import SHA as SHA1, HMAC
+from Crypto.Util.strxor import strxor
+
+def PBKDF1(password, salt, dkLen, count=1000, hashAlgo=SHA1):
+ """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.
+
+ :Parameters:
+ password : string
+ The secret password or pass phrase to generate the key from.
+ salt : byte string
+ An 8 byte string to use for better protection from dictionary attacks.
+ This value does not need to be kept secret, but it should be randomly
+ chosen for each derivation.
+ dkLen : integer
+ The length of the desired key. Default is 16 bytes, suitable for instance for `Crypto.Cipher.AES`.
+ count : integer
+ The number of iterations to carry out. It's recommended to use at least 1000.
+ hashAlgo : module
+ The hash algorithm to use, as a module or an object from the `Crypto.Hash` package.
+ The digest length must be no shorter than ``dkLen``.
+
+ :Return: A byte string of length `dkLen` that can be used as key.
+"""
+ password = tobytes(password)
+ pHash = hashAlgo.new(password+salt)
+ digest = pHash.digest_size
+ if dkLen>digest:
+ raise ValueError("Selected hash algorithm has a too short digest (%d bytes)." % len(digest))
+ if len(salt)!=8:
+ raise ValueError("Salt is not 8 bytes long.")
+ for i in xrange(count-1):
+ pHash = pHash.new(pHash.digest())
+ return pHash.digest()[:dkLen]
+
+def PBKDF2(password, salt, dkLen=16, count=1000, prf=None):
+ """Derive one or more keys from a password (or passphrase).
+
+ This performs key derivation according to the PKCS#5 standard (v2.0),
+ by means of the ``PBKDF2`` algorithm.
+
+ :Parameters:
+ password : string
+ The secret password or pass phrase to generate the key from.
+ salt : string
+ A string to use for better protection from dictionary attacks.
+ This value does not need to be kept secret, but it should be randomly
+ chosen for each derivation. It is recommended to be at least 8 bytes long.
+ dkLen : integer
+ The cumulative length of the desired keys. Default is 16 bytes, suitable for instance for `Crypto.Cipher.AES`.
+ count : integer
+ The number of iterations to carry out. It's recommended to use at least 1000.
+ prf : callable
+ A pseudorandom function. It must be a function that returns a pseudorandom string
+ from two parameters: a secret and a salt. If not specified, HMAC-SHA1 is used.
+
+ :Return: A byte string of length `dkLen` that can be used as key material.
+ If you wanted multiple keys, just break up this string into segments of the desired length.
+"""
+ password = tobytes(password)
+ if prf is None:
+ prf = lambda p,s: HMAC.new(p,s,SHA1).digest()
+ key = b('')
+ i = 1
+ while len(key)<dkLen:
+ U = previousU = prf(password,salt+struct.pack(">I", i))
+ for j in xrange(count-1):
+ previousU = t = prf(password,previousU)
+ U = strxor(U,t)
+ key += U
+ i = i + 1
+ return key[:dkLen]
+
diff --git a/lib/Crypto/Protocol/__init__.py b/lib/Crypto/Protocol/__init__.py
index 7af7848..cacc685 100644
--- a/lib/Crypto/Protocol/__init__.py
+++ b/lib/Crypto/Protocol/__init__.py
@@ -23,14 +23,19 @@
Implements various cryptographic protocols. (Don't expect to find
network protocols here.)
-Crypto.Protocol.AllOrNothing Transforms a message into a set of message
- blocks, such that the blocks can be
- recombined to get the message back.
+Crypto.Protocol.AllOrNothing
+ Transforms a message into a set of message blocks, such that the blocks
+ can be recombined to get the message back.
-Crypto.Protocol.Chaffing Takes a set of authenticated message blocks
- (the wheat) and adds a number of
- randomly generated blocks (the chaff).
+Crypto.Protocol.Chaffing
+ Takes a set of authenticated message blocks (the wheat) and adds a number
+ of randomly generated blocks (the chaff).
+
+Crypto.Protocol.KDF
+ A collection of standard key derivation functions.
+
+:undocumented: __revision__
"""
-__all__ = ['AllOrNothing', 'Chaffing']
+__all__ = ['AllOrNothing', 'Chaffing', 'KDF']
__revision__ = "$Id$"
diff --git a/lib/Crypto/PublicKey/RSA.py b/lib/Crypto/PublicKey/RSA.py
index a15cabb..1e3a433 100644
--- a/lib/Crypto/PublicKey/RSA.py
+++ b/lib/Crypto/PublicKey/RSA.py
@@ -22,7 +22,11 @@
# SOFTWARE.
# ===================================================================
-"""RSA public-key cryptography algorithm."""
+"""RSA public-key cryptography algorithm.
+
+:sort: generate,construct,importKey,error
+:undocumented: _fastmath, __revision__, _impl
+"""
__revision__ = "$Id$"
@@ -32,15 +36,17 @@ 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.number import getRandomRange
+#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 import Random
-from Crypto.Util.asn1 import DerObject, DerSequence
+from Crypto.Util.asn1 import DerObject, DerSequence, DerNull
import binascii
+import struct
+
+from Crypto.Util.number import inverse
from Crypto.Util.number import inverse
@@ -50,6 +56,21 @@ except ImportError:
_fastmath = None
class _RSAobj(pubkey.pubkey):
+ """Class defining an actual RSA key."""
+
+ #: Dictionary of RSA parameters.
+ #:
+ #: A public key will only have the following entries:
+ #:
+ #: - **n**, the modulus.
+ #: - **e**, the public exponent.
+ #:
+ #: A private key will also have:
+ #:
+ #: - **d**, the private exponent.
+ #: - **p**, the first factor of n.
+ #: - **q**, the second factor of n.
+ #: - **u**, the CRT coefficient (1/p) mod q.
keydata = ['n', 'e', 'd', 'p', 'q', 'u']
def __init__(self, implementation, key, randfunc=None):
@@ -152,45 +173,121 @@ 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'):
- """Export the RSA key. A string is returned
- with the encoded public or the private half
- under the selected format.
+ def exportKey(self, format='PEM', passphrase=None, pkcs=1):
+ """Export this RSA key.
+
+ :Parameter format: The format to use for wrapping the key.
+
+ - *'DER'*. Binary encoding, always unencrypted.
+ - *'PEM'*. Textual encoding, done according to RFC1421/3.
+ Unencrypted (default) or encrypted.
+ - *'OpenSSH'*. Textual encoding, done according to OpenSSH specification.
+ Only suitable for public keys (not private keys).
+ :Type format: string
- format: 'DER' (PKCS#1) or 'PEM' (RFC1421)
+ :Parameter passphrase: In case of PEM, the pass phrase to derive the encryption key from.
+ :Type passphrase: string
+
+ :Parameter pkcs: The PKCS standard to follow for encoding the key.
+ You have two choices: **1** (PKCS#1, RFC3447) or **8** (PKCS#8, RFC5208).
+ PKCS#8 is only available for private keys.
+ PKCS#1 is the default.
+ PKCS standards are not relevant for the *OpenSSH* format.
+ :Type pkcs: integer
+
+ :Return: A string with the encoded public or private half.
+ :Raise ValueError:
+ When the format is unknown.
"""
+ if passphrase is not None:
+ passphrase = tobytes(passphrase)
+ if format=='OpenSSH':
+ eb = long_to_bytes(self.e)
+ 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 = [ '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():
- keyType = "RSA PRIVATE"
- 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) ]
+ 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 = "PUBLIC"
- der.append(b('\x30\x0D\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01\x05\x00'))
- bitmap = DerObject('BIT STRING')
- derPK = DerSequence()
- derPK[:] = [ self.n, self.e ]
- bitmap.payload = b('\x00') + derPK.encode()
- der.append(bitmap.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 der.encode()
+ return der.encode()
if format=='PEM':
- pem = b("-----BEGIN %s KEY-----\n" % keyType)
- binaryKey = der.encode()
- # 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 %s KEY-----" % keyType)
- return pem
+ 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):
+ """
+ An RSA key factory.
+
+ This class is only internally used to implement the methods of the `Crypto.PublicKey.RSA` modulule.
+
+ :sort: __init__,generate,construct,importKey
+ :undocumented: _g*, _i*
+ """
+
def __init__(self, **kwargs):
- # 'use_fast_math' parameter:
- # None (default) - Use fast math if available; Use slow math if not.
- # True - Use fast math, and raise RuntimeError if it's not available.
- # False - Use slow math.
+ """Create a new RSA key factory.
+
+ :Keywords:
+ use_fast_math : bool
+ Specify which mathematic library to use:
+
+ - *None* (default). Use fastest math available.
+ - *True* . Use fast math.
+ - *False* . Use slow math.
+ default_randfunc : callable
+ Specify how to collect random data:
+
+ - *None* (default). Use Random.new().read().
+ - not *Note* . Use the specified function directly.
+ :Raise RuntimeError:
+ When **use_fast_math** =True but fast math is not available.
+ """
use_fast_math = kwargs.get('use_fast_math', None)
if use_fast_math is None: # Automatic
if _fastmath is not None:
@@ -209,9 +306,6 @@ class RSAImplementation(object):
self.error = self._math.error
- # 'default_randfunc' parameter:
- # None (default) - use Random.new().read
- # not None - use the specified function
self._default_randfunc = kwargs.get('default_randfunc', None)
self._current_randfunc = None
@@ -222,74 +316,238 @@ class RSAImplementation(object):
self._current_randfunc = Random.new().read
return self._current_randfunc
- def generate(self, bits, randfunc=None, progress_func=None):
+ def generate(self, bits, randfunc=None, progress_func=None, e=65537):
+ """Randomly generate a fresh, new RSA key object.
+
+ :Parameters:
+ bits : int
+ Key length, or size (in bits) of the RSA modulus.
+
+ It must be a multiple of 256, and no smaller than 1024.
+ randfunc : callable
+ Random number generation function; it should accept
+ a single integer N and return a string of random data
+ N bytes long.
+ progress_func : callable
+ Optional function that will be called with a short string
+ containing the key parameter currently being generated;
+ it's useful for interactive applications where a user is
+ waiting for a key to be generated.
+ e : int
+ Public RSA exponent. It must be an odd positive integer.
+
+ It is typically a small number with very few ones in its
+ binary representation.
+
+ The default value 65537 (= ``0b10000000000000001`` ) is a safe
+ choice: other common values are 5, 7, 17, and 257.
+
+ :attention: You should always use a cryptographically secure random number generator,
+ such as the one defined in the ``Crypto.Random`` module; **don't** just use the
+ current time and the ``random`` module.
+
+ :attention: Exponent 3 is also widely used, but it requires very special care when padding
+ the message.
+
+ :Raise ValueError:
+ When **bits** is too little or not a multiple of 256, or when
+ **e** is not odd or smaller than 2.
+ """
if bits < 1024 or (bits & 0xff) != 0:
# pubkey.getStrongPrime doesn't like anything that's not a multiple of 256 and >= 1024
raise ValueError("RSA modulus length must be a multiple of 256 and >= 1024")
+ if e%2==0 or e<3:
+ raise ValueError("RSA public exponent must be a positive, odd integer larger than 2.")
rf = self._get_randfunc(randfunc)
- obj = _RSA.generate_py(bits, rf, progress_func) # TODO: Don't use legacy _RSA module
+ obj = _RSA.generate_py(bits, rf, progress_func, e) # TODO: Don't use legacy _RSA module
key = self._math.rsa_construct(obj.n, obj.e, obj.d, obj.p, obj.q, obj.u)
return _RSAobj(self, key)
def construct(self, tup):
+ """Construct an RSA key object from a tuple of valid RSA components.
+
+ The modulus **n** must be the product of two primes.
+ The public exponent **e** must be odd and larger than 1.
+
+ In case of a private key, the following equations must apply:
+
+ - e != 1
+ - p*q = n
+ - e*d = 1 mod (p-1)(q-1)
+ - p*u = 1 mod q
+
+ :Parameters:
+ tup : tuple
+ A tuple of long integers, with at least 2 and no
+ more than 6 items. The items come in the following order:
+
+ 1. RSA modulus (n).
+ 2. Public exponent (e).
+ 3. Private exponent (d). Only required if the key is private.
+ 4. First factor of n (p). Optional.
+ 5. Second factor of n (q). Optional.
+ 6. CRT coefficient, (1/p) mod q (u). Optional.
+ """
key = self._math.rsa_construct(*tup)
return _RSAobj(self, key)
def _importKeyDER(self, externKey):
- der = DerSequence()
- der.decode(externKey, True)
- 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[0] # Remove version
- return self.construct(der[:])
- if len(der)==2:
- # The DER object is a SEQUENCE with two elements:
- # a SubjectPublicKeyInfo SEQUENCE and an opaque BIT STRING.
- #
- # The first element is always the same:
- # 0x30 0x0D SEQUENCE, 12 bytes of payload
- # 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
- #
- # The second encapsulates the actual ASN.1 RSAPublicKey element.
- if der[0]==b('\x30\x0D\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01\x05\x00'):
- bitmap = DerObject()
- bitmap.decode(der[1], True)
- if bitmap.typeTag==b('\x03')[0] and bitmap.payload[0]==b('\x00')[0]:
- der.decode(bitmap.payload[1:], True)
- if len(der)==2 and der.hasOnlyInts():
- return self.construct(der[:])
- raise ValueError("RSA key format is not supported")
+ """Import an RSA key (public or private half), encoded in DER form."""
+
+ try:
+
+ 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:
+ # 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[0] # Remove version
+ return self.construct(der[:])
+
+ # Keep on trying PKCS#1, but now for a public key
+ if len(der)==2:
+ # 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 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, IndexError:
+ pass
- def importKey(self, externKey):
- """Import an RSA key (public or private half).
-
- externKey: the RSA key to import, encoded as bytes.
- The key can be in DER (PKCS#1) or in unencrypted
- PEM format (RFC1421).
+ raise ValueError("RSA key format is not supported")
- Raises a ValueError/IndexError if the given key cannot be parsed.
+ def importKey(self, externKey, passphrase=None):
+ """Import an RSA key (public or private half), encoded in standard form.
+
+ :Parameter externKey:
+ The RSA key to import, encoded as a string.
+
+ The key can be in any of the following formats:
+
+ - DER + PKCS#1 (binary)
+ - PEM + PKCS#1 (textual, according to RFC1421/3)
+ - DER + PKCS#8 (binary, private key only)
+ - PEM + PKCS#8 (textual, according to RFC5208, private key only)
+ - OpenSSH (textual public key only)
+
+ In case of PEM + PKCS#1, the 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 PEM key, this is the pass phrase from which the encryption key is derived.
+ :Type passphrase: string
+
+ :Raise ValueError/IndexError/TypeError:
+ When the given key cannot be parsed (possibly because the pass phrase is wrong).
"""
- if isinstance(externKey, unicode) and externKey.startswith("-----"):
- # Convert unicode to bytes for PEM encoded keys
- externKey = externKey.encode('ascii')
+ externKey = tobytes(externKey)
+ if passphrase is not None:
+ passphrase = tobytes(passphrase)
+
if externKey.startswith(b('-----')):
- # This is probably a PEM encoded key
- lines = externKey.replace(b(" "),b('')).split()
- der = binascii.a2b_base64(b('').join(lines[1:-1]))
- return self._importKeyDER(der)
- if externKey[0]==b('\x30')[0]:
- # This is probably a DER encoded key
- return self._importKeyDER(externKey)
+ # This is probably a PEM encoded key
+ lines = externKey.replace(b(" "),b('')).split()
+ keyobj = None
+
+ # 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(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:]
+ e = bytes_to_long(keyparts[1])
+ n = bytes_to_long(keyparts[2])
+ return self.construct([n, e])
+ if bord(externKey[0])==0x30:
+ # This is probably a DER encoded key
+ return self._importKeyDER(externKey)
+
raise ValueError("RSA key format is not supported")
+#: 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(
+ [ b('\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01'),
+ DerNull().encode() ]
+ ).encode()
+
_impl = RSAImplementation()
+#:
+#: Randomly generate a fresh, new RSA key object.
+#:
+#: See `RSAImplementation.generate`.
+#:
generate = _impl.generate
+#:
+#: Construct an RSA key object from a tuple of valid RSA components.
+#:
+#: See `RSAImplementation.construct`.
+#:
construct = _impl.construct
+#:
+#: Import an RSA key (public or private half), encoded in standard form.
+#:
+#: See `RSAImplementation.importKey`.
+#:
importKey = _impl.importKey
error = _impl.error
diff --git a/lib/Crypto/PublicKey/_RSA.py b/lib/Crypto/PublicKey/_RSA.py
index 356881d..9366d19 100644
--- a/lib/Crypto/PublicKey/_RSA.py
+++ b/lib/Crypto/PublicKey/_RSA.py
@@ -29,15 +29,15 @@ __revision__ = "$Id$"
from Crypto.PublicKey import pubkey
from Crypto.Util import number
-def generate_py(bits, randfunc, progress_func=None):
- """generate(bits:int, randfunc:callable, progress_func:callable)
+def generate_py(bits, randfunc, progress_func=None, e=65537):
+ """generate(bits:int, randfunc:callable, progress_func:callable, e:int)
- Generate an RSA key of length 'bits', using 'randfunc' to get
- random data and 'progress_func', if present, to display
- the progress of the key generation.
+ Generate an RSA key of length 'bits', public exponent 'e'(which must be
+ odd), using 'randfunc' to get random data and 'progress_func',
+ if present, to display the progress of the key generation.
"""
obj=RSAobj()
- obj.e = 65537L
+ obj.e = long(e)
# Generate the prime factors of n
if progress_func:
@@ -50,7 +50,9 @@ def generate_py(bits, randfunc, progress_func=None):
p = pubkey.getStrongPrime(bits>>1, obj.e, 1e-12, randfunc)
q = pubkey.getStrongPrime(bits - (bits>>1), obj.e, 1e-12, randfunc)
- # p shall be smaller than q (for calc of u)
+ # It's OK for p to be larger than q, but let's be
+ # kind to the function that will invert it for
+ # th calculation of u.
if p > q:
(p, q)=(q, p)
obj.p = p
diff --git a/lib/Crypto/PublicKey/_slowmath.py b/lib/Crypto/PublicKey/_slowmath.py
index d7502ba..d926596 100644
--- a/lib/Crypto/PublicKey/_slowmath.py
+++ b/lib/Crypto/PublicKey/_slowmath.py
@@ -32,7 +32,7 @@ import sys
if sys.version_info[0] == 2 and sys.version_info[1] == 1:
from Crypto.Util.py21compat import *
-from Crypto.Util.number import size, inverse
+from Crypto.Util.number import size, inverse, GCD
class error(Exception):
pass
@@ -51,7 +51,7 @@ class _RSAKey(object):
if not self.has_private():
raise TypeError("No private key")
if (hasattr(self,'p') and hasattr(self,'q') and hasattr(self,'u')):
- m1 = pow(c, self.d % (self.p-1), self.p)
+ m1 = pow(c, self.d % (self.p-1), self.p)
m2 = pow(c, self.d % (self.q-1), self.q)
h = m2 - m1
if (h<0):
@@ -90,13 +90,53 @@ def rsa_construct(n, e, d=None, p=None, q=None, u=None):
obj = _RSAKey()
obj.n = n
obj.e = e
- if d is not None: obj.d = d
- if p is not None: obj.p = p
- if q is not None: obj.q = q
+ if d is None:
+ return obj
+ obj.d = d
+ if p is not None and q is not None:
+ obj.p = p
+ obj.q = q
+ else:
+ # Compute factors p and q from the private exponent d.
+ # We assume that n has no more than two factors.
+ # See 8.2.2(i) in Handbook of Applied Cryptography.
+ ktot = d*e-1
+ # The quantity d*e-1 is a multiple of phi(n), even,
+ # and can be represented as t*2^s.
+ t = ktot
+ while t%2==0:
+ t=divmod(t,2)[0]
+ # Cycle through all multiplicative inverses in Zn.
+ # The algorithm is non-deterministic, but there is a 50% chance
+ # any candidate a leads to successful factoring.
+ # See "Digitalized Signatures and Public Key Functions as Intractable
+ # as Factorization", M. Rabin, 1979
+ spotted = 0
+ a = 2
+ while not spotted and a<100:
+ k = t
+ # Cycle through all values a^{t*2^i}=a^k
+ while k<ktot:
+ cand = pow(a,k,n)
+ # Check if a^k is a non-trivial root of unity (mod n)
+ if cand!=1 and cand!=(n-1) and pow(cand,2,n)==1:
+ # We have found a number such that (cand-1)(cand+1)=0 (mod n).
+ # Either of the terms divides n.
+ obj.p = GCD(cand+1,n)
+ spotted = 1
+ break
+ k = k*2
+ # This value was not any good... let's try another!
+ a = a+2
+ if not spotted:
+ raise ValueError("Unable to compute factors p and q from exponent d.")
+ # Found !
+ assert ((n % obj.p)==0)
+ obj.q = divmod(n,obj.p)[0]
if u is not None:
obj.u = u
- elif p is not None and q is not None:
- obj.u = inverse(p, q)
+ else:
+ obj.u = inverse(obj.p, obj.q)
return obj
class _DSAKey(object):
diff --git a/lib/Crypto/Random/Fortuna/SHAd256.py b/lib/Crypto/Random/Fortuna/SHAd256.py
index c28e5ba..2e135c9 100644
--- a/lib/Crypto/Random/Fortuna/SHAd256.py
+++ b/lib/Crypto/Random/Fortuna/SHAd256.py
@@ -89,9 +89,10 @@ digest_size = _SHAd256.digest_size
# PEP 247 module-level "new" function
def new(data=None):
"""Return a new SHAd256 hashing object"""
- if data is None:
+ if not data:
data=b("")
-
- return _SHAd256(_SHAd256._internal, SHA256.new(data))
+ sha = _SHAd256(_SHAd256._internal, SHA256.new(data))
+ sha.new = globals()['new']
+ return sha
# vim:set ts=4 sw=4 sts=4 expandtab:
diff --git a/lib/Crypto/SelfTest/Cipher/__init__.py b/lib/Crypto/SelfTest/Cipher/__init__.py
index 6d3ab90..63e9c57 100644
--- a/lib/Crypto/SelfTest/Cipher/__init__.py
+++ b/lib/Crypto/SelfTest/Cipher/__init__.py
@@ -36,6 +36,8 @@ def get_tests(config={}):
from Crypto.SelfTest.Cipher import test_DES3; tests += test_DES3.get_tests(config=config)
from Crypto.SelfTest.Cipher import test_DES; tests += test_DES.get_tests(config=config)
from Crypto.SelfTest.Cipher import test_XOR; tests += test_XOR.get_tests(config=config)
+ from Crypto.SelfTest.Cipher import test_pkcs1_15; tests += test_pkcs1_15.get_tests(config=config)
+ from Crypto.SelfTest.Cipher import test_pkcs1_oaep; tests += test_pkcs1_oaep.get_tests(config=config)
return tests
if __name__ == '__main__':
diff --git a/lib/Crypto/SelfTest/Cipher/test_pkcs1_15.py b/lib/Crypto/SelfTest/Cipher/test_pkcs1_15.py
new file mode 100644
index 0000000..7aa1703
--- /dev/null
+++ b/lib/Crypto/SelfTest/Cipher/test_pkcs1_15.py
@@ -0,0 +1,174 @@
+# -*- coding: utf-8 -*-
+#
+# SelfTest/Cipher/test_pkcs1_15.py: Self-test for PKCS#1 v1.5 encryption
+#
+# ===================================================================
+# 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.
+# ===================================================================
+
+__revision__ = "$Id$"
+
+import unittest
+import sys
+
+from Crypto.PublicKey import RSA
+from Crypto.SelfTest.st_common import list_test_cases, a2b_hex, b2a_hex
+from Crypto import Random
+from Crypto.Cipher import PKCS1_v1_5 as PKCS
+from Crypto.Util.py3compat import *
+
+def rws(t):
+ """Remove white spaces, tabs, and new lines from a string"""
+ for c in ['\n', '\t', ' ']:
+ t = t.replace(c,'')
+ return t
+
+def t2b(t):
+ """Convert a text string with bytes in hex form to a byte string"""
+ clean = b(rws(t))
+ if len(clean)%2 == 1:
+ print clean
+ raise ValueError("Even number of characters expected")
+ return a2b_hex(clean)
+
+class PKCS1_15_Tests(unittest.TestCase):
+
+ def setUp(self):
+ self.rng = Random.new().read
+ self.key1024 = RSA.generate(1024, self.rng)
+
+ # List of tuples with test data for PKCS#1 v1.5.
+ # Each tuple is made up by:
+ # Item #0: dictionary with RSA key component, or key to import
+ # Item #1: plaintext
+ # Item #2: ciphertext
+ # Item #3: random data
+
+ _testData = (
+
+ #
+ # Generated with openssl 0.9.8o
+ #
+ (
+ # Private key
+ '''-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQDAiAnvIAOvqVwJTaYzsKnefZftgtXGE2hPJppGsWl78yz9jeXY
+W/FxX/gTPURArNhdnhP6n3p2ZaDIBrO2zizbgIXs0IsljTTcr4vnI8fMXzyNUOjA
+zP3nzMqZDZK6757XQAobOssMkBFqRWwilT/3DsBhRpl3iMUhF+wvpTSHewIDAQAB
+AoGAC4HV/inOrpgTvSab8Wj0riyZgQOZ3U3ZpSlsfR8ra9Ib9Uee3jCYnKscu6Gk
+y6zI/cdt8EPJ4PuwAWSNJzbpbVaDvUq25OD+CX8/uRT08yBS4J8TzBitZJTD4lS7
+atdTnKT0Wmwk+u8tDbhvMKwnUHdJLcuIsycts9rwJVapUtkCQQDvDpx2JMun0YKG
+uUttjmL8oJ3U0m3ZvMdVwBecA0eebZb1l2J5PvI3EJD97eKe91Nsw8T3lwpoN40k
+IocSVDklAkEAzi1HLHE6EzVPOe5+Y0kGvrIYRRhncOb72vCvBZvD6wLZpQgqo6c4
+d3XHFBBQWA6xcvQb5w+VVEJZzw64y25sHwJBAMYReRl6SzL0qA0wIYrYWrOt8JeQ
+8mthulcWHXmqTgC6FEXP9Es5GD7/fuKl4wqLKZgIbH4nqvvGay7xXLCXD/ECQH9a
+1JYNMtRen5unSAbIOxRcKkWz92F0LKpm9ZW/S9vFHO+mBcClMGoKJHiuQxLBsLbT
+NtEZfSJZAeS2sUtn3/0CQDb2M2zNBTF8LlM0nxmh0k9VGm5TVIyBEMcipmvOgqIs
+HKukWBcq9f/UOmS0oEhai/6g+Uf7VHJdWaeO5LzuvwU=
+-----END RSA PRIVATE KEY-----''',
+ # Plaintext
+ '''THIS IS PLAINTEXT\x0A''',
+ # Ciphertext
+ '''3f dc fd 3c cd 5c 9b 12 af 65 32 e3 f7 d0 da 36
+ 8f 8f d9 e3 13 1c 7f c8 b3 f9 c1 08 e4 eb 79 9c
+ 91 89 1f 96 3b 94 77 61 99 a4 b1 ee 5d e6 17 c9
+ 5d 0a b5 63 52 0a eb 00 45 38 2a fb b0 71 3d 11
+ f7 a1 9e a7 69 b3 af 61 c0 bb 04 5b 5d 4b 27 44
+ 1f 5b 97 89 ba 6a 08 95 ee 4f a2 eb 56 64 e5 0f
+ da 7c f9 9a 61 61 06 62 ed a0 bc 5f aa 6c 31 78
+ 70 28 1a bb 98 3c e3 6a 60 3c d1 0b 0f 5a f4 75''',
+ # Random data
+ '''eb d7 7d 86 a4 35 23 a3 54 7e 02 0b 42 1d
+ 61 6c af 67 b8 4e 17 56 80 66 36 04 64 34 26 8a
+ 47 dd 44 b3 1a b2 17 60 f4 91 2e e2 b5 95 64 cc
+ f9 da c8 70 94 54 86 4c ef 5b 08 7d 18 c4 ab 8d
+ 04 06 33 8f ca 15 5f 52 60 8a a1 0c f5 08 b5 4c
+ bb 99 b8 94 25 04 9c e6 01 75 e6 f9 63 7a 65 61
+ 13 8a a7 47 77 81 ae 0d b8 2c 4d 50 a5'''
+ ),
+ )
+
+ def testEncrypt1(self):
+ for test in self._testData:
+ # Build the key
+ key = RSA.importKey(test[0])
+ # RNG that takes its random numbers from a pool given
+ # at initialization
+ class randGen:
+ def __init__(self, data):
+ self.data = data
+ self.idx = 0
+ def __call__(self, N):
+ r = self.data[self.idx:N]
+ self.idx += N
+ return r
+ # The real test
+ key._randfunc = randGen(t2b(test[3]))
+ cipher = PKCS.new(key)
+ ct = cipher.encrypt(b(test[1]))
+ self.assertEqual(ct, t2b(test[2]))
+
+ def testEncrypt2(self):
+ # Verify that encryption fail if plaintext is too long
+ pt = '\x00'*(128-11+1)
+ cipher = PKCS.new(self.key1024)
+ self.assertRaises(ValueError, cipher.encrypt, pt)
+
+ def testVerify1(self):
+ for test in self._testData:
+ # Build the key
+ key = RSA.importKey(test[0])
+ # The real test
+ cipher = PKCS.new(key)
+ pt = cipher.decrypt(t2b(test[2]), "---")
+ self.assertEqual(pt, b(test[1]))
+
+ def testVerify2(self):
+ # Verify that decryption fails if ciphertext is not as long as
+ # RSA modulus
+ cipher = PKCS.new(self.key1024)
+ self.assertRaises(ValueError, cipher.decrypt, '\x00'*127, "---")
+ self.assertRaises(ValueError, cipher.decrypt, '\x00'*129, "---")
+
+ # Verify that decryption fails if there are less then 8 non-zero padding
+ # bytes
+ pt = b('\x00\x02' + '\xFF'*7 + '\x00' + '\x45'*118)
+ ct = self.key1024.encrypt(pt, 0)[0]
+ ct = b('\x00'*(128-len(ct))) + ct
+ self.assertEqual("---", cipher.decrypt(ct, "---"))
+
+ def testEncryptVerify1(self):
+ # Encrypt/Verify messages of length [0..RSAlen-11]
+ # and therefore padding [8..117]
+ for pt_len in xrange(0,128-11+1):
+ pt = self.rng(pt_len)
+ cipher = PKCS.new(self.key1024)
+ ct = cipher.encrypt(pt)
+ pt2 = cipher.decrypt(ct, "---")
+ self.assertEqual(pt,pt2)
+
+
+def get_tests(config={}):
+ tests = []
+ tests += list_test_cases(PKCS1_15_Tests)
+ 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/lib/Crypto/SelfTest/Cipher/test_pkcs1_oaep.py b/lib/Crypto/SelfTest/Cipher/test_pkcs1_oaep.py
new file mode 100644
index 0000000..accca61
--- /dev/null
+++ b/lib/Crypto/SelfTest/Cipher/test_pkcs1_oaep.py
@@ -0,0 +1,372 @@
+# -*- coding: utf-8 -*-
+#
+# SelfTest/Cipher/test_pkcs1_oaep.py: Self-test for PKCS#1 OAEP encryption
+#
+# ===================================================================
+# 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.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,SHA as SHA1,SHA256,RIPEMD
+from Crypto import Random
+
+def rws(t):
+ """Remove white spaces, tabs, and new lines from a string"""
+ for c in ['\n', '\t', ' ']:
+ t = t.replace(c,'')
+ return t
+
+def t2b(t):
+ """Convert a text string with bytes in hex form to a byte string"""
+ clean = rws(t)
+ if len(clean)%2 == 1:
+ raise ValueError("Even number of characters expected")
+ return a2b_hex(clean)
+
+class PKCS1_OAEP_Tests(unittest.TestCase):
+
+ def setUp(self):
+ self.rng = Random.new().read
+ self.key1024 = RSA.generate(1024, self.rng)
+
+ # List of tuples with test data for PKCS#1 OAEP
+ # Each tuple is made up by:
+ # Item #0: dictionary with RSA key component
+ # Item #1: plaintext
+ # Item #2: ciphertext
+ # Item #3: random data (=seed)
+ # Item #4: hash object
+
+ _testData = (
+
+ #
+ # From in oaep-int.txt to be found in
+ # ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip
+ #
+ (
+ # Private key
+ {
+ 'n':'''bb f8 2f 09 06 82 ce 9c 23 38 ac 2b 9d a8 71 f7
+ 36 8d 07 ee d4 10 43 a4 40 d6 b6 f0 74 54 f5 1f
+ b8 df ba af 03 5c 02 ab 61 ea 48 ce eb 6f cd 48
+ 76 ed 52 0d 60 e1 ec 46 19 71 9d 8a 5b 8b 80 7f
+ af b8 e0 a3 df c7 37 72 3e e6 b4 b7 d9 3a 25 84
+ ee 6a 64 9d 06 09 53 74 88 34 b2 45 45 98 39 4e
+ e0 aa b1 2d 7b 61 a5 1f 52 7a 9a 41 f6 c1 68 7f
+ e2 53 72 98 ca 2a 8f 59 46 f8 e5 fd 09 1d bd cb''',
+ # Public key
+ 'e':'11',
+ # In the test vector, only p and q were given...
+ # d is computed offline as e^{-1} mod (p-1)(q-1)
+ 'd':'''a5dafc5341faf289c4b988db30c1cdf83f31251e0
+ 668b42784813801579641b29410b3c7998d6bc465745e5c3
+ 92669d6870da2c082a939e37fdcb82ec93edac97ff3ad595
+ 0accfbc111c76f1a9529444e56aaf68c56c092cd38dc3bef
+ 5d20a939926ed4f74a13eddfbe1a1cecc4894af9428c2b7b
+ 8883fe4463a4bc85b1cb3c1'''
+ }
+ ,
+ # Plaintext
+ '''d4 36 e9 95 69 fd 32 a7 c8 a0 5b bc 90 d3 2c 49''',
+ # Ciphertext
+ '''12 53 e0 4d c0 a5 39 7b b4 4a 7a b8 7e 9b f2 a0
+ 39 a3 3d 1e 99 6f c8 2a 94 cc d3 00 74 c9 5d f7
+ 63 72 20 17 06 9e 52 68 da 5d 1c 0b 4f 87 2c f6
+ 53 c1 1d f8 23 14 a6 79 68 df ea e2 8d ef 04 bb
+ 6d 84 b1 c3 1d 65 4a 19 70 e5 78 3b d6 eb 96 a0
+ 24 c2 ca 2f 4a 90 fe 9f 2e f5 c9 c1 40 e5 bb 48
+ da 95 36 ad 87 00 c8 4f c9 13 0a de a7 4e 55 8d
+ 51 a7 4d df 85 d8 b5 0d e9 68 38 d6 06 3e 09 55''',
+ # Random
+ '''aa fd 12 f6 59 ca e6 34 89 b4 79 e5 07 6d de c2
+ f0 6c b5 8f''',
+ # Hash
+ SHA1,
+ ),
+
+ #
+ # From in oaep-vect.txt to be found in Example 1.1
+ # ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip
+ #
+ (
+ # Private key
+ {
+ 'n':'''a8 b3 b2 84 af 8e b5 0b 38 70 34 a8 60 f1 46 c4
+ 91 9f 31 87 63 cd 6c 55 98 c8 ae 48 11 a1 e0 ab
+ c4 c7 e0 b0 82 d6 93 a5 e7 fc ed 67 5c f4 66 85
+ 12 77 2c 0c bc 64 a7 42 c6 c6 30 f5 33 c8 cc 72
+ f6 2a e8 33 c4 0b f2 58 42 e9 84 bb 78 bd bf 97
+ c0 10 7d 55 bd b6 62 f5 c4 e0 fa b9 84 5c b5 14
+ 8e f7 39 2d d3 aa ff 93 ae 1e 6b 66 7b b3 d4 24
+ 76 16 d4 f5 ba 10 d4 cf d2 26 de 88 d3 9f 16 fb''',
+ 'e':'''01 00 01''',
+ 'd':'''53 33 9c fd b7 9f c8 46 6a 65 5c 73 16 ac a8 5c
+ 55 fd 8f 6d d8 98 fd af 11 95 17 ef 4f 52 e8 fd
+ 8e 25 8d f9 3f ee 18 0f a0 e4 ab 29 69 3c d8 3b
+ 15 2a 55 3d 4a c4 d1 81 2b 8b 9f a5 af 0e 7f 55
+ fe 73 04 df 41 57 09 26 f3 31 1f 15 c4 d6 5a 73
+ 2c 48 31 16 ee 3d 3d 2d 0a f3 54 9a d9 bf 7c bf
+ b7 8a d8 84 f8 4d 5b eb 04 72 4d c7 36 9b 31 de
+ f3 7d 0c f5 39 e9 cf cd d3 de 65 37 29 ea d5 d1 '''
+ }
+ ,
+ # Plaintext
+ '''66 28 19 4e 12 07 3d b0 3b a9 4c da 9e f9 53 23
+ 97 d5 0d ba 79 b9 87 00 4a fe fe 34''',
+ # Ciphertext
+ '''35 4f e6 7b 4a 12 6d 5d 35 fe 36 c7 77 79 1a 3f
+ 7b a1 3d ef 48 4e 2d 39 08 af f7 22 fa d4 68 fb
+ 21 69 6d e9 5d 0b e9 11 c2 d3 17 4f 8a fc c2 01
+ 03 5f 7b 6d 8e 69 40 2d e5 45 16 18 c2 1a 53 5f
+ a9 d7 bf c5 b8 dd 9f c2 43 f8 cf 92 7d b3 13 22
+ d6 e8 81 ea a9 1a 99 61 70 e6 57 a0 5a 26 64 26
+ d9 8c 88 00 3f 84 77 c1 22 70 94 a0 d9 fa 1e 8c
+ 40 24 30 9c e1 ec cc b5 21 00 35 d4 7a c7 2e 8a''',
+ # Random
+ '''18 b7 76 ea 21 06 9d 69 77 6a 33 e9 6b ad 48 e1
+ dd a0 a5 ef''',
+ SHA1
+ ),
+
+ #
+ # From in oaep-vect.txt to be found in Example 2.1
+ # ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip
+ #
+ (
+ # Private key
+ {
+ 'n':'''01 94 7c 7f ce 90 42 5f 47 27 9e 70 85 1f 25 d5
+ e6 23 16 fe 8a 1d f1 93 71 e3 e6 28 e2 60 54 3e
+ 49 01 ef 60 81 f6 8c 0b 81 41 19 0d 2a e8 da ba
+ 7d 12 50 ec 6d b6 36 e9 44 ec 37 22 87 7c 7c 1d
+ 0a 67 f1 4b 16 94 c5 f0 37 94 51 a4 3e 49 a3 2d
+ de 83 67 0b 73 da 91 a1 c9 9b c2 3b 43 6a 60 05
+ 5c 61 0f 0b af 99 c1 a0 79 56 5b 95 a3 f1 52 66
+ 32 d1 d4 da 60 f2 0e da 25 e6 53 c4 f0 02 76 6f
+ 45''',
+ 'e':'''01 00 01''',
+ 'd':'''08 23 f2 0f ad b5 da 89 08 8a 9d 00 89 3e 21 fa
+ 4a 1b 11 fb c9 3c 64 a3 be 0b aa ea 97 fb 3b 93
+ c3 ff 71 37 04 c1 9c 96 3c 1d 10 7a ae 99 05 47
+ 39 f7 9e 02 e1 86 de 86 f8 7a 6d de fe a6 d8 cc
+ d1 d3 c8 1a 47 bf a7 25 5b e2 06 01 a4 a4 b2 f0
+ 8a 16 7b 5e 27 9d 71 5b 1b 45 5b dd 7e ab 24 59
+ 41 d9 76 8b 9a ce fb 3c cd a5 95 2d a3 ce e7 25
+ 25 b4 50 16 63 a8 ee 15 c9 e9 92 d9 24 62 fe 39'''
+ },
+ # Plaintext
+ '''8f f0 0c aa 60 5c 70 28 30 63 4d 9a 6c 3d 42 c6
+ 52 b5 8c f1 d9 2f ec 57 0b ee e7''',
+ # Ciphertext
+ '''01 81 af 89 22 b9 fc b4 d7 9d 92 eb e1 98 15 99
+ 2f c0 c1 43 9d 8b cd 49 13 98 a0 f4 ad 3a 32 9a
+ 5b d9 38 55 60 db 53 26 83 c8 b7 da 04 e4 b1 2a
+ ed 6a ac df 47 1c 34 c9 cd a8 91 ad dc c2 df 34
+ 56 65 3a a6 38 2e 9a e5 9b 54 45 52 57 eb 09 9d
+ 56 2b be 10 45 3f 2b 6d 13 c5 9c 02 e1 0f 1f 8a
+ bb 5d a0 d0 57 09 32 da cf 2d 09 01 db 72 9d 0f
+ ef cc 05 4e 70 96 8e a5 40 c8 1b 04 bc ae fe 72
+ 0e''',
+ # Random
+ '''8c 40 7b 5e c2 89 9e 50 99 c5 3e 8c e7 93 bf 94
+ e7 1b 17 82''',
+ SHA1
+ ),
+
+ #
+ # From in oaep-vect.txt to be found in Example 10.1
+ # ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip
+ #
+ (
+ # Private key
+ {
+ 'n':'''ae 45 ed 56 01 ce c6 b8 cc 05 f8 03 93 5c 67 4d
+ db e0 d7 5c 4c 09 fd 79 51 fc 6b 0c ae c3 13 a8
+ df 39 97 0c 51 8b ff ba 5e d6 8f 3f 0d 7f 22 a4
+ 02 9d 41 3f 1a e0 7e 4e be 9e 41 77 ce 23 e7 f5
+ 40 4b 56 9e 4e e1 bd cf 3c 1f b0 3e f1 13 80 2d
+ 4f 85 5e b9 b5 13 4b 5a 7c 80 85 ad ca e6 fa 2f
+ a1 41 7e c3 76 3b e1 71 b0 c6 2b 76 0e de 23 c1
+ 2a d9 2b 98 08 84 c6 41 f5 a8 fa c2 6b da d4 a0
+ 33 81 a2 2f e1 b7 54 88 50 94 c8 25 06 d4 01 9a
+ 53 5a 28 6a fe b2 71 bb 9b a5 92 de 18 dc f6 00
+ c2 ae ea e5 6e 02 f7 cf 79 fc 14 cf 3b dc 7c d8
+ 4f eb bb f9 50 ca 90 30 4b 22 19 a7 aa 06 3a ef
+ a2 c3 c1 98 0e 56 0c d6 4a fe 77 95 85 b6 10 76
+ 57 b9 57 85 7e fd e6 01 09 88 ab 7d e4 17 fc 88
+ d8 f3 84 c4 e6 e7 2c 3f 94 3e 0c 31 c0 c4 a5 cc
+ 36 f8 79 d8 a3 ac 9d 7d 59 86 0e aa da 6b 83 bb''',
+ 'e':'''01 00 01''',
+ 'd':'''05 6b 04 21 6f e5 f3 54 ac 77 25 0a 4b 6b 0c 85
+ 25 a8 5c 59 b0 bd 80 c5 64 50 a2 2d 5f 43 8e 59
+ 6a 33 3a a8 75 e2 91 dd 43 f4 8c b8 8b 9d 5f c0
+ d4 99 f9 fc d1 c3 97 f9 af c0 70 cd 9e 39 8c 8d
+ 19 e6 1d b7 c7 41 0a 6b 26 75 df bf 5d 34 5b 80
+ 4d 20 1a dd 50 2d 5c e2 df cb 09 1c e9 99 7b be
+ be 57 30 6f 38 3e 4d 58 81 03 f0 36 f7 e8 5d 19
+ 34 d1 52 a3 23 e4 a8 db 45 1d 6f 4a 5b 1b 0f 10
+ 2c c1 50 e0 2f ee e2 b8 8d ea 4a d4 c1 ba cc b2
+ 4d 84 07 2d 14 e1 d2 4a 67 71 f7 40 8e e3 05 64
+ fb 86 d4 39 3a 34 bc f0 b7 88 50 1d 19 33 03 f1
+ 3a 22 84 b0 01 f0 f6 49 ea f7 93 28 d4 ac 5c 43
+ 0a b4 41 49 20 a9 46 0e d1 b7 bc 40 ec 65 3e 87
+ 6d 09 ab c5 09 ae 45 b5 25 19 01 16 a0 c2 61 01
+ 84 82 98 50 9c 1c 3b f3 a4 83 e7 27 40 54 e1 5e
+ 97 07 50 36 e9 89 f6 09 32 80 7b 52 57 75 1e 79'''
+ },
+ # Plaintext
+ '''8b ba 6b f8 2a 6c 0f 86 d5 f1 75 6e 97 95 68 70
+ b0 89 53 b0 6b 4e b2 05 bc 16 94 ee''',
+ # Ciphertext
+ '''53 ea 5d c0 8c d2 60 fb 3b 85 85 67 28 7f a9 15
+ 52 c3 0b 2f eb fb a2 13 f0 ae 87 70 2d 06 8d 19
+ ba b0 7f e5 74 52 3d fb 42 13 9d 68 c3 c5 af ee
+ e0 bf e4 cb 79 69 cb f3 82 b8 04 d6 e6 13 96 14
+ 4e 2d 0e 60 74 1f 89 93 c3 01 4b 58 b9 b1 95 7a
+ 8b ab cd 23 af 85 4f 4c 35 6f b1 66 2a a7 2b fc
+ c7 e5 86 55 9d c4 28 0d 16 0c 12 67 85 a7 23 eb
+ ee be ff 71 f1 15 94 44 0a ae f8 7d 10 79 3a 87
+ 74 a2 39 d4 a0 4c 87 fe 14 67 b9 da f8 52 08 ec
+ 6c 72 55 79 4a 96 cc 29 14 2f 9a 8b d4 18 e3 c1
+ fd 67 34 4b 0c d0 82 9d f3 b2 be c6 02 53 19 62
+ 93 c6 b3 4d 3f 75 d3 2f 21 3d d4 5c 62 73 d5 05
+ ad f4 cc ed 10 57 cb 75 8f c2 6a ee fa 44 12 55
+ ed 4e 64 c1 99 ee 07 5e 7f 16 64 61 82 fd b4 64
+ 73 9b 68 ab 5d af f0 e6 3e 95 52 01 68 24 f0 54
+ bf 4d 3c 8c 90 a9 7b b6 b6 55 32 84 eb 42 9f cc''',
+ # Random
+ '''47 e1 ab 71 19 fe e5 6c 95 ee 5e aa d8 6f 40 d0
+ aa 63 bd 33''',
+ SHA1
+ ),
+ )
+
+ def testEncrypt1(self):
+ # Verify encryption using all test vectors
+ for test in self._testData:
+ # Build the key
+ comps = [ long(rws(test[0][x]),16) for x in ('n','e') ]
+ key = RSA.construct(comps)
+ # RNG that takes its random numbers from a pool given
+ # at initialization
+ class randGen:
+ def __init__(self, data):
+ self.data = data
+ self.idx = 0
+ def __call__(self, N):
+ r = self.data[self.idx:N]
+ self.idx += N
+ return r
+ # The real test
+ key._randfunc = randGen(t2b(test[3]))
+ cipher = PKCS.new(key, test[4])
+ ct = cipher.encrypt(t2b(test[1]))
+ self.assertEqual(ct, t2b(test[2]))
+
+ def testEncrypt2(self):
+ # Verify that encryption fails if plaintext is too long
+ pt = '\x00'*(128-2*20-2+1)
+ cipher = PKCS.new(self.key1024)
+ self.assertRaises(ValueError, cipher.encrypt, pt)
+
+ def testDecrypt1(self):
+ # Verify decryption using all test vectors
+ for test in self._testData:
+ # Build the key
+ comps = [ long(rws(test[0][x]),16) for x in ('n','e','d') ]
+ key = RSA.construct(comps)
+ # The real test
+ cipher = PKCS.new(key, test[4])
+ pt = cipher.decrypt(t2b(test[2]))
+ self.assertEqual(pt, t2b(test[1]))
+
+ def testDecrypt2(self):
+ # Simplest possible negative tests
+ for ct_size in (127,128,129):
+ cipher = PKCS.new(self.key1024)
+ self.assertRaises(ValueError, cipher.decrypt, bchr(0x00)*ct_size)
+
+ def testEncryptDecrypt1(self):
+ # 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)
+ ct = PKCS.encrypt(pt, self.key1024)
+ pt2 = PKCS.decrypt(ct, self.key1024)
+ self.assertEqual(pt,pt2)
+
+ def testEncryptDecrypt1(self):
+ # Helper function to monitor what's requested from RNG
+ global asked
+ def localRng(N):
+ global asked
+ asked += N
+ return self.rng(N)
+ # Verify that OAEP is friendly to all hashes
+ for hashmod in (MD2,MD5,SHA1,SHA256,RIPEMD):
+ # Verify that encrypt() asks for as many random bytes
+ # as the hash output size
+ asked = 0
+ pt = self.rng(40)
+ self.key1024._randfunc = localRng
+ cipher = PKCS.new(self.key1024, hashmod)
+ ct = cipher.encrypt(pt)
+ self.assertEqual(cipher.decrypt(ct), pt)
+ self.failUnless(asked > hashmod.digest_size)
+
+ def testEncryptDecrypt2(self):
+ # Verify that OAEP supports labels
+ pt = self.rng(35)
+ xlabel = self.rng(22)
+ cipher = PKCS.new(self.key1024, label=xlabel)
+ ct = cipher.encrypt(pt)
+ self.assertEqual(cipher.decrypt(ct), pt)
+
+ def testEncryptDecrypt3(self):
+ # Verify that encrypt() uses the custom MGF
+ global mgfcalls
+ # Helper function to monitor what's requested from MGF
+ def newMGF(seed,maskLen):
+ global mgfcalls
+ mgfcalls += 1
+ return bchr(0x00)*maskLen
+ mgfcalls = 0
+ pt = self.rng(32)
+ cipher = PKCS.new(self.key1024, mgfunc=newMGF)
+ ct = cipher.encrypt(pt)
+ self.assertEqual(mgfcalls, 2)
+ self.assertEqual(cipher.decrypt(ct), pt)
+
+def get_tests(config={}):
+ tests = []
+ tests += list_test_cases(PKCS1_OAEP_Tests)
+ 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/lib/Crypto/SelfTest/Hash/common.py b/lib/Crypto/SelfTest/Hash/common.py
index 386a82e..f77fb0f 100644
--- a/lib/Crypto/SelfTest/Hash/common.py
+++ b/lib/Crypto/SelfTest/Hash/common.py
@@ -41,13 +41,32 @@ else:
dict = dict
+class HashDigestSizeSelfTest(unittest.TestCase):
+
+ def __init__(self, hashmod, description, expected):
+ unittest.TestCase.__init__(self)
+ self.hashmod = hashmod
+ self.expected = expected
+ self.description = description
+
+ def shortDescription(self):
+ return self.description
+
+ def runTest(self):
+ self.failUnless(hasattr(self.hashmod, "digest_size"))
+ self.assertEquals(self.hashmod.digest_size, self.expected)
+ h = self.hashmod.new()
+ self.failUnless(hasattr(h, "digest_size"))
+ self.assertEquals(h.digest_size, self.expected)
+
+
class HashSelfTest(unittest.TestCase):
def __init__(self, hashmod, description, expected, input):
unittest.TestCase.__init__(self)
self.hashmod = hashmod
- self.expected = b(expected)
- self.input = b(input)
+ self.expected = expected
+ self.input = input
self.description = description
def shortDescription(self):
@@ -75,6 +94,30 @@ 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 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):
+ unittest.TestCase.__init__(self)
+ self.hashmod = hashmod
+ self.oid = oid
+
+ def runTest(self):
+ h = self.hashmod.new()
+ 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, hashmod, description, expected_dict, input, key, hashmods):
@@ -124,17 +167,22 @@ class MACSelfTest(unittest.TestCase):
self.assertEqual(expected, out4)
self.assertEqual(expected, out5)
-def make_hash_tests(module, module_name, test_data):
+def make_hash_tests(module, module_name, test_data, digest_size, oid=None):
tests = []
for i in range(len(test_data)):
row = test_data[i]
+ (expected, input) = map(b,row[0:2])
if len(row) < 3:
- (expected, input) = row
description = repr(input)
else:
- (expected, input, description) = row
+ 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))
+ tests.append(HashTestOID(module, oid))
return tests
def make_mac_tests(module, module_name, test_data, hashmods):
diff --git a/lib/Crypto/SelfTest/Hash/test_MD2.py b/lib/Crypto/SelfTest/Hash/test_MD2.py
index ffea990..db636d4 100644
--- a/lib/Crypto/SelfTest/Hash/test_MD2.py
+++ b/lib/Crypto/SelfTest/Hash/test_MD2.py
@@ -52,7 +52,9 @@ test_data = [
def get_tests(config={}):
from Crypto.Hash import MD2
from common import make_hash_tests
- return make_hash_tests(MD2, "MD2", test_data)
+ return make_hash_tests(MD2, "MD2", test_data,
+ digest_size=16,
+ oid="\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x02")
if __name__ == '__main__':
import unittest
diff --git a/lib/Crypto/SelfTest/Hash/test_MD4.py b/lib/Crypto/SelfTest/Hash/test_MD4.py
index 6757280..1727bb6 100644
--- a/lib/Crypto/SelfTest/Hash/test_MD4.py
+++ b/lib/Crypto/SelfTest/Hash/test_MD4.py
@@ -52,7 +52,9 @@ test_data = [
def get_tests(config={}):
from Crypto.Hash import MD4
from common import make_hash_tests
- return make_hash_tests(MD4, "MD4", test_data)
+ return make_hash_tests(MD4, "MD4", test_data,
+ digest_size=16,
+ oid="\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x04")
if __name__ == '__main__':
import unittest
diff --git a/lib/Crypto/SelfTest/Hash/test_MD5.py b/lib/Crypto/SelfTest/Hash/test_MD5.py
index da6b0b4..2e293fc 100644
--- a/lib/Crypto/SelfTest/Hash/test_MD5.py
+++ b/lib/Crypto/SelfTest/Hash/test_MD5.py
@@ -52,7 +52,9 @@ test_data = [
def get_tests(config={}):
from Crypto.Hash import MD5
from common import make_hash_tests
- return make_hash_tests(MD5, "MD5", test_data)
+ return make_hash_tests(MD5, "MD5", test_data,
+ digest_size=16,
+ oid="\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x05")
if __name__ == '__main__':
import unittest
diff --git a/lib/Crypto/SelfTest/Hash/test_RIPEMD.py b/lib/Crypto/SelfTest/Hash/test_RIPEMD.py
index b968052..6673a93 100644
--- a/lib/Crypto/SelfTest/Hash/test_RIPEMD.py
+++ b/lib/Crypto/SelfTest/Hash/test_RIPEMD.py
@@ -61,7 +61,9 @@ test_data = [
def get_tests(config={}):
from Crypto.Hash import RIPEMD
from common import make_hash_tests
- return make_hash_tests(RIPEMD, "RIPEMD", test_data)
+ return make_hash_tests(RIPEMD, "RIPEMD", test_data,
+ digest_size=20,
+ oid="\x06\x05\x2b\x24\x03\02\x01")
if __name__ == '__main__':
import unittest
diff --git a/lib/Crypto/SelfTest/Hash/test_SHA.py b/lib/Crypto/SelfTest/Hash/test_SHA.py
index 18fdd35..7d72e77 100644
--- a/lib/Crypto/SelfTest/Hash/test_SHA.py
+++ b/lib/Crypto/SelfTest/Hash/test_SHA.py
@@ -52,7 +52,9 @@ test_data = [
def get_tests(config={}):
from Crypto.Hash import SHA
from common import make_hash_tests
- return make_hash_tests(SHA, "SHA", test_data)
+ return make_hash_tests(SHA, "SHA", test_data,
+ digest_size=20,
+ oid="\x06\x05\x2B\x0E\x03\x02\x1A")
if __name__ == '__main__':
import unittest
diff --git a/lib/Crypto/SelfTest/Hash/test_SHA224.py b/lib/Crypto/SelfTest/Hash/test_SHA224.py
index 2e0832c..a60f35a 100644
--- a/lib/Crypto/SelfTest/Hash/test_SHA224.py
+++ b/lib/Crypto/SelfTest/Hash/test_SHA224.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
-# SelfTest/Hash/SHA.py: Self-test for the SHA-1 hash function
+# SelfTest/Hash/test_SHA224.py: Self-test for the SHA-224 hash function
#
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
#
@@ -39,12 +39,23 @@ test_data = [
# RFC 3874: Section 3.3, "Test Vector #3
('20794655980c91d8bbb4c1ea97618a4bf03f42581948b2ee4ee7ad67', 'a' * 10**6, "'a' * 10**6"),
+ # Examples from http://de.wikipedia.org/wiki/Secure_Hash_Algorithm
+ ('d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f', ''),
+
+ ('49b08defa65e644cbf8a2dd9270bdededabc741997d1dadd42026d7b',
+ 'Franz jagt im komplett verwahrlosten Taxi quer durch Bayern'),
+
+ ('58911e7fccf2971a7d07f93162d8bd13568e71aa8fc86fc1fe9043d1',
+ 'Frank jagt im komplett verwahrlosten Taxi quer durch Bayern'),
+
]
def get_tests(config={}):
from Crypto.Hash import SHA224
from common import make_hash_tests
- return make_hash_tests(SHA224, "SHA224", test_data)
+ return make_hash_tests(SHA224, "SHA224", test_data,
+ digest_size=28,
+ oid='\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x04')
if __name__ == '__main__':
import unittest
diff --git a/lib/Crypto/SelfTest/Hash/test_SHA256.py b/lib/Crypto/SelfTest/Hash/test_SHA256.py
index afb7268..4b45110 100644
--- a/lib/Crypto/SelfTest/Hash/test_SHA256.py
+++ b/lib/Crypto/SelfTest/Hash/test_SHA256.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
-# SelfTest/Hash/SHA256.py: Self-test for the SHA-256 hash function
+# SelfTest/Hash/test_SHA256.py: Self-test for the SHA-256 hash function
#
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
#
@@ -33,7 +33,7 @@ class LargeSHA256Test(unittest.TestCase):
def runTest(self):
"""SHA256: 512/520 MiB test"""
from Crypto.Hash import SHA256
- zeros = '\0' * (1024*1024)
+ zeros = bchr(0x00) * (1024*1024)
h = SHA256.new(zeros)
for i in xrange(511):
@@ -69,11 +69,19 @@ def get_tests(config={}):
('f7fd017a3c721ce7ff03f3552c0813adcc48b7f33f07e5e2ba71e23ea393d103',
'This message is precisely 55 bytes long, to test a bug.',
'Length = 55 (mod 64)'),
+
+ # Example from http://de.wikipedia.org/wiki/Secure_Hash_Algorithm
+ ('e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', ''),
+
+ ('d32b568cd1b96d459e7291ebf4b25d007f275c9f13149beeb782fac0716613f8',
+ 'Franz jagt im komplett verwahrlosten Taxi quer durch Bayern'),
]
from Crypto.Hash import SHA256
from common import make_hash_tests
- tests = make_hash_tests(SHA256, "SHA256", test_data)
+ tests = make_hash_tests(SHA256, "SHA256", test_data,
+ digest_size=32,
+ oid="\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01")
if config.get('slow_tests'):
tests += [LargeSHA256Test()]
diff --git a/lib/Crypto/SelfTest/Hash/test_SHA384.py b/lib/Crypto/SelfTest/Hash/test_SHA384.py
index 375932f..b7a72c0 100644
--- a/lib/Crypto/SelfTest/Hash/test_SHA384.py
+++ b/lib/Crypto/SelfTest/Hash/test_SHA384.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
-# SelfTest/Hash/SHA.py: Self-test for the SHA-1 hash function
+# SelfTest/Hash/test_SHA.py: Self-test for the SHA-384 hash function
#
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
#
@@ -39,12 +39,21 @@ test_data = [
# RFC 4634: Section Page 8.4, "Test 3"
('9d0e1809716474cb086e834e310a4a1ced149e9c00f248527972cec5704c2a5b07b8b3dc38ecc4ebae97ddd87f3d8985', 'a' * 10**6, "'a' * 10**6"),
+ # Taken from http://de.wikipedia.org/wiki/Secure_Hash_Algorithm
+ ('38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b', ''),
+
+ # Example from http://de.wikipedia.org/wiki/Secure_Hash_Algorithm
+ ('71e8383a4cea32d6fd6877495db2ee353542f46fa44bc23100bca48f3366b84e809f0708e81041f427c6d5219a286677',
+ 'Franz jagt im komplett verwahrlosten Taxi quer durch Bayern'),
+
]
def get_tests(config={}):
from Crypto.Hash import SHA384
from common import make_hash_tests
- return make_hash_tests(SHA384, "SHA384", test_data)
+ return make_hash_tests(SHA384, "SHA384", test_data,
+ digest_size=48,
+ oid='\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02')
if __name__ == '__main__':
import unittest
diff --git a/lib/Crypto/SelfTest/Hash/test_SHA512.py b/lib/Crypto/SelfTest/Hash/test_SHA512.py
index 4049e9d..cb86177 100644
--- a/lib/Crypto/SelfTest/Hash/test_SHA512.py
+++ b/lib/Crypto/SelfTest/Hash/test_SHA512.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
-# SelfTest/Hash/SHA.py: Self-test for the SHA-1 hash function
+# SelfTest/Hash/test_SHA512.py: Self-test for the SHA-512 hash function
#
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
#
@@ -39,12 +39,18 @@ test_data = [
# RFC 4634: Section Page 8.4, "Test 3"
('e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa973ebde0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b', 'a' * 10**6, "'a' * 10**6"),
+ # Taken from http://de.wikipedia.org/wiki/Secure_Hash_Algorithm
+ ('cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e', ''),
+
+ ('af9ed2de700433b803240a552b41b5a472a6ef3fe1431a722b2063c75e9f07451f67a28e37d09cde769424c96aea6f8971389db9e1993d6c565c3c71b855723c', 'Franz jagt im komplett verwahrlosten Taxi quer durch Bayern'),
]
def get_tests(config={}):
from Crypto.Hash import SHA512
from common import make_hash_tests
- return make_hash_tests(SHA512, "SHA512", test_data)
+ return make_hash_tests(SHA512, "SHA512", test_data,
+ digest_size=64,
+ oid="\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03")
if __name__ == '__main__':
import unittest
diff --git a/lib/Crypto/SelfTest/Protocol/test_KDF.py b/lib/Crypto/SelfTest/Protocol/test_KDF.py
new file mode 100644
index 0000000..46082a5
--- /dev/null
+++ b/lib/Crypto/SelfTest/Protocol/test_KDF.py
@@ -0,0 +1,98 @@
+# -*- coding: utf-8 -*-
+#
+# SelfTest/Protocol/test_KDF.py: Self-test for key derivation functions
+#
+# ===================================================================
+# 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.
+# ===================================================================
+
+__revision__ = "$Id$"
+
+import unittest
+from binascii import unhexlify
+
+from Crypto.SelfTest.st_common import list_test_cases
+from Crypto.Hash import SHA as SHA1,HMAC
+
+from Crypto.Protocol.KDF import *
+
+def t2b(t): return unhexlify(b(t))
+
+class PBKDF1_Tests(unittest.TestCase):
+
+ # List of tuples with test data.
+ # Each tuple is made up by:
+ # Item #0: a pass phrase
+ # Item #1: salt (8 bytes encoded in hex)
+ # Item #2: output key length
+ # Item #3: iterations to use
+ # Item #4: expected result (encoded in hex)
+ _testData = (
+ # 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)
+ self.assertEqual(res, t2b(v[4]))
+
+class PBKDF2_Tests(unittest.TestCase):
+
+ # List of tuples with test data.
+ # Each tuple is made up by:
+ # Item #0: a pass phrase
+ # Item #1: salt (encoded in hex)
+ # Item #2: output key length
+ # Item #3: iterations to use
+ # Item #4: expected result (encoded in hex)
+ _testData = (
+ # From http://www.di-mgt.com.au/cryptoKDFs.html#examplespbkdf
+ ("password","78578E5A5D63CB06",24,2048,"BFDE6BE94DF7E11DD409BCE20A0255EC327CB936FFE93643"),
+ # From RFC 6050
+ ("password","73616c74", 20, 1, "0c60c80f961f0e71f3a9b524af6012062fe037a6"),
+ ("password","73616c74", 20, 2, "ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957"),
+ ("password","73616c74", 20, 4096, "4b007901b765489abead49d926f721d065a429c1"),
+ ("passwordPASSWORDpassword","73616c7453414c5473616c7453414c5473616c7453414c5473616c7453414c5473616c74",
+ 25, 4096, "3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038"),
+ ( 'pass\x00word',"7361006c74",16,4096, "56fa6aa75548099dcc37d7f03425e0c3"),
+ )
+
+ def test1(self):
+ # Test only for HMAC-SHA1 as PRF
+
+ def prf(p,s):
+ return HMAC.new(p,s,SHA1).digest()
+
+ for i in [0]: #xrange(len(self._testData)):
+ v = self._testData[i]
+ res = PBKDF2(v[0], t2b(v[1]), v[2], v[3])
+ res2 = PBKDF2(v[0], t2b(v[1]), v[2], v[3], prf)
+ self.assertEqual(res, t2b(v[4]))
+ self.assertEqual(res, res2)
+
+def get_tests(config={}):
+ tests = []
+ tests += list_test_cases(PBKDF1_Tests)
+ tests += list_test_cases(PBKDF2_Tests)
+ return tests
+
+if __name__ == '__main__':
+ suite = lambda: unittest.TestSuite(get_tests())
+ unittest.main(defaultTest='suite')
+
+# vim:set ts=4 sw=4 sts=4
diff --git a/lib/Crypto/SelfTest/PublicKey/test_RSA.py b/lib/Crypto/SelfTest/PublicKey/test_RSA.py
index 9bfb708..c971042 100644
--- a/lib/Crypto/SelfTest/PublicKey/test_RSA.py
+++ b/lib/Crypto/SelfTest/PublicKey/test_RSA.py
@@ -120,6 +120,15 @@ class RSATest(unittest.TestCase):
self._check_public_key(pub)
self._exercise_public_primitive(rsaObj)
+ def test_generate_3args(self):
+ rsaObj = self.rsa.generate(1024, Random.new().read,e=65537)
+ self._check_private_key(rsaObj)
+ self._exercise_primitive(rsaObj)
+ pub = rsaObj.publickey()
+ self._check_public_key(pub)
+ self._exercise_public_primitive(rsaObj)
+ self.assertEqual(65537,rsaObj.e)
+
def test_construct_2tuple(self):
"""RSA (default implementation) constructed key (2-tuple)"""
pub = self.rsa.construct((self.n, self.e))
@@ -161,6 +170,14 @@ class RSATest(unittest.TestCase):
self._check_signing(rsaObj)
self._check_verification(rsaObj)
+ def test_factoring(self):
+ rsaObj = self.rsa.construct([self.n, self.e, self.d])
+ self.failUnless(rsaObj.p==self.p or rsaObj.p==self.q)
+ self.failUnless(rsaObj.q==self.p or rsaObj.q==self.q)
+ self.failUnless(rsaObj.q*rsaObj.p == self.n)
+
+ self.assertRaises(ValueError, self.rsa.construct, [self.n, self.e, self.n-1])
+
def _check_private_key(self, rsaObj):
# Check capabilities
self.assertEqual(1, rsaObj.has_private())
@@ -177,7 +194,6 @@ class RSATest(unittest.TestCase):
self.assertEqual(rsaObj.u, rsaObj.key.u)
# Sanity check key data
- self.assertEqual(1, rsaObj.p < rsaObj.q) # p < q
self.assertEqual(rsaObj.n, rsaObj.p * rsaObj.q) # n = pq
self.assertEqual(1, rsaObj.d * rsaObj.e % ((rsaObj.p-1) * (rsaObj.q-1))) # ed = 1 (mod (p-1)(q-1))
self.assertEqual(1, rsaObj.p * rsaObj.u % rsaObj.q) # pu = 1 (mod q)
@@ -333,6 +349,9 @@ class RSAFastMathTest(RSATest):
"""RSA (_fastmath implementation) constructed key (6-tuple)"""
RSATest.test_construct_6tuple(self)
+ def test_factoring(self):
+ RSATest.test_factoring(self)
+
class RSASlowMathTest(RSATest):
def setUp(self):
RSATest.setUp(self)
@@ -366,6 +385,8 @@ class RSASlowMathTest(RSATest):
"""RSA (_slowmath implementation) constructed key (6-tuple)"""
RSATest.test_construct_6tuple(self)
+ def test_factoring(self):
+ RSATest.test_factoring(self)
def get_tests(config={}):
tests = []
@@ -383,7 +404,8 @@ def get_tests(config={}):
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)
- tests += list_test_cases(RSASlowMathTest)
+ if config.get('slow_tests',1):
+ tests += list_test_cases(RSASlowMathTest)
return tests
if __name__ == '__main__':
diff --git a/lib/Crypto/SelfTest/PublicKey/test_importKey.py b/lib/Crypto/SelfTest/PublicKey/test_importKey.py
index f9ae51a..9e62fcf 100644
--- a/lib/Crypto/SelfTest/PublicKey/test_importKey.py
+++ b/lib/Crypto/SelfTest/PublicKey/test_importKey.py
@@ -20,18 +20,18 @@
# SOFTWARE.
# ===================================================================
+from __future__ import nested_scopes
+
__revision__ = "$Id$"
import unittest
from Crypto.PublicKey import RSA
from Crypto.SelfTest.st_common import *
-from Crypto.SelfTest.st_common import list_test_cases, a2b_hex, b2a_hex
from Crypto.Util.py3compat import *
from Crypto.Util.number import inverse
class ImportKeyTests(unittest.TestCase):
-
# 512-bit RSA key generated with openssl
rsaKeyPEM = u'''-----BEGIN RSA PRIVATE KEY-----
MIIBOwIBAAJBAL8eJ5AKoIsjURpcEoGubZMxLD7+kT+TLr7UkvEtFrRhDDKMtuII
@@ -43,11 +43,61 @@ 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
@@ -62,6 +112,22 @@ Lr7UkvEtFrRhDDKMtuIIq19FrL4pUIMymPMSLBn3hJLe30Dw48GQM4UCAwEAAQ==
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
@@ -81,8 +147,8 @@ Lr7UkvEtFrRhDDKMtuIIq19FrL4pUIMymPMSLBn3hJLe30Dw48GQM4UCAwEAAQ==
pInv = inverse(p,q)
def testImportKey1(self):
- key = RSA.importKey(self.rsaKeyDER)
- self.assertEqual(key.has_private(),True) # assert_
+ 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)
@@ -90,8 +156,8 @@ Lr7UkvEtFrRhDDKMtuIIq19FrL4pUIMymPMSLBn3hJLe30Dw48GQM4UCAwEAAQ==
self.assertEqual(key.q, self.q)
def testImportKey2(self):
- key = RSA.importKey(self.rsaPublicKeyDER)
- self.assertEqual(key.has_private(),False) # failIf
+ key = self.rsa.importKey(self.rsaPublicKeyDER)
+ self.failIf(key.has_private())
self.assertEqual(key.n, self.n)
self.assertEqual(key.e, self.e)
@@ -137,33 +203,105 @@ Lr7UkvEtFrRhDDKMtuIIq19FrL4pUIMymPMSLBn3hJLe30Dw48GQM4UCAwEAAQ==
idem = key.encrypt(key.decrypt(b("Test")),0)
self.assertEqual(idem[0],b("Test"))
+ def testImportKey7(self):
+ key = self.rsa.importKey(self.rsaPublicKeyOpenSSH)
+ self.assertEqual(key.n, self.n)
+ self.assertEqual(key.e, self.e)
+
+ def testImportKey8(self):
+ 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):
+ 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):
+ 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 testExportKey1(self):
- key = RSA.construct([self.n, self.e, self.d, self.p, self.q, self.pInv])
+ 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 = RSA.construct([self.n, self.e])
+ key = self.rsa.construct([self.n, self.e])
derKey = key.exportKey("DER")
self.assertEqual(derKey, self.rsaPublicKeyDER)
def testExportKey3(self):
- key = RSA.construct([self.n, self.e, self.d, self.p, self.q, self.pInv])
+ 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 = RSA.construct([self.n, self.e])
+ 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 = []
- tests += list_test_cases(ImportKeyTests)
+ try:
+ from Crypto.PublicKey import _fastmath
+ tests += list_test_cases(ImportKeyTestsFast)
+ except ImportError:
+ pass
+ tests += list_test_cases(ImportKeyTestsSlow)
return tests
if __name__ == '__main__':
diff --git a/lib/Crypto/SelfTest/Random/Fortuna/test_FortunaAccumulator.py b/lib/Crypto/SelfTest/Random/Fortuna/test_FortunaAccumulator.py
index feb9b5a..c4e6ccf 100644
--- a/lib/Crypto/SelfTest/Random/Fortuna/test_FortunaAccumulator.py
+++ b/lib/Crypto/SelfTest/Random/Fortuna/test_FortunaAccumulator.py
@@ -45,12 +45,12 @@ class FortunaAccumulatorTests(unittest.TestCase):
self.assertEqual(0, pool.length)
self.assertEqual("5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456", pool.hexdigest())
- pool.append('abc')
+ pool.append(b('abc'))
self.assertEqual(3, pool.length)
self.assertEqual("4f8b42c22dd3729b519ba6f68d2da7cc5b2d606d05daed5ad5128cc03e6c6358", pool.hexdigest())
- pool.append("dbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq")
+ pool.append(b("dbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"))
self.assertEqual(56, pool.length)
self.assertEqual(b('0cffe17f68954dac3a84fb1458bd5ec99209449749b2b308b7cb55812f9563af'), b2a_hex(pool.digest()))
@@ -59,7 +59,7 @@ class FortunaAccumulatorTests(unittest.TestCase):
self.assertEqual(0, pool.length)
- pool.append('a' * 10**6)
+ pool.append(b('a') * 10**6)
self.assertEqual(10**6, pool.length)
self.assertEqual(b('80d1189477563e1b5206b2749f1afe4807e5705e8bd77887a60187a712156688'), b2a_hex(pool.digest()))
diff --git a/lib/Crypto/SelfTest/Random/Fortuna/test_SHAd256.py b/lib/Crypto/SelfTest/Random/Fortuna/test_SHAd256.py
index 285bac0..f94db8a 100644
--- a/lib/Crypto/SelfTest/Random/Fortuna/test_SHAd256.py
+++ b/lib/Crypto/SelfTest/Random/Fortuna/test_SHAd256.py
@@ -45,7 +45,7 @@ test_data = [
def get_tests(config={}):
from Crypto.Random.Fortuna import SHAd256
from Crypto.SelfTest.Hash.common import make_hash_tests
- return make_hash_tests(SHAd256, "SHAd256", test_data)
+ return make_hash_tests(SHAd256, "SHAd256", test_data, 32)
if __name__ == '__main__':
import unittest
diff --git a/lib/Crypto/SelfTest/Signature/__init__.py b/lib/Crypto/SelfTest/Signature/__init__.py
new file mode 100644
index 0000000..653b66c
--- /dev/null
+++ b/lib/Crypto/SelfTest/Signature/__init__.py
@@ -0,0 +1,40 @@
+# -*- coding: utf-8 -*-
+#
+# SelfTest/Signature/__init__.py: Self-test for signature modules
+#
+# ===================================================================
+# 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 for signature modules"""
+
+__revision__ = "$Id$"
+
+import os
+
+def get_tests(config={}):
+ tests = []
+ import test_pkcs1_15; tests += test_pkcs1_15.get_tests(config=config)
+ import test_pkcs1_pss; tests += test_pkcs1_pss.get_tests(config=config)
+ return tests
+
+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/lib/Crypto/SelfTest/Signature/test_pkcs1_15.py b/lib/Crypto/SelfTest/Signature/test_pkcs1_15.py
new file mode 100644
index 0000000..bc36696
--- /dev/null
+++ b/lib/Crypto/SelfTest/Signature/test_pkcs1_15.py
@@ -0,0 +1,219 @@
+# -*- coding: utf-8 -*-
+#
+# SelfTest/Signature/test_pkcs1_15.py: Self-test for PKCS#1 v1.5 signatures
+#
+# ===================================================================
+# 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.
+# ===================================================================
+
+__revision__ = "$Id$"
+
+import unittest
+
+from Crypto.PublicKey import RSA
+from Crypto.SelfTest.st_common import list_test_cases, a2b_hex, b2a_hex
+from Crypto.Hash import *
+from Crypto import Random
+from Crypto.Signature import PKCS1_v1_5 as PKCS
+from Crypto.Util.py3compat import *
+
+def isStr(s):
+ t = ''
+ try:
+ t += s
+ except TypeError:
+ return 0
+ return 1
+
+def rws(t):
+ """Remove white spaces, tabs, and new lines from a string"""
+ for c in ['\n', '\t', ' ']:
+ t = t.replace(c,'')
+ return t
+
+def t2b(t):
+ """Convert a text string with bytes in hex form to a byte string"""
+ clean = b(rws(t))
+ if len(clean)%2 == 1:
+ raise ValueError("Even number of characters expected")
+ return a2b_hex(clean)
+
+class PKCS1_15_Tests(unittest.TestCase):
+
+ # List of tuples with test data for PKCS#1 v1.5.
+ # Each tuple is made up by:
+ # Item #0: dictionary with RSA key component, or key to import
+ # Item #1: data to hash and sign
+ # Item #2: signature of the data #1, done with the key #0, after
+ # hashing it with #3
+ # Item #3: hash object generator
+
+ _testData = (
+
+ #
+ # Taken from ftp://ftp.rsa.com/pub/pkcs/ascii/examples.asc
+ # "Some Examples of the PKCS Standards", 1999
+ #
+ (
+
+ # Private key, from 2.1
+ {
+ 'n':'''0a 66 79 1d c6 98 81 68 de 7a b7 74 19 bb 7f b0 c0 01 c6
+ 27 10 27 00 75 14 29 42 e1 9a 8d 8c 51 d0 53 b3 e3 78 2a 1d
+ e5 dc 5a f4 eb e9 94 68 17 01 14 a1 df e6 7c dc 9a 9a f5 5d
+ 65 56 20 bb ab''',
+ 'e':'''01 00
+ 01''',
+ 'd':'''01 23 c5 b6 1b a3 6e db 1d 36 79 90 41 99 a8 9e a8 0c 09
+ b9 12 2e 14 00 c0 9a dc f7 78 46 76 d0 1d 23 35 6a 7d 44 d6
+ bd 8b d5 0e 94 bf c7 23 fa 87 d8 86 2b 75 17 76 91 c1 1d 75
+ 76 92 df 88 81'''
+ },
+ # Data to sign, from 3.1
+ '''30 81 a4 02 01 00 30 42 31 0b 30 09 06
+ 03 55 04 06 13 02 55 53 31 1d 30 1b 06 03 55 04 0a 13 14
+ 45 78 61 6d 70 6c 65 20 4f 72 67 61 6e 69 7a 61 74 69 6f
+ 6e 31 14 30 12 06 03 55 04 03 13 0b 54 65 73 74 20 55 73
+ 65 72 20 31 30 5b 30 0d 06 09 2a 86 48 86 f7 0d 01 01 01
+ 05 00 03 4a 00 30 47 02 40
+ 0a 66 79 1d c6 98 81 68 de 7a b7 74 19 bb 7f b0
+ c0 01 c6 27 10 27 00 75 14 29 42 e1 9a 8d 8c 51
+ d0 53 b3 e3 78 2a 1d e5 dc 5a f4 eb e9 94 68 17
+ 01 14 a1 df e6 7c dc 9a 9a f5 5d 65 56 20 bb ab
+ 02 03 01 00 01''',
+ # Signature, from 3.2 (at the very end)
+ '''06 db 36 cb 18 d3 47 5b 9c 01 db 3c 78 95 28 08
+ 02 79 bb ae ff 2b 7d 55 8e d6 61 59 87 c8 51 86
+ 3f 8a 6c 2c ff bc 89 c3 f7 5a 18 d9 6b 12 7c 71
+ 7d 54 d0 d8 04 8d a8 a0 54 46 26 d1 7a 2a 8f be''',
+ MD2
+ ),
+
+ #
+ # RSA keypair generated with openssl
+ #
+ (
+ """-----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-----""",
+ "This is a test\x0a",
+ #
+ # PKCS#1 signature computed with openssl
+ #
+ '''4a700a16432a291a3194646952687d5316458b8b86fb0a25aa30e0dcecdb
+ 442676759ac63d56ec1499c3ae4c0013c2053cabd5b5804848994541ac16
+ fa243a4d''',
+ SHA
+ ),
+
+ #
+ # Test vector from http://www.di-mgt.com.au/rsa_alg.html#signpkcs1
+ #
+ (
+ {
+ 'n':'''E08973398DD8F5F5E88776397F4EB005BB5383DE0FB7ABDC7DC775290D052E6D
+ 12DFA68626D4D26FAA5829FC97ECFA82510F3080BEB1509E4644F12CBBD832CF
+ C6686F07D9B060ACBEEE34096A13F5F7050593DF5EBA3556D961FF197FC981E6
+ F86CEA874070EFAC6D2C749F2DFA553AB9997702A648528C4EF357385774575F''',
+ 'e':'''010001''',
+ 'd':'''00A403C327477634346CA686B57949014B2E8AD2C862B2C7D748096A8B91F736
+ F275D6E8CD15906027314735644D95CD6763CEB49F56AC2F376E1CEE0EBF282D
+ F439906F34D86E085BD5656AD841F313D72D395EFE33CBFF29E4030B3D05A28F
+ B7F18EA27637B07957D32F2BDE8706227D04665EC91BAF8B1AC3EC9144AB7F21'''
+ },
+ "abc",
+ '''60AD5A78FB4A4030EC542C8974CD15F55384E836554CEDD9A322D5F4135C6267
+ A9D20970C54E6651070B0144D43844C899320DD8FA7819F7EBC6A7715287332E
+ C8675C136183B3F8A1F81EF969418267130A756FDBB2C71D9A667446E34E0EAD
+ 9CF31BFB66F816F319D0B7E430A5F2891553986E003720261C7E9022C0D9F11F''',
+ SHA
+ )
+
+ )
+
+ def testSign1(self):
+ for i in range(len(self._testData)):
+ row = self._testData[i]
+ # Build the key
+ if isStr(row[0]):
+ key = RSA.importKey(row[0])
+ else:
+ comps = [ long(rws(row[0][x]),16) for x in ('n','e','d') ]
+ key = RSA.construct(comps)
+ h = row[3].new()
+ # Data to sign can either be in hex form or not
+ try:
+ h.update(t2b(row[1]))
+ except:
+ h.update(b(row[1]))
+ # The real test
+ signer = PKCS.new(key)
+ self.failUnless(signer.can_sign())
+ s = signer.sign(h)
+ self.assertEqual(s, t2b(row[2]))
+
+ def testVerify1(self):
+ for i in range(len(self._testData)):
+ row = self._testData[i]
+ # Build the key
+ if isStr(row[0]):
+ key = RSA.importKey(row[0]).publickey()
+ else:
+ comps = [ long(rws(row[0][x]),16) for x in ('n','e') ]
+ key = RSA.construct(comps)
+ h = row[3].new()
+ # Data to sign can either be in hex form or not
+ try:
+ h.update(t2b(row[1]))
+ except:
+ h.update(b(row[1]))
+ # The real test
+ verifier = PKCS.new(key)
+ self.failIf(verifier.can_sign())
+ result = verifier.verify(h, t2b(row[2]))
+ self.failUnless(result)
+
+ def testSignVerify(self):
+ rng = Random.new().read
+ key = RSA.generate(1024, rng)
+
+ for hashmod in (MD2,MD5,SHA,SHA224,SHA256,SHA384,SHA512,RIPEMD):
+ h = hashmod.new()
+ h.update(b('blah blah blah'))
+
+ signer = PKCS.new(key)
+ s = signer.sign(h)
+ result = signer.verify(h, s)
+ self.failUnless(result)
+
+
+def get_tests(config={}):
+ tests = []
+ tests += list_test_cases(PKCS1_15_Tests)
+ 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/lib/Crypto/SelfTest/Signature/test_pkcs1_pss.py b/lib/Crypto/SelfTest/Signature/test_pkcs1_pss.py
new file mode 100644
index 0000000..f5256a5
--- /dev/null
+++ b/lib/Crypto/SelfTest/Signature/test_pkcs1_pss.py
@@ -0,0 +1,446 @@
+# -*- coding: utf-8 -*-
+#
+# SelfTest/Signature/test_pkcs1_pss.py: Self-test for PKCS#1 PSS signatures
+#
+# ===================================================================
+# 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 import Random
+from Crypto.SelfTest.st_common import list_test_cases, a2b_hex, b2a_hex
+from Crypto.Hash import *
+from Crypto.Signature import PKCS1_PSS as PKCS
+from Crypto.Util.py3compat import *
+
+def isStr(s):
+ t = ''
+ try:
+ t += s
+ except TypeError:
+ return 0
+ return 1
+
+def rws(t):
+ """Remove white spaces, tabs, and new lines from a string"""
+ for c in ['\t', '\n', ' ']:
+ t = t.replace(c,'')
+ return t
+
+def t2b(t):
+ """Convert a text string with bytes in hex form to a byte string"""
+ clean = b(rws(t))
+ if len(clean)%2 == 1:
+ raise ValueError("Even number of characters expected")
+ return a2b_hex(clean)
+
+# Helper class to count how many bytes have been requested
+# from the key's private RNG, w/o counting those used for blinding
+class MyKey:
+ def __init__(self, key):
+ self._key = key
+ self.n = key.n
+ self.asked = 0
+ def _randfunc(self, N):
+ self.asked += N
+ return self._key._randfunc(N)
+ def sign(self, m):
+ return self._key.sign(m)
+ def has_private(self):
+ return self._key.has_private()
+ def decrypt(self, m):
+ return self._key.decrypt(m)
+ def verify(self, m, p):
+ return self._key.verify(m, p)
+ def encrypt(self, m, p):
+ return self._key.encrypt(m, p)
+
+class PKCS1_PSS_Tests(unittest.TestCase):
+
+ # List of tuples with test data for PKCS#1 PSS
+ # Each tuple is made up by:
+ # Item #0: dictionary with RSA key component, or key to import
+ # Item #1: data to hash and sign
+ # Item #2: signature of the data #1, done with the key #0,
+ # and salt #3 after hashing it with #4
+ # Item #3: salt
+ # Item #4: hash object generator
+
+ _testData = (
+
+ #
+ # From in pss-vect.txt to be found in
+ # ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip
+ #
+ (
+ # Private key
+ {
+ 'n':'''a2 ba 40 ee 07 e3 b2 bd 2f 02 ce 22 7f 36 a1 95
+ 02 44 86 e4 9c 19 cb 41 bb bd fb ba 98 b2 2b 0e
+ 57 7c 2e ea ff a2 0d 88 3a 76 e6 5e 39 4c 69 d4
+ b3 c0 5a 1e 8f ad da 27 ed b2 a4 2b c0 00 fe 88
+ 8b 9b 32 c2 2d 15 ad d0 cd 76 b3 e7 93 6e 19 95
+ 5b 22 0d d1 7d 4e a9 04 b1 ec 10 2b 2e 4d e7 75
+ 12 22 aa 99 15 10 24 c7 cb 41 cc 5e a2 1d 00 ee
+ b4 1f 7c 80 08 34 d2 c6 e0 6b ce 3b ce 7e a9 a5''',
+ 'e':'''01 00 01''',
+ # In the test vector, only p and q were given...
+ # d is computed offline as e^{-1} mod (p-1)(q-1)
+ 'd':'''50e2c3e38d886110288dfc68a9533e7e12e27d2aa56
+ d2cdb3fb6efa990bcff29e1d2987fb711962860e7391b1ce01
+ ebadb9e812d2fbdfaf25df4ae26110a6d7a26f0b810f54875e
+ 17dd5c9fb6d641761245b81e79f8c88f0e55a6dcd5f133abd3
+ 5f8f4ec80adf1bf86277a582894cb6ebcd2162f1c7534f1f49
+ 47b129151b71'''
+ },
+
+ # Data to sign
+ '''85 9e ef 2f d7 8a ca 00 30 8b dc 47 11 93 bf 55
+ bf 9d 78 db 8f 8a 67 2b 48 46 34 f3 c9 c2 6e 64
+ 78 ae 10 26 0f e0 dd 8c 08 2e 53 a5 29 3a f2 17
+ 3c d5 0c 6d 5d 35 4f eb f7 8b 26 02 1c 25 c0 27
+ 12 e7 8c d4 69 4c 9f 46 97 77 e4 51 e7 f8 e9 e0
+ 4c d3 73 9c 6b bf ed ae 48 7f b5 56 44 e9 ca 74
+ ff 77 a5 3c b7 29 80 2f 6e d4 a5 ff a8 ba 15 98
+ 90 fc''',
+ # Signature
+ '''8d aa 62 7d 3d e7 59 5d 63 05 6c 7e c6 59 e5 44
+ 06 f1 06 10 12 8b aa e8 21 c8 b2 a0 f3 93 6d 54
+ dc 3b dc e4 66 89 f6 b7 95 1b b1 8e 84 05 42 76
+ 97 18 d5 71 5d 21 0d 85 ef bb 59 61 92 03 2c 42
+ be 4c 29 97 2c 85 62 75 eb 6d 5a 45 f0 5f 51 87
+ 6f c6 74 3d ed dd 28 ca ec 9b b3 0e a9 9e 02 c3
+ 48 82 69 60 4f e4 97 f7 4c cd 7c 7f ca 16 71 89
+ 71 23 cb d3 0d ef 5d 54 a2 b5 53 6a d9 0a 74 7e''',
+ # Salt
+ '''e3 b5 d5 d0 02 c1 bc e5 0c 2b 65 ef 88 a1 88 d8
+ 3b ce 7e 61''',
+ # Hash algorithm
+ SHA
+ ),
+
+ #
+ # Example 1.1 to be found in
+ # ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip
+ #
+ (
+ # Private key
+ {
+ 'n':'''a5 6e 4a 0e 70 10 17 58 9a 51 87 dc 7e a8 41 d1
+ 56 f2 ec 0e 36 ad 52 a4 4d fe b1 e6 1f 7a d9 91
+ d8 c5 10 56 ff ed b1 62 b4 c0 f2 83 a1 2a 88 a3
+ 94 df f5 26 ab 72 91 cb b3 07 ce ab fc e0 b1 df
+ d5 cd 95 08 09 6d 5b 2b 8b 6d f5 d6 71 ef 63 77
+ c0 92 1c b2 3c 27 0a 70 e2 59 8e 6f f8 9d 19 f1
+ 05 ac c2 d3 f0 cb 35 f2 92 80 e1 38 6b 6f 64 c4
+ ef 22 e1 e1 f2 0d 0c e8 cf fb 22 49 bd 9a 21 37''',
+ 'e':'''01 00 01''',
+ 'd':'''33 a5 04 2a 90 b2 7d 4f 54 51 ca 9b bb d0 b4 47
+ 71 a1 01 af 88 43 40 ae f9 88 5f 2a 4b be 92 e8
+ 94 a7 24 ac 3c 56 8c 8f 97 85 3a d0 7c 02 66 c8
+ c6 a3 ca 09 29 f1 e8 f1 12 31 88 44 29 fc 4d 9a
+ e5 5f ee 89 6a 10 ce 70 7c 3e d7 e7 34 e4 47 27
+ a3 95 74 50 1a 53 26 83 10 9c 2a ba ca ba 28 3c
+ 31 b4 bd 2f 53 c3 ee 37 e3 52 ce e3 4f 9e 50 3b
+ d8 0c 06 22 ad 79 c6 dc ee 88 35 47 c6 a3 b3 25'''
+ },
+ # Message
+ '''cd c8 7d a2 23 d7 86 df 3b 45 e0 bb bc 72 13 26
+ d1 ee 2a f8 06 cc 31 54 75 cc 6f 0d 9c 66 e1 b6
+ 23 71 d4 5c e2 39 2e 1a c9 28 44 c3 10 10 2f 15
+ 6a 0d 8d 52 c1 f4 c4 0b a3 aa 65 09 57 86 cb 76
+ 97 57 a6 56 3b a9 58 fe d0 bc c9 84 e8 b5 17 a3
+ d5 f5 15 b2 3b 8a 41 e7 4a a8 67 69 3f 90 df b0
+ 61 a6 e8 6d fa ae e6 44 72 c0 0e 5f 20 94 57 29
+ cb eb e7 7f 06 ce 78 e0 8f 40 98 fb a4 1f 9d 61
+ 93 c0 31 7e 8b 60 d4 b6 08 4a cb 42 d2 9e 38 08
+ a3 bc 37 2d 85 e3 31 17 0f cb f7 cc 72 d0 b7 1c
+ 29 66 48 b3 a4 d1 0f 41 62 95 d0 80 7a a6 25 ca
+ b2 74 4f d9 ea 8f d2 23 c4 25 37 02 98 28 bd 16
+ be 02 54 6f 13 0f d2 e3 3b 93 6d 26 76 e0 8a ed
+ 1b 73 31 8b 75 0a 01 67 d0''',
+ # Signature
+ '''90 74 30 8f b5 98 e9 70 1b 22 94 38 8e 52 f9 71
+ fa ac 2b 60 a5 14 5a f1 85 df 52 87 b5 ed 28 87
+ e5 7c e7 fd 44 dc 86 34 e4 07 c8 e0 e4 36 0b c2
+ 26 f3 ec 22 7f 9d 9e 54 63 8e 8d 31 f5 05 12 15
+ df 6e bb 9c 2f 95 79 aa 77 59 8a 38 f9 14 b5 b9
+ c1 bd 83 c4 e2 f9 f3 82 a0 d0 aa 35 42 ff ee 65
+ 98 4a 60 1b c6 9e b2 8d eb 27 dc a1 2c 82 c2 d4
+ c3 f6 6c d5 00 f1 ff 2b 99 4d 8a 4e 30 cb b3 3c''',
+ # Salt
+ '''de e9 59 c7 e0 64 11 36 14 20 ff 80 18 5e d5 7f
+ 3e 67 76 af''',
+ # Hash
+ SHA
+ ),
+
+ #
+ # Example 1.2 to be found in
+ # ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip
+ #
+ (
+ # Private key
+ {
+ 'n':'''a5 6e 4a 0e 70 10 17 58 9a 51 87 dc 7e a8 41 d1
+ 56 f2 ec 0e 36 ad 52 a4 4d fe b1 e6 1f 7a d9 91
+ d8 c5 10 56 ff ed b1 62 b4 c0 f2 83 a1 2a 88 a3
+ 94 df f5 26 ab 72 91 cb b3 07 ce ab fc e0 b1 df
+ d5 cd 95 08 09 6d 5b 2b 8b 6d f5 d6 71 ef 63 77
+ c0 92 1c b2 3c 27 0a 70 e2 59 8e 6f f8 9d 19 f1
+ 05 ac c2 d3 f0 cb 35 f2 92 80 e1 38 6b 6f 64 c4
+ ef 22 e1 e1 f2 0d 0c e8 cf fb 22 49 bd 9a 21 37''',
+ 'e':'''01 00 01''',
+ 'd':'''33 a5 04 2a 90 b2 7d 4f 54 51 ca 9b bb d0 b4 47
+ 71 a1 01 af 88 43 40 ae f9 88 5f 2a 4b be 92 e8
+ 94 a7 24 ac 3c 56 8c 8f 97 85 3a d0 7c 02 66 c8
+ c6 a3 ca 09 29 f1 e8 f1 12 31 88 44 29 fc 4d 9a
+ e5 5f ee 89 6a 10 ce 70 7c 3e d7 e7 34 e4 47 27
+ a3 95 74 50 1a 53 26 83 10 9c 2a ba ca ba 28 3c
+ 31 b4 bd 2f 53 c3 ee 37 e3 52 ce e3 4f 9e 50 3b
+ d8 0c 06 22 ad 79 c6 dc ee 88 35 47 c6 a3 b3 25'''
+ },
+ # Message
+ '''85 13 84 cd fe 81 9c 22 ed 6c 4c cb 30 da eb 5c
+ f0 59 bc 8e 11 66 b7 e3 53 0c 4c 23 3e 2b 5f 8f
+ 71 a1 cc a5 82 d4 3e cc 72 b1 bc a1 6d fc 70 13
+ 22 6b 9e''',
+ # Signature
+ '''3e f7 f4 6e 83 1b f9 2b 32 27 41 42 a5 85 ff ce
+ fb dc a7 b3 2a e9 0d 10 fb 0f 0c 72 99 84 f0 4e
+ f2 9a 9d f0 78 07 75 ce 43 73 9b 97 83 83 90 db
+ 0a 55 05 e6 3d e9 27 02 8d 9d 29 b2 19 ca 2c 45
+ 17 83 25 58 a5 5d 69 4a 6d 25 b9 da b6 60 03 c4
+ cc cd 90 78 02 19 3b e5 17 0d 26 14 7d 37 b9 35
+ 90 24 1b e5 1c 25 05 5f 47 ef 62 75 2c fb e2 14
+ 18 fa fe 98 c2 2c 4d 4d 47 72 4f db 56 69 e8 43''',
+ # Salt
+ '''ef 28 69 fa 40 c3 46 cb 18 3d ab 3d 7b ff c9 8f
+ d5 6d f4 2d''',
+ # Hash
+ SHA
+ ),
+
+ #
+ # Example 2.1 to be found in
+ # ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip
+ #
+ (
+ # Private key
+ {
+ 'n':'''01 d4 0c 1b cf 97 a6 8a e7 cd bd 8a 7b f3 e3 4f
+ a1 9d cc a4 ef 75 a4 74 54 37 5f 94 51 4d 88 fe
+ d0 06 fb 82 9f 84 19 ff 87 d6 31 5d a6 8a 1f f3
+ a0 93 8e 9a bb 34 64 01 1c 30 3a d9 91 99 cf 0c
+ 7c 7a 8b 47 7d ce 82 9e 88 44 f6 25 b1 15 e5 e9
+ c4 a5 9c f8 f8 11 3b 68 34 33 6a 2f d2 68 9b 47
+ 2c bb 5e 5c ab e6 74 35 0c 59 b6 c1 7e 17 68 74
+ fb 42 f8 fc 3d 17 6a 01 7e dc 61 fd 32 6c 4b 33
+ c9''',
+ 'e':'''01 00 01''',
+ 'd':'''02 7d 14 7e 46 73 05 73 77 fd 1e a2 01 56 57 72
+ 17 6a 7d c3 83 58 d3 76 04 56 85 a2 e7 87 c2 3c
+ 15 57 6b c1 6b 9f 44 44 02 d6 bf c5 d9 8a 3e 88
+ ea 13 ef 67 c3 53 ec a0 c0 dd ba 92 55 bd 7b 8b
+ b5 0a 64 4a fd fd 1d d5 16 95 b2 52 d2 2e 73 18
+ d1 b6 68 7a 1c 10 ff 75 54 5f 3d b0 fe 60 2d 5f
+ 2b 7f 29 4e 36 01 ea b7 b9 d1 ce cd 76 7f 64 69
+ 2e 3e 53 6c a2 84 6c b0 c2 dd 48 6a 39 fa 75 b1'''
+ },
+ # Message
+ '''da ba 03 20 66 26 3f ae db 65 98 48 11 52 78 a5
+ 2c 44 fa a3 a7 6f 37 51 5e d3 36 32 10 72 c4 0a
+ 9d 9b 53 bc 05 01 40 78 ad f5 20 87 51 46 aa e7
+ 0f f0 60 22 6d cb 7b 1f 1f c2 7e 93 60''',
+ # Signature
+ '''01 4c 5b a5 33 83 28 cc c6 e7 a9 0b f1 c0 ab 3f
+ d6 06 ff 47 96 d3 c1 2e 4b 63 9e d9 13 6a 5f ec
+ 6c 16 d8 88 4b dd 99 cf dc 52 14 56 b0 74 2b 73
+ 68 68 cf 90 de 09 9a db 8d 5f fd 1d ef f3 9b a4
+ 00 7a b7 46 ce fd b2 2d 7d f0 e2 25 f5 46 27 dc
+ 65 46 61 31 72 1b 90 af 44 53 63 a8 35 8b 9f 60
+ 76 42 f7 8f ab 0a b0 f4 3b 71 68 d6 4b ae 70 d8
+ 82 78 48 d8 ef 1e 42 1c 57 54 dd f4 2c 25 89 b5
+ b3''',
+ # Salt
+ '''57 bf 16 0b cb 02 bb 1d c7 28 0c f0 45 85 30 b7
+ d2 83 2f f7''',
+ SHA
+ ),
+
+ #
+ # Example 8.1 to be found in
+ # ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip
+ #
+ (
+ # Private key
+ {
+ 'n':'''49 53 70 a1 fb 18 54 3c 16 d3 63 1e 31 63 25 5d
+ f6 2b e6 ee e8 90 d5 f2 55 09 e4 f7 78 a8 ea 6f
+ bb bc df 85 df f6 4e 0d 97 20 03 ab 36 81 fb ba
+ 6d d4 1f d5 41 82 9b 2e 58 2d e9 f2 a4 a4 e0 a2
+ d0 90 0b ef 47 53 db 3c ee 0e e0 6c 7d fa e8 b1
+ d5 3b 59 53 21 8f 9c ce ea 69 5b 08 66 8e de aa
+ dc ed 94 63 b1 d7 90 d5 eb f2 7e 91 15 b4 6c ad
+ 4d 9a 2b 8e fa b0 56 1b 08 10 34 47 39 ad a0 73
+ 3f''',
+ 'e':'''01 00 01''',
+ 'd':'''6c 66 ff e9 89 80 c3 8f cd ea b5 15 98 98 83 61
+ 65 f4 b4 b8 17 c4 f6 a8 d4 86 ee 4e a9 13 0f e9
+ b9 09 2b d1 36 d1 84 f9 5f 50 4a 60 7e ac 56 58
+ 46 d2 fd d6 59 7a 89 67 c7 39 6e f9 5a 6e ee bb
+ 45 78 a6 43 96 6d ca 4d 8e e3 de 84 2d e6 32 79
+ c6 18 15 9c 1a b5 4a 89 43 7b 6a 61 20 e4 93 0a
+ fb 52 a4 ba 6c ed 8a 49 47 ac 64 b3 0a 34 97 cb
+ e7 01 c2 d6 26 6d 51 72 19 ad 0e c6 d3 47 db e9'''
+ },
+ # Message
+ '''81 33 2f 4b e6 29 48 41 5e a1 d8 99 79 2e ea cf
+ 6c 6e 1d b1 da 8b e1 3b 5c ea 41 db 2f ed 46 70
+ 92 e1 ff 39 89 14 c7 14 25 97 75 f5 95 f8 54 7f
+ 73 56 92 a5 75 e6 92 3a f7 8f 22 c6 99 7d db 90
+ fb 6f 72 d7 bb 0d d5 74 4a 31 de cd 3d c3 68 58
+ 49 83 6e d3 4a ec 59 63 04 ad 11 84 3c 4f 88 48
+ 9f 20 97 35 f5 fb 7f da f7 ce c8 ad dc 58 18 16
+ 8f 88 0a cb f4 90 d5 10 05 b7 a8 e8 4e 43 e5 42
+ 87 97 75 71 dd 99 ee a4 b1 61 eb 2d f1 f5 10 8f
+ 12 a4 14 2a 83 32 2e db 05 a7 54 87 a3 43 5c 9a
+ 78 ce 53 ed 93 bc 55 08 57 d7 a9 fb''',
+ # Signature
+ '''02 62 ac 25 4b fa 77 f3 c1 ac a2 2c 51 79 f8 f0
+ 40 42 2b 3c 5b af d4 0a 8f 21 cf 0f a5 a6 67 cc
+ d5 99 3d 42 db af b4 09 c5 20 e2 5f ce 2b 1e e1
+ e7 16 57 7f 1e fa 17 f3 da 28 05 2f 40 f0 41 9b
+ 23 10 6d 78 45 aa f0 11 25 b6 98 e7 a4 df e9 2d
+ 39 67 bb 00 c4 d0 d3 5b a3 55 2a b9 a8 b3 ee f0
+ 7c 7f ec db c5 42 4a c4 db 1e 20 cb 37 d0 b2 74
+ 47 69 94 0e a9 07 e1 7f bb ca 67 3b 20 52 23 80
+ c5''',
+ # Salt
+ '''1d 65 49 1d 79 c8 64 b3 73 00 9b e6 f6 f2 46 7b
+ ac 4c 78 fa''',
+ SHA
+ )
+ )
+
+ def testSign1(self):
+ for i in range(len(self._testData)):
+ # Build the key
+ comps = [ long(rws(self._testData[i][0][x]),16) for x in ('n','e','d') ]
+ key = MyKey(RSA.construct(comps))
+ # Hash function
+ h = self._testData[i][4].new()
+ # Data to sign
+ h.update(t2b(self._testData[i][1]))
+ # Salt
+ test_salt = t2b(self._testData[i][3])
+ key._randfunc = lambda N: test_salt
+ # The real test
+ signer = PKCS.new(key)
+ self.failUnless(signer.can_sign())
+ s = signer.sign(h)
+ self.assertEqual(s, t2b(self._testData[i][2]))
+
+ def testVerify1(self):
+ for i in range(len(self._testData)):
+ # Build the key
+ comps = [ long(rws(self._testData[i][0][x]),16) for x in ('n','e') ]
+ key = MyKey(RSA.construct(comps))
+ # Hash function
+ h = self._testData[i][4].new()
+ # Data to sign
+ h.update(t2b(self._testData[i][1]))
+ # Salt
+ test_salt = t2b(self._testData[i][3])
+ # The real test
+ key._randfunc = lambda N: test_salt
+ verifier = PKCS.new(key)
+ self.failIf(verifier.can_sign())
+ result = verifier.verify(h, t2b(self._testData[i][2]))
+ self.failUnless(result)
+
+ def testSignVerify(self):
+ 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):
+ global mgfcalls
+ mgfcalls += 1
+ return bchr(0x00)*maskLen
+
+ # Verify that PSS is friendly to all ciphers
+ for hashmod in (MD2,MD5,SHA,SHA224,SHA256,SHA384,RIPEMD):
+ h = hashmod.new()
+ h.update(b('blah blah blah'))
+
+ # Verify that sign() asks for as many random bytes
+ # as the hash output size
+ key.asked = 0
+ signer = PKCS.new(key)
+ s = signer.sign(h)
+ self.failUnless(signer.verify(h, s))
+ self.assertEqual(key.asked, h.digest_size)
+
+ h = SHA.new()
+ h.update(b('blah blah blah'))
+
+ # Verify that sign() uses a different salt length
+ for sLen in (0,3,21):
+ key.asked = 0
+ signer = PKCS.new(key, saltLen=sLen)
+ s = signer.sign(h)
+ self.assertEqual(key.asked, sLen)
+ self.failUnless(signer.verify(h, s))
+
+ # Verify that sign() uses the custom MGF
+ mgfcalls = 0
+ signer = PKCS.new(key, newMGF)
+ s = signer.sign(h)
+ self.assertEqual(mgfcalls, 1)
+ self.failUnless(signer.verify(h, s))
+
+ # Verify that sign() does not call the RNG
+ # when salt length is 0, even when a new MGF is provided
+ key.asked = 0
+ mgfcalls = 0
+ signer = PKCS.new(key, newMGF, 0)
+ s = signer.sign(h)
+ self.assertEqual(key.asked,0)
+ self.assertEqual(mgfcalls, 1)
+ self.failUnless(signer.verify(h, s))
+
+def get_tests(config={}):
+ tests = []
+ tests += list_test_cases(PKCS1_PSS_Tests)
+ return tests
+
+if __name__ == '__main__':
+ suite = lambda: unittest.TestSuite(get_tests())
+ unittest.main(defaultTest='suite')
+
+# vim:set ts=4 sw=4 sts=4
diff --git a/lib/Crypto/SelfTest/Util/test_asn1.py b/lib/Crypto/SelfTest/Util/test_asn1.py
index f856eb3..578dabe 100644
--- a/lib/Crypto/SelfTest/Util/test_asn1.py
+++ b/lib/Crypto/SelfTest/Util/test_asn1.py
@@ -27,6 +27,7 @@ __revision__ = "$Id$"
import unittest
import sys
+from Crypto.Util.py3compat import *
from Crypto.Util.asn1 import DerSequence, DerObject
class DerObjectTests(unittest.TestCase):
@@ -40,6 +41,10 @@ class DerObjectTests(unittest.TestCase):
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
@@ -52,21 +57,21 @@ class DerObjectTests(unittest.TestCase):
# Long payload
der = DerObject(b('\x34'))
der.payload = b("0")*128
- self.assertEquals(der.encode(), b('\x34\x81\x80') + (b("0")*128))
+ self.assertEquals(der.encode(), b('\x34\x81\x80' + "0"*128))
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, b("\x20"))
+ self.assertEquals(der.typeTag, 0x20)
def testObjDecode2(self):
# Decode short payload
der = DerObject()
- der.decode(b('\x22\x81\x80') + (b("1")*128))
+ der.decode(b('\x22\x81\x80' + "1"*128))
self.assertEquals(der.payload, b("1")*128)
- self.assertEquals(der.typeTag, b("\x22"))
+ self.assertEquals(der.typeTag, 0x22)
class DerSequenceTests(unittest.TestCase):
@@ -200,6 +205,8 @@ class DerSequenceTests(unittest.TestCase):
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')+
diff --git a/lib/Crypto/SelfTest/Util/test_number.py b/lib/Crypto/SelfTest/Util/test_number.py
index 8397efd..7a74e3a 100644
--- a/lib/Crypto/SelfTest/Util/test_number.py
+++ b/lib/Crypto/SelfTest/Util/test_number.py
@@ -265,6 +265,12 @@ class MiscTests(unittest.TestCase):
4186358431L * 8372716861L, 1576820467L * 3153640933L):
self.assertEqual(number.isPrime(long(composite)), False)
+ def test_size(self):
+ self.assertEqual(number.size(2),2)
+ self.assertEqual(number.size(3),2)
+ self.assertEqual(number.size(0xa2),8)
+ self.assertEqual(number.size(0xa2ba40),8*3)
+ self.assertEqual(number.size(0xa2ba40ee07e3b2bd2f02ce227f36a195024486e49c19cb41bbbdfbba98b22b0e577c2eeaffa20d883a76e65e394c69d4b3c05a1e8fadda27edb2a42bc000fe888b9b32c22d15add0cd76b3e7936e19955b220dd17d4ea904b1ec102b2e4de7751222aa99151024c7cb41cc5ea21d00eeb41f7c800834d2c6e06bce3bce7ea9a5L), 1024)
def get_tests(config={}):
from Crypto.SelfTest.st_common import list_test_cases
diff --git a/lib/Crypto/SelfTest/__init__.py b/lib/Crypto/SelfTest/__init__.py
index c4cef21..40b3969 100644
--- a/lib/Crypto/SelfTest/__init__.py
+++ b/lib/Crypto/SelfTest/__init__.py
@@ -82,6 +82,7 @@ def get_tests(config={}):
from Crypto.SelfTest import PublicKey; tests += PublicKey.get_tests(config=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)
return tests
if __name__ == '__main__':
diff --git a/lib/Crypto/Signature/PKCS1_PSS.py b/lib/Crypto/Signature/PKCS1_PSS.py
new file mode 100644
index 0000000..a89faef
--- /dev/null
+++ b/lib/Crypto/Signature/PKCS1_PSS.py
@@ -0,0 +1,355 @@
+# -*- coding: utf-8 -*-
+#
+# Signature/PKCS1_PSS.py : PKCS#1 PPS
+#
+# ===================================================================
+# 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.
+# ===================================================================
+
+"""RSA digital signature protocol with appendix according to PKCS#1 PSS.
+
+See RFC3447__ or the `original RSA Labs specification`__.
+
+This scheme is more properly called ``RSASSA-PSS``.
+
+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 SHA
+ >>> from Crypto.PublicKey import RSA
+ >>> from Crypto import Random
+ >>>
+ >>> message = 'To be signed'
+ >>> key = RSA.importKey(open('privkey.der').read())
+ >>> h = SHA.new()
+ >>> h.update(message)
+ >>> signer = PKCS1_PSS.new(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 = SHA.new()
+ >>> h.update(message)
+ >>> verifier = PKCS1_PSS.new(key)
+ >>> if verifier.verify(h, signature):
+ >>> print "The signature is authentic."
+ >>> else:
+ >>> print "The signature is not authentic."
+
+:undocumented: __revision__, __package__
+
+.. __: http://www.ietf.org/rfc/rfc3447.txt
+.. __: http://www.rsa.com/rsalabs/node.asp?id=2125
+"""
+
+# Allow nested scopes in Python 2.1
+# See http://oreilly.com/pub/a/python/2001/04/19/pythonnews.html
+from __future__ import nested_scopes
+
+__revision__ = "$Id$"
+__all__ = [ 'new' ]
+
+from Crypto.Util.py3compat import *
+if sys.version_info[0] == 2 and sys.version_info[1] == 1:
+ from Crypto.Util.py21compat import *
+import Crypto.Util.number
+from Crypto.Util.number import ceil_shift, ceil_div, long_to_bytes
+from Crypto.Util.strxor import strxor
+
+class PSS_SigScheme:
+ """This signature scheme can perform PKCS#1 PSS RSA signature or verification."""
+
+ def __init__(self, key, mgfunc, saltLen):
+ """Initialize this PKCS#1 PSS signature scheme object.
+
+ :Parameters:
+ key : an RSA key object
+ If a private half is given, both signature and verification are possible.
+ If a public half is given, only verification is possible.
+ 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.
+ saltLen : int
+ Length of the salt, in bytes.
+ """
+ self._key = key
+ self._saltLen = saltLen
+ self._mgfunc = mgfunc
+
+ def can_sign(self):
+ """Return True if this cipher object can be used for signing messages."""
+ return self._key.has_private()
+
+ def sign(self, mhash):
+ """Produce the PKCS#1 PSS signature of a message.
+
+ This function is named ``RSASSA-PSS-SIGN``, and is specified in
+ section 8.1.1 of RFC3447.
+
+ :Parameters:
+ mhash : hash object
+ The hash that was carried out over the message. This is an object
+ belonging to the `Crypto.Hash` module.
+
+ :Return: The PSS signature encoded as a string.
+ :Raise ValueError:
+ If the RSA key length is not sufficiently long to deal with the given
+ hash algorithm.
+ :Raise TypeError:
+ If the RSA key has no private half.
+
+ :attention: Modify the salt length and the mask generation function only
+ if you know what you are doing.
+ The receiver must use the same parameters too.
+ """
+ # TODO: Verify the key is RSA
+
+ randfunc = self._key._randfunc
+
+ # Set defaults for salt length and mask generation function
+ if self._saltLen == None:
+ sLen = mhash.digest_size
+ else:
+ sLen = self._saltLen
+ if self._mgfunc:
+ mgf = self._mgfunc
+ else:
+ mgf = lambda x,y: MGF1(x,y,mhash)
+
+ modBits = Crypto.Util.number.size(self._key.n)
+
+ # See 8.1.1 in RFC3447
+ k = ceil_div(modBits,8) # Convert from bits to bytes
+ # Step 1
+ em = EMSA_PSS_ENCODE(mhash, modBits-1, randfunc, mgf, sLen)
+ # Step 2a (OS2IP) and 2b (RSASP1)
+ m = self._key.decrypt(em)
+ # Step 2c (I2OSP)
+ S = bchr(0x00)*(k-len(m)) + m
+ return S
+
+ def verify(self, mhash, S):
+ """Verify that a certain PKCS#1 PSS signature is authentic.
+
+ This function checks if the party holding the private half of the given
+ RSA key has really signed the message.
+
+ This function is called ``RSASSA-PSS-VERIFY``, and is specified in section
+ 8.1.2 of RFC3447.
+
+ :Parameters:
+ mhash : hash object
+ The hash that was carried out over the message. This is an object
+ belonging to the `Crypto.Hash` module.
+ S : string
+ The signature that needs to be validated.
+
+ :Return: True if verification is correct. False otherwise.
+ """
+ # TODO: Verify the key is RSA
+
+ # Set defaults for salt length and mask generation function
+ if self._saltLen == None:
+ sLen = mhash.digest_size
+ else:
+ sLen = self._saltLen
+ if self._mgfunc:
+ mgf = self._mgfunc
+ else:
+ mgf = lambda x,y: MGF1(x,y,mhash)
+
+ modBits = Crypto.Util.number.size(self._key.n)
+
+ # See 8.1.2 in RFC3447
+ k = ceil_div(modBits,8) # Convert from bits to bytes
+ # Step 1
+ if len(S) != k:
+ return False
+ # Step 2a (O2SIP), 2b (RSAVP1), and partially 2c (I2OSP)
+ # Note that signature must be smaller than the module
+ # but RSA.py won't complain about it.
+ # TODO: Fix RSA object; don't do it here.
+ em = self._key.encrypt(S, 0)[0]
+ # Step 2c
+ emLen = ceil_div(modBits-1,8)
+ em = bchr(0x00)*(emLen-len(em)) + em
+ # Step 3
+ try:
+ result = EMSA_PSS_VERIFY(mhash, em, modBits-1, mgf, sLen)
+ except ValueError:
+ return False
+ # Step 4
+ return result
+
+def MGF1(mgfSeed, maskLen, hash):
+ """Mask Generation Function, described in B.2.1"""
+ T = b("")
+ for counter in xrange(ceil_div(maskLen, hash.digest_size)):
+ c = long_to_bytes(counter, 4)
+ T = T + hash.new(mgfSeed + c).digest()
+ assert(len(T)>=maskLen)
+ return T[:maskLen]
+
+def EMSA_PSS_ENCODE(mhash, emBits, randFunc, mgf, sLen):
+ """
+ Implement the ``EMSA-PSS-ENCODE`` function, as defined
+ in PKCS#1 v2.1 (RFC3447, 9.1.1).
+
+ The original ``EMSA-PSS-ENCODE`` actually accepts the message ``M`` as input,
+ and hash it internally. Here, we expect that the message has already
+ been hashed instead.
+
+ :Parameters:
+ mhash : hash object
+ The hash object that holds the digest of the message being signed.
+ emBits : int
+ Maximum length of the final encoding, in bits.
+ randFunc : callable
+ An RNG function that accepts as only parameter an int, and returns
+ a string of random bytes, to be used as salt.
+ mgf : 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.
+ sLen : int
+ Length of the salt, in bytes.
+
+ :Return: An ``emLen`` byte long string that encodes the hash
+ (with ``emLen = \ceil(emBits/8)``).
+
+ :Raise ValueError:
+ When digest or salt length are too big.
+ """
+
+ emLen = ceil_div(emBits,8)
+
+ # Bitmask of digits that fill up
+ lmask = 0
+ for i in xrange(8*emLen-emBits):
+ lmask = lmask>>1 | 0x80
+
+ # Step 1 and 2 have been already done
+ # Step 3
+ if emLen < mhash.digest_size+sLen+2:
+ raise ValueError("Digest or salt length are too long for given key size.")
+ # Step 4
+ salt = b("")
+ if randFunc and sLen>0:
+ salt = randFunc(sLen)
+ # Step 5 and 6
+ 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
+ dbMask = mgf(h.digest(), emLen-mhash.digest_size-1)
+ # Step 10
+ maskedDB = strxor(db,dbMask)
+ # Step 11
+ maskedDB = bchr(bord(maskedDB[0]) & ~lmask) + maskedDB[1:]
+ # Step 12
+ em = maskedDB + h.digest() + bchr(0xBC)
+ return em
+
+def EMSA_PSS_VERIFY(mhash, em, emBits, mgf, sLen):
+ """
+ Implement the ``EMSA-PSS-VERIFY`` function, as defined
+ in PKCS#1 v2.1 (RFC3447, 9.1.2).
+
+ ``EMSA-PSS-VERIFY`` actually accepts the message ``M`` as input,
+ and hash it internally. Here, we expect that the message has already
+ been hashed instead.
+
+ :Parameters:
+ mhash : hash object
+ The hash object that holds the digest of the message to be verified.
+ em : string
+ The signature to verify, therefore proving that the sender really signed
+ the message that was received.
+ emBits : int
+ Length of the final encoding (em), in bits.
+ mgf : 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.
+ sLen : int
+ Length of the salt, in bytes.
+
+ :Return: 0 if the encoding is consistent, 1 if it is inconsistent.
+
+ :Raise ValueError:
+ When digest or salt length are too big.
+ """
+
+ emLen = ceil_div(emBits,8)
+
+ # Bitmask of digits that fill up
+ lmask = 0
+ for i in xrange(8*emLen-emBits):
+ lmask = lmask>>1 | 0x80
+
+ # Step 1 and 2 have been already done
+ # Step 3
+ if emLen < mhash.digest_size+sLen+2:
+ return False
+ # Step 4
+ if ord(em[-1:])!=0xBC:
+ return False
+ # Step 5
+ maskedDB = em[:emLen-mhash.digest_size-1]
+ h = em[emLen-mhash.digest_size-1:-1]
+ # Step 6
+ if lmask & bord(em[0]):
+ return False
+ # Step 7
+ dbMask = mgf(h, emLen-mhash.digest_size-1)
+ # Step 8
+ db = strxor(maskedDB, dbMask)
+ # Step 9
+ db = bchr(bord(db[0]) & ~lmask) + db[1:]
+ # Step 10
+ if not db.startswith(bchr(0x00)*(emLen-mhash.digest_size-sLen-2) + bchr(0x01)):
+ return False
+ # Step 11
+ salt = b("")
+ if sLen: salt = db[-sLen:]
+ # Step 12 and 13
+ hp = mhash.new(bchr(0x00)*8 + mhash.digest() + salt).digest()
+ # Step 14
+ if h!=hp:
+ return False
+ return True
+
+def new(key, mgfunc=None, saltLen=None):
+ """Return a signature scheme object `PSS_SigScheme` that
+ can be used to perform PKCS#1 PSS signature or verification.
+
+ :Parameters:
+ key : RSA key object
+ The key to use to sign or verify the message. This is a `Crypto.PublicKey.RSA` object.
+ Signing is only possible if *key* is a private RSA key.
+ 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.
+ saltLen : int
+ Length of the salt, in bytes. If not specified, it matches the output
+ size of the hash function.
+
+ """
+ return PSS_SigScheme(key, mgfunc, saltLen)
+
diff --git a/lib/Crypto/Signature/PKCS1_v1_5.py b/lib/Crypto/Signature/PKCS1_v1_5.py
new file mode 100644
index 0000000..5490687
--- /dev/null
+++ b/lib/Crypto/Signature/PKCS1_v1_5.py
@@ -0,0 +1,236 @@
+# -*- coding: utf-8 -*-
+#
+# Signature/PKCS1-v1_5.py : PKCS#1 v1.5
+#
+# ===================================================================
+# 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.
+# ===================================================================
+
+"""
+RSA digital signature protocol according to PKCS#1 v1.5
+
+See RFC3447__ or the `original RSA Labs specification`__.
+
+This scheme is more properly called ``RSASSA-PKCS1-v1_5``.
+
+For example, a sender may authenticate a message using SHA-1 like
+this:
+
+ >>> from Crypto.Signature import PKCS1_v1_5
+ >>> from Crypto.Hash import SHA
+ >>> from Crypto.PublicKey import RSA
+ >>>
+ >>> message = 'To be signed'
+ >>> key = RSA.importKey(open('privkey.der').read())
+ >>> h = SHA.new(message)
+ >>> signer = PKCS1_v1_5.new(key)
+ >>> signature = signer.sign(h)
+
+At the receiver side, verification can be done using the public part of
+the RSA key:
+
+ >>> key = RSA.importKey(open('pubkey.der').read())
+ >>> h = SHA.new(message)
+ >>> verifier = PKCS1_v1_5.new(key)
+ >>> if verifier.verify(h, signature):
+ >>> print "The signature is authentic."
+ >>> else:
+ >>> print "The signature is not authentic."
+
+:undocumented: __revision__, __package__
+
+.. __: http://www.ietf.org/rfc/rfc3447.txt
+.. __: http://www.rsa.com/rsalabs/node.asp?id=2125
+"""
+
+__revision__ = "$Id$"
+__all__ = [ 'new' ]
+
+import Crypto.Util.number
+from Crypto.Util.number import ceil_div
+from Crypto.Util.asn1 import DerSequence, DerNull, DerOctetString
+from Crypto.Util.py3compat import *
+
+class PKCS115_SigScheme:
+ """This signature scheme can perform PKCS#1 v1.5 RSA signature or verification."""
+
+ def __init__(self, key):
+ """Initialize this PKCS#1 v1.5 signature scheme object.
+
+ :Parameters:
+ key : an RSA key object
+ If a private half is given, both signature and verification are possible.
+ If a public half is given, only verification is possible.
+ """
+ self._key = key
+
+ def can_sign(self):
+ """Return True if this cipher object can be used for signing messages."""
+ return self._key.has_private()
+
+ def sign(self, mhash):
+ """Produce the PKCS#1 v1.5 signature of a message.
+
+ This function is named ``RSASSA-PKCS1-V1_5-SIGN``, and is specified in
+ section 8.2.1 of RFC3447.
+
+ :Parameters:
+ mhash : hash object
+ The hash that was carried out over the message. This is an object
+ belonging to the `Crypto.Hash` module.
+
+ :Return: The signature encoded as a string.
+ :Raise ValueError:
+ If the RSA key length is not sufficiently long to deal with the given
+ hash algorithm.
+ :Raise TypeError:
+ If the RSA key has no private half.
+ """
+ # TODO: Verify the key is RSA
+
+ # See 8.2.1 in RFC3447
+ modBits = Crypto.Util.number.size(self._key.n)
+ k = ceil_div(modBits,8) # Convert from bits to bytes
+
+ # Step 1
+ em = EMSA_PKCS1_V1_5_ENCODE(mhash, k)
+ # Step 2a (OS2IP) and 2b (RSASP1)
+ m = self._key.decrypt(em)
+ # Step 2c (I2OSP)
+ S = bchr(0x00)*(k-len(m)) + m
+ return S
+
+ def verify(self, mhash, S):
+ """Verify that a certain PKCS#1 v1.5 signature is authentic.
+
+ This function checks if the party holding the private half of the key
+ really signed the message.
+
+ This function is named ``RSASSA-PKCS1-V1_5-VERIFY``, and is specified in
+ section 8.2.2 of RFC3447.
+
+ :Parameters:
+ mhash : hash object
+ The hash that was carried out over the message. This is an object
+ belonging to the `Crypto.Hash` module.
+ S : string
+ The signature that needs to be validated.
+
+ :Return: True if verification is correct. False otherwise.
+ """
+ # TODO: Verify the key is RSA
+
+ # See 8.2.2 in RFC3447
+ modBits = Crypto.Util.number.size(self._key.n)
+ k = ceil_div(modBits,8) # Convert from bits to bytes
+
+ # Step 1
+ if len(S) != k:
+ return 0
+ # Step 2a (O2SIP) and 2b (RSAVP1)
+ # Note that signature must be smaller than the module
+ # but RSA.py won't complain about it.
+ # TODO: Fix RSA object; don't do it here.
+ m = self._key.encrypt(S, 0)[0]
+ # Step 2c (I2OSP)
+ em1 = bchr(0x00)*(k-len(m)) + m
+ # Step 3
+ try:
+ em2 = EMSA_PKCS1_V1_5_ENCODE(mhash, k)
+ except ValueError:
+ return 0
+ # Step 4
+ # By comparing the full encodings (as opposed to checking each
+ # 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
+
+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).
+
+ ``EMSA-PKCS1-V1_5-ENCODE`` actually accepts the message ``M`` as input,
+ and hash it internally. Here, we expect that the message has already
+ been hashed instead.
+
+ :Parameters:
+ hash : hash object
+ The hash object that holds the digest of the message being signed.
+ emLen : int
+ The length the final encoding must have, in bytes.
+
+ :attention: the early standard (RFC2313) stated that ``DigestInfo``
+ had to be BER-encoded. This means that old signatures
+ might have length tags in indefinite form, which
+ 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.
+ """
+
+ # First, build the ASN.1 DER object DigestInfo:
+ #
+ # DigestInfo ::= SEQUENCE {
+ # digestAlgorithm AlgorithmIdentifier,
+ # digest OCTET STRING
+ # }
+ #
+ # where digestAlgorithm identifies the hash function and shall be an
+ # algorithm ID with an OID in the set PKCS1-v1-5DigestAlgorithms.
+ #
+ # PKCS1-v1-5DigestAlgorithms ALGORITHM-IDENTIFIER ::= {
+ # { OID id-md2 PARAMETERS NULL }|
+ # { OID id-md5 PARAMETERS NULL }|
+ # { OID id-sha1 PARAMETERS NULL }|
+ # { OID id-sha256 PARAMETERS NULL }|
+ # { OID id-sha384 PARAMETERS NULL }|
+ # { OID id-sha512 PARAMETERS NULL }
+ # }
+ #
+ digestAlgo = DerSequence([hash.oid, DerNull().encode()])
+ digest = DerOctetString(hash.digest())
+ digestInfo = DerSequence([
+ digestAlgo.encode(),
+ digest.encode()
+ ]).encode()
+
+ # We need at least 11 bytes for the remaining data: 3 fixed bytes and
+ # at least 8 bytes of padding).
+ if emLen<len(digestInfo)+11:
+ raise ValueError("Selected hash algorith has a too long digest (%d bytes)." % len(digest))
+ PS = bchr(0xFF) * (emLen - len(digestInfo) - 3)
+ return b("\x00\x01") + PS + bchr(0x00) + digestInfo
+
+def new(key):
+ """Return a signature scheme object `PKCS115_SigScheme` that
+ can be used to perform PKCS#1 v1.5 signature or verification.
+
+ :Parameters:
+ key : RSA key object
+ The key to use to sign or verify the message. This is a `Crypto.PublicKey.RSA` object.
+ Signing is only possible if *key* is a private RSA key.
+
+ """
+ return PKCS115_SigScheme(key)
+
diff --git a/lib/Crypto/Signature/__init__.py b/lib/Crypto/Signature/__init__.py
new file mode 100644
index 0000000..ed523b4
--- /dev/null
+++ b/lib/Crypto/Signature/__init__.py
@@ -0,0 +1,31 @@
+# -*- 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.
+# ===================================================================
+
+"""Digital signature protocols
+
+A collection of standardized protocols to carry out digital signatures.
+
+:undocumented: __revision__, __package__
+"""
+
+__all__ = [ 'PKCS1_v1_5', 'PKCS1_PSS' ]
+__revision__ = "$Id$"
+
+
diff --git a/lib/Crypto/Util/asn1.py b/lib/Crypto/Util/asn1.py
index 1cbd529..dd5ec31 100644
--- a/lib/Crypto/Util/asn1.py
+++ b/lib/Crypto/Util/asn1.py
@@ -24,147 +24,263 @@ from Crypto.Util.number import long_to_bytes, bytes_to_long
import sys
from Crypto.Util.py3compat import *
-__all__ = [ 'DerObject', 'DerInteger', 'DerSequence' ]
+__all__ = [ 'DerObject', 'DerInteger', 'DerOctetString', 'DerNull', 'DerSequence', 'DerObjectId' ]
class DerObject:
- typeTags = { 'SEQUENCE':b('\x30'), 'BIT STRING':b('\x03'), 'INTEGER':b('\x02') }
-
- def __init__(self, ASN1Type=None):
- self.typeTag = self.typeTags.get(ASN1Type, ASN1Type)
- self.payload = b('')
-
- def _lengthOctets(self, payloadLen):
- '''
- Return an octet string that is suitable for the BER/DER
- length element if the relevant payload is of the given
- size (in bytes).
- '''
- if payloadLen>127:
- encoding = long_to_bytes(payloadLen)
- return bchr(len(encoding)+128) + encoding
- return bchr(payloadLen)
-
- def encode(self):
- return self.typeTag + self._lengthOctets(len(self.payload)) + self.payload
-
- def _decodeLen(self, idx, str):
- '''
- Given a string and an index to a DER LV,
- this function returns a tuple with the length of V
- and an index to the first byte of it.
- '''
- length = bord(str[idx])
- if length<=127:
- return (length,idx+1)
- else:
- payloadLength = bytes_to_long(str[idx+1:idx+1+(length & 0x7F)])
- if payloadLength<=127:
- raise ValueError("Not a DER length tag.")
- return (payloadLength, idx+1+(length & 0x7F))
-
- def decode(self, input, noLeftOvers=0):
- try:
- self.typeTag = input[0]
- if (bord(self.typeTag) & 0x1F)==0x1F:
- raise ValueError("Unsupported DER tag")
- (length,idx) = self._decodeLen(1,input)
- if noLeftOvers and len(input) != (idx+length):
- raise ValueError("Not a DER structure")
- self.payload = input[idx:idx+length]
- except IndexError:
- raise ValueError("Not a valid DER SEQUENCE.")
- return idx+length
+ """Base class for defining a single DER object.
+
+ Instantiate this class ONLY when you have to decode a DER element.
+ """
+
+ # Known TAG types
+ typeTags = { 'SEQUENCE': 0x30, 'BIT STRING': 0x03, 'INTEGER': 0x02,
+ 'OCTET STRING': 0x04, 'NULL': 0x05, 'OBJECT IDENTIFIER': 0x06 }
+
+ def __init__(self, ASN1Type=None, payload=b('')):
+ """Initialize the DER object according to a specific type.
+
+ The ASN.1 type is either specified as the ASN.1 string (e.g.
+ 'SEQUENCE'), directly with its numerical tag or with no tag
+ at all (None)."""
+ if isInt(ASN1Type) or ASN1Type is None:
+ self.typeTag = ASN1Type
+ else:
+ if len(ASN1Type)==1:
+ self.typeTag = ord(ASN1Type)
+ else:
+ self.typeTag = self.typeTags.get(ASN1Type)
+ self.payload = payload
+
+ def isType(self, ASN1Type):
+ return self.typeTags[ASN1Type]==self.typeTag
+
+ def _lengthOctets(self, payloadLen):
+ """Return a byte string that encodes the given payload length (in
+ bytes) in a format suitable for a DER length tag (L).
+ """
+ if payloadLen>127:
+ encoding = long_to_bytes(payloadLen)
+ return bchr(len(encoding)+128) + encoding
+ return bchr(payloadLen)
+
+ def encode(self):
+ """Return a complete DER element, fully encoded as a TLV."""
+ return bchr(self.typeTag) + self._lengthOctets(len(self.payload)) + self.payload
+
+ 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).
+
+ 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,idx+1)
+ payloadLength = bytes_to_long(der[idx+1:idx+1+(length & 0x7F)])
+ if payloadLength<=127:
+ raise ValueError("Not a DER length tag.")
+ return (payloadLength, idx+1+(length & 0x7F))
+
+ def decode(self, derEle, noLeftOvers=0):
+ """Decode a complete DER element, and re-initializes this
+ object with it.
+
+ @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.
+
+ Raises a ValueError exception in case of parsing errors.
+ Raises an IndexError exception if the DER element is too short.
+ """
+ try:
+ 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):
- def __init__(self, value = 0):
- DerObject.__init__(self, 'INTEGER')
- self.value = value
-
- def encode(self):
- self.payload = long_to_bytes(self.value)
- if bord(self.payload[0])>127:
- self.payload = b('\x00') + self.payload
- return DerObject.encode(self)
-
- def decode(self, input, noLeftOvers=0):
- tlvLength = DerObject.decode(self, input,noLeftOvers)
- if bord(self.payload[0])>127:
- raise ValueError ("Negative INTEGER.")
- self.value = bytes_to_long(self.payload)
- return tlvLength
-
+ def __init__(self, value = 0):
+ """Class to model an INTEGER DER element.
+
+ Limitation: only non-negative values are supported.
+ """
+ DerObject.__init__(self, 'INTEGER')
+ self.value = value
+
+ def encode(self):
+ """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
+ return DerObject.encode(self)
+
+ def decode(self, derEle, noLeftOvers=0):
+ """Decode a complete INTEGER DER element, and re-initializes this
+ object with it.
+
+ @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.
+ """
+ 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):
- def __init__(self):
- DerObject.__init__(self, 'SEQUENCE')
- self._seq = []
- def __delitem__(self, n):
- del self._seq[n]
- def __getitem__(self, n):
- return self._seq[n]
- def __setitem__(self, key, value):
- self._seq[key] = value
- if sys.version_info[0] == 2:
+ """Class to model a SEQUENCE DER element.
+
+ 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).
+ """
+
+ def __init__(self, startSeq=None):
+ """Initialize the SEQUENCE DER object. Always empty
+ initially."""
+ DerObject.__init__(self, 'SEQUENCE')
+ if startSeq==None:
+ self._seq = []
+ else:
+ self._seq = startSeq
+
+ ## A few methods to make it behave like a python sequence
+
+ def __delitem__(self, n):
+ del self._seq[n]
+ def __getitem__(self, n):
+ return self._seq[n]
+ def __setitem__(self, key, value):
+ self._seq[key] = value
def __setslice__(self,i,j,sequence):
- self._seq[i:j] = sequence
+ self._seq[i:j] = sequence
def __delslice__(self,i,j):
- del self._seq[i:j]
+ del self._seq[i:j]
def __getslice__(self, i, j):
- return self._seq[max(0, i):max(0, j)]
- def __len__(self):
- return len(self._seq)
- def append(self, item):
- return self._seq.append(item)
-
- def hasOnlyInts(self):
- if not self._seq: return 0
- test = 0
- for item in self._seq:
- try:
- test += item
- except TypeError:
- return 0
- return 1
-
- def encode(self):
- '''
- Return the DER encoding for the ASN.1 SEQUENCE containing
- the non-negative integers and longs added to this object.
- '''
- self.payload = b('')
- for item in self._seq:
- try:
- self.payload += item
- except:
+ return self._seq[max(0, i):max(0, j)]
+ def __len__(self):
+ return len(self._seq)
+ def append(self, item):
+ return self._seq.append(item)
+
+ def hasInts(self):
+ """Return the number of items in this sequence that are numbers."""
+ return len(filter(isInt, 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 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:
+ try:
+ self.payload += DerInteger(item).encode()
+ except:
+ raise ValueError("Trying to DER encode an unknown object")
+ return DerObject.encode(self)
+
+ def decode(self, derEle, noLeftOvers=0):
+ """Decode a complete SEQUENCE DER element, and re-initializes this
+ object with it.
+
+ @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.
+ """
+
+ self._seq = []
try:
- self.payload += DerInteger(item).encode()
- except:
- raise ValueError("Trying to DER encode an unknown object")
- return DerObject.encode(self)
-
- def decode(self, input,noLeftOvers=0):
- '''
- This function decodes the given string into a sequence of
- ASN.1 objects. Yet, we only know about unsigned INTEGERs.
- Any other type is stored as its rough TLV. In the latter
- case, the correctectness of the TLV is not checked.
- '''
- self._seq = []
- try:
- tlvLength = DerObject.decode(self, input,noLeftOvers)
- if self.typeTag!=self.typeTags['SEQUENCE'][0]:
- raise ValueError("Not a DER SEQUENCE.")
- # Scan one TLV at once
- idx = 0
- while idx<len(self.payload):
- typeTag = self.payload[idx]
- if typeTag==self.typeTags['INTEGER'][0]:
- newInteger = DerInteger()
- idx += newInteger.decode(self.payload[idx:])
- self._seq.append(newInteger.value)
- else:
- itemLen,itemIdx = self._decodeLen(idx+1,self.payload)
- self._seq.append(self.payload[idx:itemIdx+itemLen])
- idx = itemIdx + itemLen
- except IndexError:
- raise ValueError("Not a valid DER SEQUENCE.")
- return tlvLength
+ 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<len(self.payload):
+ typeTag = bord(self.payload[idx])
+ if typeTag==self.typeTags['INTEGER']:
+ newInteger = DerInteger()
+ idx += newInteger.decode(self.payload[idx:])
+ self._seq.append(newInteger.value)
+ else:
+ itemLen,itemIdx = self._decodeLen(idx+1,self.payload)
+ self._seq.append(self.payload[idx:itemIdx+itemLen])
+ idx = itemIdx + itemLen
+ except IndexError:
+ raise ValueError("Not a valid DER SEQUENCE.")
+ return tlvLength
+
+class DerOctetString(DerObject):
+ def __init__(self, value = b('')):
+ DerObject.__init__(self, 'OCTET STRING')
+ self.payload = value
+
+ 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):
+ def __init__(self):
+ DerObject.__init__(self, 'NULL')
+
+class DerObjectId(DerObject):
+ def __init__(self):
+ DerObject.__init__(self, 'OBJECT IDENTIFIER')
+
+ 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
+
+def isInt(x):
+ test = 0
+ try:
+ test += x
+ except TypeError:
+ return 0
+ return 1
+
diff --git a/lib/Crypto/Util/py3compat.py b/lib/Crypto/Util/py3compat.py
index 7c90de2..34e5224 100644
--- a/lib/Crypto/Util/py3compat.py
+++ b/lib/Crypto/Util/py3compat.py
@@ -23,6 +23,39 @@
# ===================================================================
"""Compatibility code for handling string/bytes changes from Python 2.x to Py3k
+
+In Python 2.x, strings (of type ''str'') contain binary data, including encoded
+Unicode text (e.g. UTF-8). The separate type ''unicode'' holds Unicode text.
+Unicode literals are specified via the u'...' prefix. Indexing or slicing
+either type always produces a string of the same type as the original.
+Data read from a file is always of '''str'' type.
+
+In Python 3.x, strings (type ''str'') may only contain Unicode text. The u'...'
+prefix and the ''unicode'' type are now redundant. A new type (called
+''bytes'') has to be used for binary data (including any particular
+''encoding'' of a string). The b'...' prefix allows one to specify a binary
+literal. Indexing or slicing a string produces another string. Slicing a byte
+string produces another byte string, but the indexing operation produces an
+integer. Data read from a file is of '''str'' type if the file was opened in
+text mode, or of ''bytes'' type otherwise.
+
+Since PyCrypto aims at supporting both Python 2.x and 3.x, the following helper
+functions are used to keep the rest of the library as independent as possible
+from the actual Python version.
+
+In general, the code should always deal with binary strings, and use integers
+instead of 1-byte character strings.
+
+b(s)
+ Take a text string literal (with no prefix or with u'...' prefix) and
+ make a byte string.
+bchr(c)
+ Take an integer and make a 1-character byte string.
+bord(c)
+ Take the result of indexing on a byte string and make an integer.
+tobytes(s)
+ Take a text string, a byte string, or a sequence of character taken from
+ a byte string and make a byte string.
"""
__revision__ = "$Id$"
@@ -38,6 +71,18 @@ if sys.version_info[0] == 2:
return str(s)
def bord(s):
return ord(s)
+ if sys.version_info[1] == 1:
+ def tobytes(s):
+ try:
+ return s.encode('latin-1')
+ except:
+ return ''.join(s)
+ else:
+ def tobytes(s):
+ if isinstance(s, unicode):
+ return s.encode("latin-1")
+ else:
+ return ''.join(s)
else:
def b(s):
return s.encode("latin-1") # utf-8 would cause some side-effects we don't want
@@ -50,5 +95,13 @@ else:
return bytes(s)
def bord(s):
return s
+ def tobytes(s):
+ if isinstance(s,bytes):
+ return s
+ else:
+ if isinstance(s,str):
+ return s.encode("latin-1")
+ else:
+ return bytes(s)
# vim:set ts=4 sw=4 sts=4 expandtab:
diff --git a/lib/Crypto/Util/wrapper.py b/lib/Crypto/Util/wrapper.py
new file mode 100644
index 0000000..1090fc7
--- /dev/null
+++ b/lib/Crypto/Util/wrapper.py
@@ -0,0 +1,47 @@
+#
+# wrapper.py: Small class to wrap an object, instantiated from a class
+# or generated by a module.
+#
+# ===================================================================
+# 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.
+# ===================================================================
+#
+
+__all__ = [ 'Wrapper' ]
+
+class Wrapper:
+ '''
+ Wrapper for an object, instantiated from a class
+ or from a call to a new() function in a module.
+ '''
+ def __init__(self, wrapped, *args):
+ """
+ wrapped is either a class or a module with a new() function.
+ """
+ if hasattr(wrapped, 'new'):
+ self._wrapped = wrapped.new(*args)
+ else:
+ self._wrapped = wrapped(*args)
+
+ def __getattr__(self, name):
+ try:
+ return getattr(getattr(self,'_wrapped'),name)
+ except AttributeError:
+ if hasattr(self, name):
+ return getattr(self,name)
+ raise
+
diff --git a/lib/Crypto/__init__.py b/lib/Crypto/__init__.py
index 19c645b..4d0ef7c 100644
--- a/lib/Crypto/__init__.py
+++ b/lib/Crypto/__init__.py
@@ -24,19 +24,24 @@ A collection of cryptographic modules implementing various algorithms
and protocols.
Subpackages:
-Crypto.Cipher Secret-key encryption algorithms (AES, DES, ARC4)
-Crypto.Hash Hashing algorithms (MD5, SHA, HMAC)
-Crypto.Protocol Cryptographic protocols (Chaffing, all-or-nothing
- transform). This package does not contain any
- network protocols.
-Crypto.PublicKey Public-key encryption and signature algorithms
- (RSA, DSA)
-Crypto.Util Various useful modules and functions (long-to-string
- conversion, random number generation, number
- theoretic functions)
+
+Crypto.Cipher
+ Secret-key (AES, DES, ARC4) and public-key encryption (RSA PKCS#1) algorithms
+Crypto.Hash
+ Hashing algorithms (MD5, SHA, HMAC)
+Crypto.Protocol
+ Cryptographic protocols (Chaffing, all-or-nothing transform, key derivation
+ functions). This package does not contain any network protocols.
+Crypto.PublicKey
+ Public-key encryption and signature algorithms (RSA, DSA)
+Crypto.Signature
+ 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']
+__all__ = ['Cipher', 'Hash', 'Protocol', 'PublicKey', 'Util', 'Signature']
__version__ = '2.4.1' # See also below and setup.py
__revision__ = "$Id$"
diff --git a/setup.py b/setup.py
index 36adf67..b6d0bf7 100644
--- a/setup.py
+++ b/setup.py
@@ -41,7 +41,6 @@ from distutils.ccompiler import new_compiler
from distutils.core import Extension, Command
from distutils.command.build import build
from distutils.command.build_ext import build_ext
-
import os, sys, re
import struct
@@ -73,6 +72,7 @@ try:
except ImportError:
# Python 2
from distutils.command.build_py import build_py
+
# List of pure Python modules that will be excluded from the binary packages.
# The list consists of (package, module_name) tuples
if sys.version_info[0] == 2:
@@ -85,14 +85,6 @@ else:
if sys.platform != "win32": # Avoid nt.py, as 2to3 can't fix it w/o winrandom
EXCLUDE_PY += [('Crypto.Random.OSRNG','nt')]
-# Exclude SHA224/384/512 if they're not present (Python < 2.5)
-try: from hashlib import sha224
-except ImportError: EXCLUDE_PY += [('Crypto.Hash', 'SHA224')]
-try: from hashlib import sha384
-except ImportError: EXCLUDE_PY += [('Crypto.Hash', 'SHA384')]
-try: from hashlib import sha512
-except ImportError: EXCLUDE_PY += [('Crypto.Hash', 'SHA512')]
-
# Work around the print / print() issue with Python 2.x and 3.x. We only need
# to print at one point of the code, which makes this easy
@@ -107,43 +99,6 @@ def PrintErr(*args, **kwd):
w(str(a))
w(kwd.get("end", "\n"))
-# Functions for finding libraries and files, copied from Python's setup.py.
-
-def find_file(filename, std_dirs, paths):
- """Searches for the directory where a given file is located,
- and returns a possibly-empty list of additional directories, or None
- if the file couldn't be found at all.
-
- 'filename' is the name of a file, such as readline.h or libcrypto.a.
- 'std_dirs' is the list of standard system directories; if the
- file is found in one of them, no additional directives are needed.
- 'paths' is a list of additional locations to check; if the file is
- found in one of them, the resulting list will contain the directory.
- """
-
- # Check the standard locations
- for dir in std_dirs:
- f = os.path.join(dir, filename)
- if os.path.exists(f): return []
-
- # Check the additional directories
- for dir in paths:
- f = os.path.join(dir, filename)
- if os.path.exists(f):
- return [dir]
-
- # Not found anywhere
- return None
-
-def find_library_file(compiler, libname, std_dirs, paths):
- filename = compiler.library_filename(libname, lib_type='shared')
- result = find_file(filename, std_dirs, paths)
- if result is not None: return result
-
- filename = compiler.library_filename(libname, lib_type='static')
- result = find_file(filename, std_dirs, paths)
- return result
-
def endianness_macro():
s = struct.pack("@I", 0x33221100)
if s == "\x00\x11\x22\x33".encode(): # little endian
@@ -403,7 +358,10 @@ kw = {'name':"pycrypto",
"Crypto.SelfTest.Random.Fortuna",
"Crypto.SelfTest.Random.OSRNG",
"Crypto.SelfTest.Util",
- "Crypto.Protocol", "Crypto.PublicKey"],
+ "Crypto.SelfTest.Signature",
+ "Crypto.Protocol",
+ "Crypto.PublicKey",
+ "Crypto.Signature"],
'package_dir' : { "Crypto": "lib/Crypto" },
'ext_modules': plat_ext + [
# _fastmath (uses GNU mp library)
@@ -413,16 +371,25 @@ kw = {'name':"pycrypto",
sources=["src/_fastmath.c"]),
# Hash functions
- Extension("Crypto.Hash.MD2",
+ Extension("Crypto.Hash._MD2",
include_dirs=['src/'],
sources=["src/MD2.c"]),
- Extension("Crypto.Hash.MD4",
+ Extension("Crypto.Hash._MD4",
include_dirs=['src/'],
sources=["src/MD4.c"]),
- Extension("Crypto.Hash.SHA256",
+ Extension("Crypto.Hash._SHA256",
include_dirs=['src/'],
sources=["src/SHA256.c"]),
- Extension("Crypto.Hash.RIPEMD160",
+ Extension("Crypto.Hash._SHA224",
+ include_dirs=['src/'],
+ sources=["src/SHA224.c"]),
+ Extension("Crypto.Hash._SHA384",
+ include_dirs=['src/'],
+ sources=["src/SHA384.c"]),
+ Extension("Crypto.Hash._SHA512",
+ include_dirs=['src/'],
+ sources=["src/SHA512.c"]),
+ Extension("Crypto.Hash._RIPEMD160",
include_dirs=['src/'],
sources=["src/RIPEMD160.c"],
define_macros=[endianness_macro()]),
diff --git a/src/MD2.c b/src/MD2.c
index 88c3cd8..3054fb2 100644
--- a/src/MD2.c
+++ b/src/MD2.c
@@ -31,10 +31,21 @@
#include "Python.h"
#include "pycrypto_compat.h"
-#define MODULE_NAME MD2
+#define MODULE_NAME _MD2
#define DIGEST_SIZE 16
#define BLOCK_SIZE 64
+/**
+ * id-md2 OBJECT IDENTIFIER ::= {
+ * iso(1) member-body(2) us(840) rsadsi(113549)
+ * digestAlgorithm(2) 2
+ * }
+ */
+static const char md2_oid[] = { 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x02 };
+
+#define DER_OID ((void*)&md2_oid)
+#define DER_OID_SIZE (sizeof md2_oid)
+
typedef unsigned char U8;
typedef unsigned int U32;
diff --git a/src/MD4.c b/src/MD4.c
index 12d2740..2c0a984 100644
--- a/src/MD4.c
+++ b/src/MD4.c
@@ -31,7 +31,7 @@
#include "Python.h"
#include "pycrypto_compat.h"
-#define MODULE_NAME MD4
+#define MODULE_NAME _MD4
#define DIGEST_SIZE 16
#define BLOCK_SIZE 64
diff --git a/src/RIPEMD160.c b/src/RIPEMD160.c
index 692cb3e..9786af8 100644
--- a/src/RIPEMD160.c
+++ b/src/RIPEMD160.c
@@ -400,7 +400,7 @@ static int ripemd160_digest(const ripemd160_state *self, unsigned char *out)
}
/* Template definitions */
-#define MODULE_NAME RIPEMD160
+#define MODULE_NAME _RIPEMD160
#define DIGEST_SIZE RIPEMD160_DIGEST_SIZE
#define hash_state ripemd160_state
#define hash_init ripemd160_init
diff --git a/src/SHA224.c b/src/SHA224.c
new file mode 100644
index 0000000..ca70fbd
--- /dev/null
+++ b/src/SHA224.c
@@ -0,0 +1,74 @@
+/*
+ * An implementation of the SHA-224 hash function.
+ *
+ * The Federal Information Processing Standards (FIPS) Specification
+ * can be found here (FIPS 180-3):
+ * http://csrc.nist.gov/publications/PubsFIPS.html
+ *
+ * Written in 2010 by Lorenz Quack <don@amberfisharts.com>
+ *
+ * ===================================================================
+ * 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.
+ * ===================================================================
+ *
+ */
+
+#define MODULE_NAME _SHA224
+#define DIGEST_SIZE (224/8)
+#define BLOCK_SIZE (512/8)
+#define WORD_SIZE 4
+#define SCHEDULE_SIZE 64
+
+#include "hash_SHA2.h"
+
+/* Initial Values H */
+static const sha2_word_t H[8] = {
+ 0xc1059ed8,
+ 0x367cd507,
+ 0x3070dd17,
+ 0xf70e5939,
+ 0xffc00b31,
+ 0x68581511,
+ 0x64f98fa7,
+ 0xbefa4fa4
+};
+
+/* the Constants K */
+static const sha2_word_t K[SCHEDULE_SIZE] = {
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b,
+ 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01,
+ 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7,
+ 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
+ 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152,
+ 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
+ 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc,
+ 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819,
+ 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08,
+ 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f,
+ 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
+ 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
+};
+
+/* SHA-224 specific functions */
+#define Sigma0(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22))
+#define Sigma1(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25))
+#define Gamma0(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SHR(x, 3))
+#define Gamma1(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHR(x, 10))
+
+#include "hash_SHA2_template.c"
+
diff --git a/src/SHA256.c b/src/SHA256.c
index 70f0211..61a2d74 100644
--- a/src/SHA256.c
+++ b/src/SHA256.c
@@ -1,16 +1,11 @@
/*
- * An implementation of the SHA-256 hash function, this is endian neutral
- * so should work just about anywhere.
+ * An implementation of the SHA-256 hash function.
*
- * This code works much like the MD5 code provided by RSA. You sha_init()
- * a "sha_state" then sha_process() the bytes you want and sha_done() to get
- * the output.
- *
- * Revised Code: Complies to SHA-256 standard now.
- *
- * Originally written by Tom St Denis -- http://tomstdenis.home.dhs.org
- *
- * Adapted for PyCrypto by Jeethu Rao, Taylor Boon, and others.
+ * The Federal Information Processing Standards (FIPS) Specification
+ * can be found here (FIPS 180-3):
+ * http://csrc.nist.gov/publications/PubsFIPS.html
+ *
+ * Written in 2010 by Lorenz Quack <don@amberfisharts.com>
*
* ===================================================================
* The contents of this file are dedicated to the public domain. To
@@ -31,202 +26,48 @@
* ===================================================================
*
*/
-#include "Python.h"
-#include "pycrypto_compat.h"
-#define MODULE_NAME SHA256
-#define DIGEST_SIZE 32
-#define BLOCK_SIZE 64
-
-typedef unsigned char U8;
-#ifdef __alpha__
-typedef unsigned int U32;
-#elif defined(__amd64__)
-#include <inttypes.h>
-typedef uint32_t U32;
-#else
-typedef unsigned int U32;
-#endif
-
-typedef struct {
- U32 state[8], curlen;
- U32 length_upper, length_lower;
- unsigned char buf[64];
-}
-hash_state;
-
-/* the K array */
-static const U32 K[64] = {
- 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, 0x3956c25bUL,
- 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, 0xd807aa98UL, 0x12835b01UL,
- 0x243185beUL, 0x550c7dc3UL, 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL,
- 0xc19bf174UL, 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL,
- 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, 0x983e5152UL,
- 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, 0xc6e00bf3UL, 0xd5a79147UL,
- 0x06ca6351UL, 0x14292967UL, 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL,
- 0x53380d13UL, 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL,
- 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, 0xd192e819UL,
- 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, 0x19a4c116UL, 0x1e376c08UL,
- 0x2748774cUL, 0x34b0bcb5UL, 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL,
- 0x682e6ff3UL, 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL,
- 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL
+#define MODULE_NAME _SHA256
+#define DIGEST_SIZE (256/8)
+#define BLOCK_SIZE (512/8)
+#define WORD_SIZE 4
+#define SCHEDULE_SIZE 64
+
+#include "hash_SHA2.h"
+
+/* Initial Values H */
+static const sha2_word_t H[8] = {
+ 0x6a09e667,
+ 0xbb67ae85,
+ 0x3c6ef372,
+ 0xa54ff53a,
+ 0x510e527f,
+ 0x9b05688c,
+ 0x1f83d9ab,
+ 0x5be0cd19
};
-/* Various logical functions */
-#define Ch(x,y,z) ((x & y) ^ (~x & z))
-#define Maj(x,y,z) ((x & y) ^ (x & z) ^ (y & z))
-#define S(x, n) (((x)>>((n)&31))|((x)<<(32-((n)&31))))
-#define R(x, n) ((x)>>(n))
-#define Sigma0(x) (S(x, 2) ^ S(x, 13) ^ S(x, 22))
-#define Sigma1(x) (S(x, 6) ^ S(x, 11) ^ S(x, 25))
-#define Gamma0(x) (S(x, 7) ^ S(x, 18) ^ R(x, 3))
-#define Gamma1(x) (S(x, 17) ^ S(x, 19) ^ R(x, 10))
-
-/* compress 512-bits */
-static void sha_compress(hash_state * md)
-{
- U32 S[8], W[64], t0, t1;
- int i;
-
- /* copy state into S */
- for (i = 0; i < 8; i++)
- S[i] = md->state[i];
-
- /* copy the state into 512-bits into W[0..15] */
- for (i = 0; i < 16; i++)
- W[i] = (((U32) md->buf[(4 * i) + 0]) << 24) |
- (((U32) md->buf[(4 * i) + 1]) << 16) |
- (((U32) md->buf[(4 * i) + 2]) << 8) |
- (((U32) md->buf[(4 * i) + 3]));
-
- /* fill W[16..63] */
- for (i = 16; i < 64; i++)
- W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16];
-
- /* Compress */
- for (i = 0; i < 64; i++) {
- t0 = S[7] + Sigma1(S[4]) + Ch(S[4], S[5], S[6]) + K[i] + W[i];
- t1 = Sigma0(S[0]) + Maj(S[0], S[1], S[2]);
- S[7] = S[6];
- S[6] = S[5];
- S[5] = S[4];
- S[4] = S[3] + t0;
- S[3] = S[2];
- S[2] = S[1];
- S[1] = S[0];
- S[0] = t0 + t1;
- }
-
- /* feedback */
- for (i = 0; i < 8; i++)
- md->state[i] += S[i];
-}
-
-/* init the SHA state */
-static void sha_init(hash_state * md)
-{
- md->curlen = md->length_upper = md->length_lower = 0;
- md->state[0] = 0x6A09E667UL;
- md->state[1] = 0xBB67AE85UL;
- md->state[2] = 0x3C6EF372UL;
- md->state[3] = 0xA54FF53AUL;
- md->state[4] = 0x510E527FUL;
- md->state[5] = 0x9B05688CUL;
- md->state[6] = 0x1F83D9ABUL;
- md->state[7] = 0x5BE0CD19UL;
-}
-
-static void sha_process(hash_state * md, unsigned char *buf, int len)
-{
- while (len--) {
- /* copy byte */
- md->buf[md->curlen++] = *buf++;
-
- /* is 64 bytes full? */
- if (md->curlen == 64) {
- U32 orig_length;
- sha_compress(md);
- orig_length = md->length_lower;
- md->length_lower += 512;
- if (orig_length > md->length_lower) {
- md->length_upper++;
- }
- md->curlen = 0;
- }
- }
-}
-
-static void sha_done(hash_state * md, unsigned char *hash)
-{
- int i;
- U32 orig_length;
-
- /* increase the length of the message */
- orig_length = md->length_lower;
- md->length_lower += md->curlen * 8;
- if (orig_length > md->length_lower) {
- md->length_upper++;
- }
-
- /* append the '1' bit */
- md->buf[md->curlen++] = 0x80;
-
- /* if the length is currently above 56 bytes we append zeros
- * then compress. Then we can fall back to padding zeros and length
- * encoding like normal.
- */
- if (md->curlen > 56) {
- for (; md->curlen < 64;)
- md->buf[md->curlen++] = 0;
- sha_compress(md);
- md->curlen = 0;
- }
-
- /* pad upto 56 bytes of zeroes */
- for (; md->curlen < 56;)
- md->buf[md->curlen++] = 0;
-
- /* append length */
- for (i = 56; i < 60; i++)
- md->buf[i] = (md->length_upper >> ((59 - i) * 8)) & 255;
- for (i = 60; i < 64; i++)
- md->buf[i] = (md->length_lower >> ((63 - i) * 8)) & 255;
- sha_compress(md);
-
- /* copy output */
- for (i = 0; i < 32; i++)
- hash[i] = (md->state[i >> 2] >> (((3 - i) & 3) << 3)) & 255;
-}
-
-// Done
-static void hash_init (hash_state *ptr)
-{
- sha_init(ptr);
-}
-
-// Done
-static void
-hash_update (hash_state *self, const U8 *buf, U32 len)
-{
- sha_process(self,(unsigned char *)buf,len);
-}
-
-// Done
-static void
-hash_copy(hash_state *src, hash_state *dest)
-{
- memcpy(dest,src,sizeof(hash_state));
-}
+/* the Constants K */
+static const sha2_word_t K[SCHEDULE_SIZE] = {
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b,
+ 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01,
+ 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7,
+ 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
+ 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152,
+ 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
+ 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc,
+ 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819,
+ 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08,
+ 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f,
+ 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
+ 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
+};
-// Done
-static PyObject *
-hash_digest (const hash_state *self)
-{
- unsigned char digest[32];
- hash_state temp;
+/* SHA-256 specific functions */
+#define Sigma0(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22))
+#define Sigma1(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25))
+#define Gamma0(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SHR(x, 3))
+#define Gamma1(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHR(x, 10))
- hash_copy((hash_state*)self,&temp);
- sha_done(&temp,digest);
- return PyBytes_FromStringAndSize((char *)digest, 32);
-}
+#include "hash_SHA2_template.c"
-#include "hash_template.c"
diff --git a/src/SHA384.c b/src/SHA384.c
new file mode 100644
index 0000000..05dfe25
--- /dev/null
+++ b/src/SHA384.c
@@ -0,0 +1,80 @@
+/*
+ * An implementation of the SHA-384 hash function.
+ *
+ * The Federal Information Processing Standards (FIPS) Specification
+ * can be found here (FIPS 180-3):
+ * http://csrc.nist.gov/publications/PubsFIPS.html
+ *
+ * Written in 2010 by Lorenz Quack <don@amberfisharts.com>
+ *
+ * ===================================================================
+ * 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.
+ * ===================================================================
+ *
+ */
+
+#define MODULE_NAME _SHA384
+#define DIGEST_SIZE (384/8)
+#define BLOCK_SIZE (1024/8)
+#define WORD_SIZE 8
+#define SCHEDULE_SIZE 80
+
+#include "hash_SHA2.h"
+
+/* Initial Values H */
+static const sha2_word_t H[8] = {
+ 0xcbbb9d5dc1059ed8,
+ 0x629a292a367cd507,
+ 0x9159015a3070dd17,
+ 0x152fecd8f70e5939,
+ 0x67332667ffc00b31,
+ 0x8eb44a8768581511,
+ 0xdb0c2e0d64f98fa7,
+ 0x47b5481dbefa4fa4
+};
+
+/* the Constants K */
+static const sha2_word_t K[SCHEDULE_SIZE] = {
+ 0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc,
+ 0x3956c25bf348b538, 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118,
+ 0xd807aa98a3030242, 0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2,
+ 0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235, 0xc19bf174cf692694,
+ 0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65,
+ 0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5,
+ 0x983e5152ee66dfab, 0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4,
+ 0xc6e00bf33da88fc2, 0xd5a79147930aa725, 0x06ca6351e003826f, 0x142929670a0e6e70,
+ 0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed, 0x53380d139d95b3df,
+ 0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b,
+ 0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30,
+ 0xd192e819d6ef5218, 0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8,
+ 0x19a4c116b8d2d0c8, 0x1e376c085141ab53, 0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8,
+ 0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3,
+ 0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec,
+ 0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b,
+ 0xca273eceea26619c, 0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178,
+ 0x06f067aa72176fba, 0x0a637dc5a2c898a6, 0x113f9804bef90dae, 0x1b710b35131c471b,
+ 0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc, 0x431d67c49c100d4c,
+ 0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817
+};
+
+/* SHA-384 specific functions */
+#define Sigma0(x) (ROTR(x, 28) ^ ROTR(x, 34) ^ ROTR(x, 39))
+#define Sigma1(x) (ROTR(x, 14) ^ ROTR(x, 18) ^ ROTR(x, 41))
+#define Gamma0(x) (ROTR(x, 1) ^ ROTR(x, 8) ^ SHR(x, 7))
+#define Gamma1(x) (ROTR(x, 19) ^ ROTR(x, 61) ^ SHR(x, 6))
+
+#include "hash_SHA2_template.c"
diff --git a/src/SHA512.c b/src/SHA512.c
new file mode 100644
index 0000000..3370e8e
--- /dev/null
+++ b/src/SHA512.c
@@ -0,0 +1,80 @@
+/*
+ * An implementation of the SHA-512 hash function.
+ *
+ * The Federal Information Processing Standards (FIPS) Specification
+ * can be found here (FIPS 180-3):
+ * http://csrc.nist.gov/publications/PubsFIPS.html
+ *
+ * Written in 2010 by Lorenz Quack <don@amberfisharts.com>
+ *
+ * ===================================================================
+ * 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.
+ * ===================================================================
+ *
+ */
+
+#define MODULE_NAME _SHA512
+#define DIGEST_SIZE (512/8)
+#define BLOCK_SIZE (1024/8)
+#define WORD_SIZE 8
+#define SCHEDULE_SIZE 80
+
+#include "hash_SHA2.h"
+
+/* Initial Values H */
+static const sha2_word_t H[8] = {
+ 0x6a09e667f3bcc908,
+ 0xbb67ae8584caa73b,
+ 0x3c6ef372fe94f82b,
+ 0xa54ff53a5f1d36f1,
+ 0x510e527fade682d1,
+ 0x9b05688c2b3e6c1f,
+ 0x1f83d9abfb41bd6b,
+ 0x5be0cd19137e2179
+};
+
+/* the Constants K */
+static const sha2_word_t K[SCHEDULE_SIZE] = {
+ 0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc,
+ 0x3956c25bf348b538, 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118,
+ 0xd807aa98a3030242, 0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2,
+ 0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235, 0xc19bf174cf692694,
+ 0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65,
+ 0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5,
+ 0x983e5152ee66dfab, 0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4,
+ 0xc6e00bf33da88fc2, 0xd5a79147930aa725, 0x06ca6351e003826f, 0x142929670a0e6e70,
+ 0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed, 0x53380d139d95b3df,
+ 0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b,
+ 0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30,
+ 0xd192e819d6ef5218, 0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8,
+ 0x19a4c116b8d2d0c8, 0x1e376c085141ab53, 0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8,
+ 0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3,
+ 0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec,
+ 0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b,
+ 0xca273eceea26619c, 0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178,
+ 0x06f067aa72176fba, 0x0a637dc5a2c898a6, 0x113f9804bef90dae, 0x1b710b35131c471b,
+ 0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc, 0x431d67c49c100d4c,
+ 0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817
+};
+
+/* SHA-512 specific functions */
+#define Sigma0(x) (ROTR(x, 28) ^ ROTR(x, 34) ^ ROTR(x, 39))
+#define Sigma1(x) (ROTR(x, 14) ^ ROTR(x, 18) ^ ROTR(x, 41))
+#define Gamma0(x) (ROTR(x, 1) ^ ROTR(x, 8) ^ SHR(x, 7))
+#define Gamma1(x) (ROTR(x, 19) ^ ROTR(x, 61) ^ SHR(x, 6))
+
+#include "hash_SHA2_template.c"
diff --git a/src/_fastmath.c b/src/_fastmath.c
index eff3e29..6af1f05 100644
--- a/src/_fastmath.c
+++ b/src/_fastmath.c
@@ -277,13 +277,13 @@ rsaDecrypt (rsaKey * key, mpz_t v)
mpz_sub_ui(h, key->q, 1);
mpz_fdiv_r(h, key->d, h);
MPZ_POWM(m2, v, h, key->q);
- /* h = u * ( m2 - m1 ) mod q */
+ /* h = u * ( m2 - m1 + q) mod q */
mpz_sub(h, m2, m1);
if (mpz_sgn(h)==-1)
mpz_add(h, h, key->q);
mpz_mul(h, key->u, h);
mpz_mod(h, h, key->q);
- /* m = m2 + h * p */
+ /* m = m1 + h * p */
mpz_mul(h, h, key->p);
mpz_add(v, m1, h);
/* ready */
@@ -633,6 +633,62 @@ dsaKey_has_private (dsaKey * key, PyObject * args)
}
}
+/**
+ * Compute key->p and key->q from the key with private exponent only.
+ * Return 0 if factoring was succesful, 1 otherwise.
+ */
+static int factorize_N_from_D(rsaKey *key)
+{
+ mpz_t ktot, t, a, k, cand, nminus1, cand2;
+ unsigned long cnt;
+ int spotted;
+
+ mpz_init(ktot);
+ mpz_init(t);
+ mpz_init(a);
+ mpz_init(k);
+ mpz_init(cand);
+ mpz_init(nminus1);
+ mpz_init(cand2);
+
+ mpz_sub_ui(nminus1, key->n, 1);
+
+ /** See _slowmath.py **/
+ mpz_mul(ktot, key->e, key->d);
+ mpz_sub_ui(ktot, ktot, 1);
+ mpz_set(t, ktot);
+ cnt = mpz_scan1(t, 0);
+ mpz_fdiv_q_2exp(t,t,cnt);
+ mpz_set_ui(a, 2);
+ for (spotted=0; (!spotted) && (mpz_cmp_ui(a,100)<0); mpz_add_ui(a,a,2)) {
+ mpz_set(k, t);
+ for (; (mpz_cmp(k,ktot)<0); mpz_mul_ui(k,k,2)) {
+ mpz_powm(cand,a,k,key->n);
+ if ((mpz_cmp_ui(cand,1)==0) || (mpz_cmp(cand,nminus1)==0))
+ continue;
+ mpz_powm_ui(cand2,cand,2,key->n);
+ if (mpz_cmp_ui(cand2,1)==0) {
+ mpz_add_ui(cand,cand,1);
+ mpz_gcd(key->p, cand, key->n);
+ spotted=1;
+ break;
+ }
+ }
+ }
+ if (spotted)
+ mpz_divexact(key->q, key->n, key->p);
+
+ mpz_clear(ktot);
+ mpz_clear(t);
+ mpz_clear(a);
+ mpz_clear(k);
+ mpz_clear(cand);
+ mpz_clear(nminus1);
+ mpz_clear(cand2);
+
+ return (spotted?0:1);
+}
+
static PyObject *
rsaKey_new (PyObject * self, PyObject * args)
{
@@ -664,12 +720,19 @@ rsaKey_new (PyObject * self, PyObject * args)
{
longObjToMPZ (key->p, p);
longObjToMPZ (key->q, q);
- if (u) {
- longObjToMPZ (key->u, u);
- } else {
- mpz_invert (key->u, key->p, key->q);
+ } else {
+ if (factorize_N_from_D(key))
+ {
+ PyErr_SetString(PyExc_ValueError,
+ "Unable to compute factors p and q from exponent d.");
+ return NULL;
}
}
+ if (u) {
+ longObjToMPZ (key->u, u);
+ } else {
+ mpz_invert (key->u, key->p, key->q);
+ }
return (PyObject *) key;
}
diff --git a/src/hash_SHA2.h b/src/hash_SHA2.h
new file mode 100644
index 0000000..5867191
--- /dev/null
+++ b/src/hash_SHA2.h
@@ -0,0 +1,104 @@
+/*
+ * An generic header for the SHA-2 hash family.
+ *
+ * Written in 2010 by Lorenz Quack <don@amberfisharts.com>
+ *
+ * ===================================================================
+ * 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.
+ * ===================================================================
+ *
+ */
+
+#ifndef __HASH_SHA2_H
+#define __HASH_SHA2_H
+
+/* check if implementation set the correct macros */
+#ifndef MODULE_NAME
+#error SHA2 Implementation must define MODULE_NAME before including this header
+#endif
+
+#ifndef DIGEST_SIZE
+#error SHA2 Implementation must define DIGEST_SIZE before including this header
+#else
+#define DIGEST_SIZE_BITS (DIGEST_SIZE*8)
+#endif
+
+#ifndef BLOCK_SIZE
+#error SHA2 Implementation must define BLOCK_SIZE before including this header
+#else
+#define BLOCK_SIZE_BITS (BLOCK_SIZE*8)
+#endif
+
+#ifndef WORD_SIZE
+#error SHA2 Implementation must define WORD_SIZE before including this header
+#else
+#if ((WORD_SIZE != 4) && (WORD_SIZE != 8))
+#error WORD_SIZE must be either 4 or 8
+#else
+#define WORD_SIZE_BITS (WORD_SIZE*8)
+#endif
+#endif
+
+#ifndef SCHEDULE_SIZE
+#error SHA2 Implementation must define SCHEDULE_SIZE before including this header
+#endif
+
+/* define some helper macros */
+#define PADDING_SIZE (2 * WORD_SIZE)
+#define LAST_BLOCK_SIZE (BLOCK_SIZE - PADDING_SIZE)
+
+/* define generic SHA-2 family functions */
+#define Ch(x,y,z) ((x & y) ^ (~x & z))
+#define Maj(x,y,z) ((x & y) ^ (x & z) ^ (y & z))
+#define ROTR(x, n) (((x)>>((n)&(WORD_SIZE_BITS-1)))|((x)<<(WORD_SIZE_BITS-((n)&(WORD_SIZE_BITS-1)))))
+#define SHR(x, n) ((x)>>(n))
+
+/* determine fixed size types */
+#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
+#include <stdint.h>
+typedef uint8_t U8;
+typedef uint32_t U32;
+typedef uint64_t U64;
+#elif defined(_MSC_VER)
+typedef unsigned char U8;
+typedef unsigned __int64 U64;
+typedef unsigned int U32;
+#elif defined(__sun) || defined(__sun__)
+#include <sys/inttypes.h>
+typedef uint8_t U8;
+typedef uint32_t U32;
+typedef uint64_t U64;
+#endif
+
+/* typedef a sha2_word_t type of appropriate size */
+#if (WORD_SIZE_BITS == 64)
+typedef U64 sha2_word_t;
+#elif (WORD_SIZE_BITS == 32)
+typedef U32 sha2_word_t;
+#else
+#error According to the FIPS Standard WORD_SIZE_BITS must be either 32 or 64
+#endif
+
+/* define the hash_state structure */
+typedef struct{
+ sha2_word_t state[8];
+ int curlen;
+ sha2_word_t length_upper, length_lower;
+ unsigned char buf[BLOCK_SIZE];
+} hash_state;
+
+#endif /* __HASH_SHA2_H */
diff --git a/src/hash_SHA2_template.c b/src/hash_SHA2_template.c
new file mode 100644
index 0000000..81b1ea6
--- /dev/null
+++ b/src/hash_SHA2_template.c
@@ -0,0 +1,199 @@
+/*
+ * An generic implementation of the SHA-2 hash family, this is endian neutral
+ * so should work just about anywhere.
+ *
+ * This code works much like the MD5 code provided by RSA. You sha_init()
+ * a "sha_state" then sha_process() the bytes you want and sha_done() to get
+ * the output.
+ *
+ * Originally written by Tom St Denis -- http://tomstdenis.home.dhs.org
+ * Adapted for PyCrypto by Jeethu Rao, Taylor Boon, and others.
+ * Turned into a generic template by Lorenz Quack <don@amberfisharts.com>
+ *
+ * ===================================================================
+ * 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.
+ * ===================================================================
+ *
+ */
+
+#include "Python.h"
+#include "pycrypto_compat.h"
+
+/* compress one block */
+static void sha_compress(hash_state * hs)
+{
+ sha2_word_t S[8], W[SCHEDULE_SIZE], T1, T2;
+ int i;
+
+ /* copy state into S */
+ for (i = 0; i < 8; i++)
+ S[i] = hs->state[i];
+
+ /* copy the state into W[0..15] */
+ for (i = 0; i < 16; i++){
+ W[i] = (
+ (((sha2_word_t) hs->buf[(WORD_SIZE*i)+0]) << (WORD_SIZE_BITS- 8)) |
+ (((sha2_word_t) hs->buf[(WORD_SIZE*i)+1]) << (WORD_SIZE_BITS-16)) |
+ (((sha2_word_t) hs->buf[(WORD_SIZE*i)+2]) << (WORD_SIZE_BITS-24)) |
+ (((sha2_word_t) hs->buf[(WORD_SIZE*i)+3]) << (WORD_SIZE_BITS-32))
+#if (WORD_SIZE_BITS == 64)
+ |
+ (((sha2_word_t) hs->buf[(WORD_SIZE*i)+4]) << (WORD_SIZE_BITS-40)) |
+ (((sha2_word_t) hs->buf[(WORD_SIZE*i)+5]) << (WORD_SIZE_BITS-48)) |
+ (((sha2_word_t) hs->buf[(WORD_SIZE*i)+6]) << (WORD_SIZE_BITS-56)) |
+ (((sha2_word_t) hs->buf[(WORD_SIZE*i)+7]))
+#endif
+ );
+ }
+
+ /* fill W[16..SCHEDULE_SIZE] */
+ for (i = 16; i < SCHEDULE_SIZE; i++)
+ W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16];
+
+ /* Compress */
+ for (i = 0; i < SCHEDULE_SIZE; i++) {
+ T1 = S[7] + Sigma1(S[4]) + Ch(S[4], S[5], S[6]) + K[i] + W[i];
+ T2 = Sigma0(S[0]) + Maj(S[0], S[1], S[2]);
+ S[7] = S[6];
+ S[6] = S[5];
+ S[5] = S[4];
+ S[4] = S[3] + T1;
+ S[3] = S[2];
+ S[2] = S[1];
+ S[1] = S[0];
+ S[0] = T1 + T2;
+ }
+
+ /* feedback */
+ for (i = 0; i < 8; i++)
+ hs->state[i] += S[i];
+}
+
+/* adds *inc* to the length of the hash_state *hs*
+ * return 1 on success
+ * return 0 if the length overflows
+ */
+int add_length(hash_state *hs, sha2_word_t inc) {
+ sha2_word_t overflow_detector;
+ overflow_detector = hs->length_lower;
+ hs->length_lower += inc;
+ if (overflow_detector > hs->length_lower) {
+ overflow_detector = hs->length_upper;
+ hs->length_upper++;
+ if (hs->length_upper > hs->length_upper)
+ return 0;
+ }
+ return 1;
+}
+
+/* init the SHA state */
+static void sha_init(hash_state * hs)
+{
+ int i;
+ hs->curlen = hs->length_upper = hs->length_lower = 0;
+ for (i = 0; i < 8; ++i)
+ hs->state[i] = H[i];
+}
+
+static void sha_process(hash_state * hs, unsigned char *buf, int len)
+{
+ while (len--) {
+ /* copy byte */
+ hs->buf[hs->curlen++] = *buf++;
+
+ /* is a block full? */
+ if (hs->curlen == BLOCK_SIZE) {
+ sha_compress(hs);
+ add_length(hs, BLOCK_SIZE_BITS);
+ hs->curlen = 0;
+ }
+ }
+}
+
+static void sha_done(hash_state * hs, unsigned char *hash)
+{
+ int i;
+
+ /* increase the length of the message */
+ add_length(hs, hs->curlen * 8);
+
+ /* append the '1' bit */
+ hs->buf[hs->curlen++] = 0x80;
+
+ /* if the length is currently above LAST_BLOCK_SIZE bytes we append
+ * zeros then compress. Then we can fall back to padding zeros and length
+ * encoding like normal.
+ */
+ if (hs->curlen > LAST_BLOCK_SIZE) {
+ for (; hs->curlen < BLOCK_SIZE;)
+ hs->buf[hs->curlen++] = 0;
+ sha_compress(hs);
+ hs->curlen = 0;
+ }
+
+ /* pad upto LAST_BLOCK_SIZE bytes of zeroes */
+ for (; hs->curlen < LAST_BLOCK_SIZE;)
+ hs->buf[hs->curlen++] = 0;
+
+ /* append length */
+ for (i = 0; i < WORD_SIZE; i++)
+ hs->buf[i + LAST_BLOCK_SIZE] =
+ (hs->length_upper >> ((WORD_SIZE - 1 - i) * 8)) & 0xFF;
+ for (i = 0; i < WORD_SIZE; i++)
+ hs->buf[i + LAST_BLOCK_SIZE + WORD_SIZE] =
+ (hs->length_lower >> ((WORD_SIZE - 1 - i) * 8)) & 0xFF;
+ sha_compress(hs);
+
+ /* copy output */
+ for (i = 0; i < DIGEST_SIZE; i++)
+ hash[i] = (hs->state[i / WORD_SIZE] >>
+ ((WORD_SIZE - 1 - (i % WORD_SIZE)) * 8)) & 0xFF;
+}
+
+// Done
+static void hash_init (hash_state *ptr)
+{
+ sha_init(ptr);
+}
+
+// Done
+static void
+hash_update (hash_state *self, const U8 *buf, int len)
+{
+ sha_process(self,(unsigned char *)buf, len);
+}
+
+// Done
+static void
+hash_copy(hash_state *src, hash_state *dest)
+{
+ memcpy(dest,src,sizeof(hash_state));
+}
+
+// Done
+static PyObject *
+hash_digest (const hash_state *self)
+{
+ unsigned char digest[DIGEST_SIZE];
+ hash_state temp;
+
+ hash_copy((hash_state*)self,&temp);
+ sha_done(&temp,digest);
+ return PyBytes_FromStringAndSize((char *)digest, DIGEST_SIZE);
+}
+
+#include "hash_template.c"
diff --git a/src/hash_template.c b/src/hash_template.c
index 35c9bf5..eb27e9f 100644
--- a/src/hash_template.c
+++ b/src/hash_template.c
@@ -178,11 +178,21 @@ ALG_update(ALGobject *self, PyObject *args)
return Py_None;
}
+/** Forward declaration for this module's new() method **/
+static char ALG_new__doc__[] =
+"new([string]): Return a new " _MODULE_STRING
+" hashing object. An optional string "
+"argument may be provided; if present, this string will be "
+"automatically hashed into the initial state of the object.";
+
+static PyObject *ALG_new(PyObject*, PyObject*);
+
static PyMethodDef ALG_methods[] = {
{"copy", (PyCFunction)ALG_copy, METH_VARARGS, ALG_copy__doc__},
{"digest", (PyCFunction)ALG_digest, METH_VARARGS, ALG_digest__doc__},
{"hexdigest", (PyCFunction)ALG_hexdigest, METH_VARARGS, ALG_hexdigest__doc__},
{"update", (PyCFunction)ALG_update, METH_VARARGS, ALG_update__doc__},
+ {"new", (PyCFunction)ALG_new, METH_VARARGS, ALG_new__doc__},
{NULL, NULL} /* sentinel */
};
@@ -198,10 +208,11 @@ ALG_getattr(PyObject *self, char *name)
goto generic;
if (PyUnicode_CompareWithASCIIString(attr, "digest_size")==0)
+ return PyLong_FromLong(DIGEST_SIZE);
#else
if (strcmp(name, "digest_size")==0)
+ return PyInt_FromLong(DIGEST_SIZE);
#endif
- return PyLong_FromLong(DIGEST_SIZE);
#ifdef IS_PY3K
generic:
@@ -256,12 +267,7 @@ static PyTypeObject ALGtype = {
/* The single module-level function: new() */
-static char ALG_new__doc__[] =
-"new([string]): Return a new " _MODULE_STRING
-" hashing object. An optional string "
-"argument may be provided; if present, this string will be "
-"automatically hashed into the initial state of the object.";
-
+/** This method belong to both the module and the hash object **/
static PyObject *
ALG_new(PyObject *self, PyObject *args)
{