summaryrefslogtreecommitdiff
path: root/lib/Crypto/Cipher
diff options
context:
space:
mode:
authorLegrandin <gooksankoo@hoiptorrow.mailexpire.com>2011-10-02 22:30:07 +0200
committerLegrandin <gooksankoo@hoiptorrow.mailexpire.com>2011-10-02 22:37:36 +0200
commit9cb1a2d35d916180dee8351fe6f2ddf4f6dba72d (patch)
treebb891e868546746a8a756ba8052bb70e79100a16 /lib/Crypto/Cipher
parent02103e2a5aca97b299b63723fb6752c2cbc00b23 (diff)
downloadpycrypto-9cb1a2d35d916180dee8351fe6f2ddf4f6dba72d.tar.gz
To simplify, no RNG needs to be provided with PKCS1 encryption: the one belonging to each RSA key is reused.
Error detection is internally implemented in a simpler (and safer) way for PKCS1 OAEP decryption. General fixes to documentation for PKCS1.
Diffstat (limited to 'lib/Crypto/Cipher')
-rw-r--r--lib/Crypto/Cipher/PKCS1_OAEP.py36
-rw-r--r--lib/Crypto/Cipher/PKCS1_v1_5.py38
-rw-r--r--lib/Crypto/Cipher/__init__.py3
3 files changed, 43 insertions, 34 deletions
diff --git a/lib/Crypto/Cipher/PKCS1_OAEP.py b/lib/Crypto/Cipher/PKCS1_OAEP.py
index 0555950..889333a 100644
--- a/lib/Crypto/Cipher/PKCS1_OAEP.py
+++ b/lib/Crypto/Cipher/PKCS1_OAEP.py
@@ -22,8 +22,7 @@
"""RSA encryption protocol according to PKCS#1 OAEP
-See RFC3447 or the original RSA Labs specification at
-http://www.rsa.com/rsalabs/node.asp?id=2125.
+See RFC3447__ or the `original RSA Labs specification`__ .
This scheme is more properly called ``RSAES-OAEP``.
@@ -31,18 +30,21 @@ As an example, a sender may encrypt a message in this way:
>>> from Crypto.Cipher import PKCS1_OAEP
>>> from Crypto.PublicKey import RSA
- >>> from Crypto import Random
>>>
>>> message = 'To be encrypted'
- >>> key = RSA.importKey('pubkey.der')
- >>> rng = Random.new().read
- >>> ciphertext = PKCS1_OAEP.encrypt(message, key, rng)
+ >>> key = RSA.importKey(open('pubkey.der').read())
+ >>> ciphertext = PKCS1_OAEP.encrypt(message, key)
At the receiver side, decryption can be done using the private part of
the RSA key:
- >>> key = RSA.importKey('privkey.der')
+ >>> key = RSA.importKey(open('privkey.der').read())
>>> message = PKCS1_OAEP.decrypt(ciphertext, key):
+
+:undocumented: __revision__, __package__
+
+.. __: http://www.ietf.org/rfc/rfc3447.txt
+.. __: http://www.rsa.com/rsalabs/node.asp?id=2125.
"""
from __future__ import nested_scopes
@@ -57,9 +59,7 @@ import Crypto.Util.number
from Crypto.Util.number import ceil_div
from Crypto.Util.strxor import strxor
-import re
-
-def encrypt(message, key, randFunc, hashAlgo=None, mgfunc=None, label=''):
+def encrypt(message, key, hashAlgo=None, mgfunc=None, label=''):
"""Produce the PKCS#1 OAEP encryption of a message.
This function is named ``RSAES-OAEP-ENCRYPT``, and is specified in
@@ -73,9 +73,6 @@ def encrypt(message, key, randFunc, hashAlgo=None, mgfunc=None, label=''):
key : RSA key object
The key to use to encrypt the message. This is a `Crypto.PublicKey.RSA`
object.
- randFunc : callable
- An RNG function that accepts as only parameter an integer, and returns
- a string of random bytes.
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,
@@ -100,6 +97,8 @@ def encrypt(message, key, randFunc, hashAlgo=None, mgfunc=None, label=''):
"""
# TODO: Verify the key is RSA
+ randFunc = key._randfunc
+
# See 7.1.1 in RFC3447
modBits = Crypto.Util.number.size(key.n)
k = ceil_div(modBits,8) # Convert from bits to bytes
@@ -200,6 +199,8 @@ def decrypt(ct, key, hashAlgo=None, mgfunc=None, label=''):
lHash = hashObj.new(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
@@ -212,14 +213,13 @@ def decrypt(ct, key, hashAlgo=None, mgfunc=None, label=''):
db = strxor(maskedDB, dbMask)
# Step 3g
valid = 1
+ one = db[hLen:].find('\x01')
lHash1 = db[:hLen]
if lHash1!=lHash:
valid = 0
- try:
- one = re.match('\x00+',db[hLen:]).end()
- if db[hLen+one]!='\x01':
- valid = 0
- except (IndexError, AttributeError):
+ if one<0:
+ valid = 0
+ if y!='\x00':
valid = 0
if not valid:
raise ValueError("Incorrect decryption.")
diff --git a/lib/Crypto/Cipher/PKCS1_v1_5.py b/lib/Crypto/Cipher/PKCS1_v1_5.py
index 6052e89..8d02afe 100644
--- a/lib/Crypto/Cipher/PKCS1_v1_5.py
+++ b/lib/Crypto/Cipher/PKCS1_v1_5.py
@@ -22,7 +22,7 @@
"""RSA encryption protocol according to PKCS#1 v1.5
-See RFC3447 or the `original RSA Labs specification`__ .
+See RFC3447__ or the `original RSA Labs specification`__ .
This scheme is more properly called ``RSAES-PKCS1-v1_5``.
@@ -33,13 +33,13 @@ 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
- >>> from Crypto import Random
>>>
- >>> rng = Random.new()
>>> message = 'To be encrypted'
>>> h = SHA.new(message)
+ >>>
>>> key = RSA.importKey(open('pubkey.der').read())
- >>> ciphertext = PKCS1_v1_5.encrypt(message+h.digest(), key, rng.read)
+ >>>
+ >>> ciphertext = PKCS1_v1_5.encrypt(message+h.digest(), key)
At the receiver side, decryption can be done using the private part of
the RSA key:
@@ -47,17 +47,22 @@ the RSA key:
>>> From Crypto.Hash import SHA
>>> from Crypto import Random
>>>
- >>> rng = Random.new()
- >>> dsize = SHA.digest_size
>>> 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
+ >>>
>>> message = PKCS1_v1_5.decrypt(ciphertext, key, 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.
"""
@@ -67,7 +72,7 @@ __all__ = [ 'encrypt', 'decrypt' ]
from Crypto.Util.number import ceil_div
import Crypto.Util.number
-def encrypt(message, key, randFunc):
+def encrypt(message, key):
"""Produce the PKCS#1 v1.5 encryption of a message.
This function is named ``RSAES-PKCS1-V1_5-ENCRYPT``, and is specified in
@@ -80,9 +85,6 @@ def encrypt(message, key, randFunc):
key : RSA key object
The key to use to encrypt the message. This is a `Crypto.PublicKey.RSA`
object.
- randFunc : callable
- An RNG function that accepts as only parameter an integer, and returns
- a string of random bytes.
:Return: A string, the ciphertext in which the message is encrypted.
It is as long as the RSA modulus (in bytes).
@@ -92,6 +94,8 @@ def encrypt(message, key, randFunc):
"""
# TODO: Verify the key is RSA
+ randFunc = key._randfunc
+
# See 7.2.1 in RFC3447
modBits = Crypto.Util.number.size(key.n)
k = ceil_div(modBits,8) # Convert from bits to bytes
@@ -130,7 +134,7 @@ def decrypt(ct, key, sentinel):
sentinel : string
The string to return to indicate that an error was detected during decryption.
- :Return: A string, the original message or the ``sentinel``.
+ :Return: A 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:
@@ -138,9 +142,11 @@ def decrypt(ct, key, sentinel):
:attention:
You should **never** let the party who submitted the ciphertext know that
- this function returned the ``sentinel`` value, since attacks exist (e.g. `Bleichenbacher's`__)
- that can compromise your RSA private key by means of such information.
-
+ 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.
@@ -157,12 +163,14 @@ def decrypt(ct, key, sentinel):
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.springerlink.com/index/j5758n240017h867.pdf
+ .. __: http://www.bell-labs.com/user/bleichen/papers/pkcs.ps
+
"""
# TODO: Verify the key is RSA
diff --git a/lib/Crypto/Cipher/__init__.py b/lib/Crypto/Cipher/__init__.py
index 75a081f..70b35c2 100644
--- a/lib/Crypto/Cipher/__init__.py
+++ b/lib/Crypto/Cipher/__init__.py
@@ -58,7 +58,7 @@ 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
+of a key pair. The encryption key is often called ``public`` whereas
the decryption key is called ``private``.
======================== =======================
@@ -68,6 +68,7 @@ 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',