| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| from hashlib import sha1 |
|
|
| import Crypto.Hash.SHA1 |
| import Crypto.Util.number |
| import gmpy2 |
| from Crypto import Random |
| from Crypto.Signature.pss import MGF1 |
| from Crypto.Util.number import bytes_to_long, ceil_div, long_to_bytes |
| from Crypto.Util.py3compat import _copy_bytes, bord |
| from Crypto.Util.strxor import strxor |
|
|
|
|
| class PKCS1OAepCipher: |
| """Cipher object for PKCS#1 v1.5 OAEP. |
| Do not create directly: use :func:`new` instead.""" |
|
|
| def __init__(self, key, hashAlgo, mgfunc, label, randfunc): |
| """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.SHA1` is used. |
| mgfunc : callable |
| A mask generation function that accepts two parameters: a string to |
| use as seed, and the length of the mask to generate, in bytes. |
| If not specified, the standard MGF1 consistent with ``hashAlgo`` is used (a safe choice). |
| label : bytes/bytearray/memoryview |
| A label to apply to this particular encryption. If not specified, |
| an empty string is used. Specifying a label does not improve |
| security. |
| randfunc : callable |
| A function that returns random bytes. |
| |
| :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.SHA1 |
|
|
| if mgfunc: |
| self._mgf = mgfunc |
| else: |
| self._mgf = lambda x, y: MGF1(x, y, self._hashObj) |
|
|
| self._label = _copy_bytes(None, None, label) |
| self._randfunc = randfunc |
|
|
| def can_encrypt(self): |
| """Legacy function to check if you can call :meth:`encrypt`. |
| |
| .. deprecated:: 3.0""" |
| return self._key.can_encrypt() |
|
|
| def can_decrypt(self): |
| """Legacy function to check if you can call :meth:`decrypt`. |
| |
| .. deprecated:: 3.0""" |
| return self._key.can_decrypt() |
|
|
| def encrypt(self, message): |
| """Encrypt a message with PKCS#1 OAEP. |
| |
| :param message: |
| 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. |
| For instance, if you use RSA 2048 and SHA-256, the longest message |
| you can encrypt is 190 byte long. |
| :type message: bytes/bytearray/memoryview |
| |
| :returns: The ciphertext, as large as the RSA modulus. |
| :rtype: bytes |
| |
| :raises ValueError: |
| if the message is too long. |
| """ |
|
|
| |
| modBits = Crypto.Util.number.size(self._key.n) |
| k = ceil_div(modBits, 8) |
| hLen = self._hashObj.digest_size |
| mLen = len(message) |
|
|
| |
| ps_len = k - mLen - 2 * hLen - 2 |
| if ps_len < 0: |
| raise ValueError("Plaintext is too long.") |
| |
| lHash = sha1(self._label).digest() |
| |
| ps = b"\x00" * ps_len |
| |
| db = lHash + ps + b"\x01" + _copy_bytes(None, None, message) |
| |
| ros = self._randfunc(hLen) |
| |
| dbMask = self._mgf(ros, k - hLen - 1) |
| |
| maskedDB = strxor(db, dbMask) |
| |
| seedMask = self._mgf(maskedDB, hLen) |
| |
| maskedSeed = strxor(ros, seedMask) |
| |
| em = b"\x00" + maskedSeed + maskedDB |
| |
| em_int = bytes_to_long(em) |
| |
| m_int = gmpy2.powmod(em_int, self._key.e, self._key.n) |
| |
| c = long_to_bytes(m_int, k) |
| return c |
|
|
| def decrypt(self, ciphertext): |
| """Decrypt a message with PKCS#1 OAEP. |
| |
| :param ciphertext: The encrypted message. |
| :type ciphertext: bytes/bytearray/memoryview |
| |
| :returns: The original message (plaintext). |
| :rtype: bytes |
| |
| :raises ValueError: |
| if the ciphertext has the wrong length, or if decryption |
| fails the integrity check (in which case, the decryption |
| key is probably wrong). |
| :raises TypeError: |
| if the RSA key has no private half (i.e. you are trying |
| to decrypt using a public key). |
| """ |
| |
| modBits = Crypto.Util.number.size(self._key.n) |
| k = ceil_div(modBits, 8) |
| hLen = self._hashObj.digest_size |
| |
| if len(ciphertext) != k or k < hLen + 2: |
| raise ValueError("Ciphertext with incorrect length.") |
| |
| ct_int = bytes_to_long(ciphertext) |
| |
| |
| m_int = gmpy2.powmod(ct_int, self._key.d, self._key.n) |
| |
| em = long_to_bytes(m_int, k) |
| |
| lHash = sha1(self._label).digest() |
| |
| y = em[0] |
| |
| |
| maskedSeed = em[1 : hLen + 1] |
| maskedDB = em[hLen + 1 :] |
| |
| seedMask = self._mgf(maskedDB, hLen) |
| |
| seed = strxor(maskedSeed, seedMask) |
| |
| dbMask = self._mgf(seed, k - hLen - 1) |
| |
| db = strxor(maskedDB, dbMask) |
| |
| one_pos = hLen + db[hLen:].find(b"\x01") |
| lHash1 = db[:hLen] |
| invalid = bord(y) | int(one_pos < hLen) |
| hash_compare = strxor(lHash1, lHash) |
| for x in hash_compare: |
| invalid |= bord(x) |
| for x in db[hLen:one_pos]: |
| invalid |= bord(x) |
| if invalid != 0: |
| raise ValueError("Incorrect decryption.") |
| |
| return db[one_pos + 1 :] |
|
|
|
|
| def new(key, hashAlgo=None, mgfunc=None, label=b"", randfunc=None): |
| """Return a cipher object :class:`PKCS1OAEP_Cipher` |
| that can be used to perform PKCS#1 OAEP encryption or decryption. |
| |
| :param key: |
| The key object to use to encrypt or decrypt the message. |
| Decryption is only possible with a private RSA key. |
| :type key: RSA key object |
| |
| :param hashAlgo: |
| The hash function to use. This can be a module under `Crypto.Hash` |
| or an existing hash object created from any of such modules. |
| If not specified, `Crypto.Hash.SHA1` is used. |
| :type hashAlgo: hash object |
| |
| :param mgfunc: |
| A mask generation function that accepts two parameters: a string to |
| use as seed, and the length of the mask to generate, in bytes. |
| If not specified, the standard MGF1 consistent with ``hashAlgo`` is used (a safe choice). |
| :type mgfunc: callable |
| |
| :param label: |
| A label to apply to this particular encryption. If not specified, |
| an empty string is used. Specifying a label does not improve |
| security. |
| :type label: bytes/bytearray/memoryview |
| |
| :param randfunc: |
| A function that returns random bytes. |
| The default is `Random.get_random_bytes`. |
| :type randfunc: callable |
| """ |
|
|
| if randfunc is None: |
| randfunc = Random.get_random_bytes |
| return PKCS1OAepCipher(key, hashAlgo, mgfunc, label, randfunc) |
|
|