| 
									
										
										
										
											2024-01-24 20:22:01 +08:00
										 |  |  | # | 
					
						
							|  |  |  | #  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. | 
					
						
							|  |  |  | # =================================================================== | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-01 18:11:57 +08:00
										 |  |  | from hashlib import sha1 | 
					
						
							| 
									
										
										
										
											2024-01-24 20:22:01 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-01 18:11:57 +08:00
										 |  |  | import Crypto.Hash.SHA1 | 
					
						
							| 
									
										
										
										
											2024-01-24 20:22:01 +08:00
										 |  |  | import Crypto.Util.number | 
					
						
							|  |  |  | import gmpy2 | 
					
						
							| 
									
										
										
										
											2024-02-01 18:11:57 +08:00
										 |  |  | 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 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-24 20:22:01 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-11 16:40:52 +08:00
										 |  |  | class PKCS1OAepCipher: | 
					
						
							| 
									
										
										
										
											2024-01-24 20:22:01 +08:00
										 |  |  |     """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 | 
					
						
							| 
									
										
										
										
											2024-05-13 11:45:29 +09:00
										 |  |  |                 use as seed, and the length of the mask to generate, in bytes. | 
					
						
							| 
									
										
										
										
											2024-01-24 20:22:01 +08:00
										 |  |  |                 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: | 
					
						
							| 
									
										
										
										
											2024-08-15 17:53:12 +08:00
										 |  |  |             self._mgf = lambda x, y: MGF1(x, y, self._hashObj) | 
					
						
							| 
									
										
										
										
											2024-01-24 20:22:01 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         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. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # See 7.1.1 in RFC3447 | 
					
						
							|  |  |  |         modBits = Crypto.Util.number.size(self._key.n) | 
					
						
							| 
									
										
										
										
											2024-08-15 17:53:12 +08:00
										 |  |  |         k = ceil_div(modBits, 8)  # Convert from bits to bytes | 
					
						
							| 
									
										
										
										
											2024-01-24 20:22:01 +08:00
										 |  |  |         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 = sha1(self._label).digest() | 
					
						
							|  |  |  |         # Step 2b | 
					
						
							| 
									
										
										
										
											2024-08-15 17:53:12 +08:00
										 |  |  |         ps = b"\x00" * ps_len | 
					
						
							| 
									
										
										
										
											2024-01-24 20:22:01 +08:00
										 |  |  |         # Step 2c | 
					
						
							| 
									
										
										
										
											2024-08-15 17:53:12 +08:00
										 |  |  |         db = lHash + ps + b"\x01" + _copy_bytes(None, None, message) | 
					
						
							| 
									
										
										
										
											2024-01-24 20:22:01 +08:00
										 |  |  |         # Step 2d | 
					
						
							|  |  |  |         ros = self._randfunc(hLen) | 
					
						
							|  |  |  |         # Step 2e | 
					
						
							| 
									
										
										
										
											2024-08-15 17:53:12 +08:00
										 |  |  |         dbMask = self._mgf(ros, k - hLen - 1) | 
					
						
							| 
									
										
										
										
											2024-01-24 20:22:01 +08:00
										 |  |  |         # Step 2f | 
					
						
							|  |  |  |         maskedDB = strxor(db, dbMask) | 
					
						
							|  |  |  |         # Step 2g | 
					
						
							|  |  |  |         seedMask = self._mgf(maskedDB, hLen) | 
					
						
							|  |  |  |         # Step 2h | 
					
						
							|  |  |  |         maskedSeed = strxor(ros, seedMask) | 
					
						
							|  |  |  |         # Step 2i | 
					
						
							| 
									
										
										
										
											2024-08-15 17:53:12 +08:00
										 |  |  |         em = b"\x00" + maskedSeed + maskedDB | 
					
						
							| 
									
										
										
										
											2024-01-24 20:22:01 +08:00
										 |  |  |         # Step 3a (OS2IP) | 
					
						
							|  |  |  |         em_int = bytes_to_long(em) | 
					
						
							|  |  |  |         # Step 3b (RSAEP) | 
					
						
							|  |  |  |         m_int = gmpy2.powmod(em_int, self._key.e, self._key.n) | 
					
						
							|  |  |  |         # Step 3c (I2OSP) | 
					
						
							|  |  |  |         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). | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         # See 7.1.2 in RFC3447 | 
					
						
							|  |  |  |         modBits = Crypto.Util.number.size(self._key.n) | 
					
						
							| 
									
										
										
										
											2024-08-15 17:53:12 +08:00
										 |  |  |         k = ceil_div(modBits, 8)  # Convert from bits to bytes | 
					
						
							| 
									
										
										
										
											2024-01-24 20:22:01 +08:00
										 |  |  |         hLen = self._hashObj.digest_size | 
					
						
							|  |  |  |         # Step 1b and 1c | 
					
						
							| 
									
										
										
										
											2024-08-15 17:53:12 +08:00
										 |  |  |         if len(ciphertext) != k or k < hLen + 2: | 
					
						
							| 
									
										
										
										
											2024-01-24 20:22:01 +08:00
										 |  |  |             raise ValueError("Ciphertext with incorrect length.") | 
					
						
							|  |  |  |         # Step 2a (O2SIP) | 
					
						
							|  |  |  |         ct_int = bytes_to_long(ciphertext) | 
					
						
							|  |  |  |         # Step 2b (RSADP) | 
					
						
							|  |  |  |         # m_int = self._key._decrypt(ct_int) | 
					
						
							|  |  |  |         m_int = gmpy2.powmod(ct_int, self._key.d, self._key.n) | 
					
						
							|  |  |  |         # Complete step 2c (I2OSP) | 
					
						
							|  |  |  |         em = long_to_bytes(m_int, k) | 
					
						
							|  |  |  |         # Step 3a | 
					
						
							|  |  |  |         lHash = sha1(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) | 
					
						
							| 
									
										
										
										
											2024-08-15 17:53:12 +08:00
										 |  |  |         maskedSeed = em[1 : hLen + 1] | 
					
						
							|  |  |  |         maskedDB = em[hLen + 1 :] | 
					
						
							| 
									
										
										
										
											2024-01-24 20:22:01 +08:00
										 |  |  |         # Step 3c | 
					
						
							|  |  |  |         seedMask = self._mgf(maskedDB, hLen) | 
					
						
							|  |  |  |         # Step 3d | 
					
						
							|  |  |  |         seed = strxor(maskedSeed, seedMask) | 
					
						
							|  |  |  |         # Step 3e | 
					
						
							| 
									
										
										
										
											2024-08-15 17:53:12 +08:00
										 |  |  |         dbMask = self._mgf(seed, k - hLen - 1) | 
					
						
							| 
									
										
										
										
											2024-01-24 20:22:01 +08:00
										 |  |  |         # Step 3f | 
					
						
							|  |  |  |         db = strxor(maskedDB, dbMask) | 
					
						
							|  |  |  |         # Step 3g | 
					
						
							| 
									
										
										
										
											2024-08-15 17:53:12 +08:00
										 |  |  |         one_pos = hLen + db[hLen:].find(b"\x01") | 
					
						
							| 
									
										
										
										
											2024-01-24 20:22:01 +08:00
										 |  |  |         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.") | 
					
						
							|  |  |  |         # Step 4 | 
					
						
							| 
									
										
										
										
											2024-08-15 17:53:12 +08:00
										 |  |  |         return db[one_pos + 1 :] | 
					
						
							| 
									
										
										
										
											2024-01-24 20:22:01 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-15 17:53:12 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | def new(key, hashAlgo=None, mgfunc=None, label=b"", randfunc=None): | 
					
						
							| 
									
										
										
										
											2024-09-12 14:00:36 +08:00
										 |  |  |     """Return a cipher object :class:`PKCS1OAEP_Cipher`
 | 
					
						
							|  |  |  |      that can be used to perform PKCS#1 OAEP encryption or decryption. | 
					
						
							| 
									
										
										
										
											2024-01-24 20:22:01 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     :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 | 
					
						
							| 
									
										
										
										
											2024-05-13 11:45:29 +09:00
										 |  |  |       use as seed, and the length of the mask to generate, in bytes. | 
					
						
							| 
									
										
										
										
											2024-01-24 20:22:01 +08:00
										 |  |  |       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 | 
					
						
							| 
									
										
										
										
											2024-09-11 16:40:52 +08:00
										 |  |  |     return PKCS1OAepCipher(key, hashAlgo, mgfunc, label, randfunc) |