Spaces:
Build error
Build error
# | |
# 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. | |
# =================================================================== | |
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. | |
""" | |
# 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 = sha1(self._label).digest() | |
# Step 2b | |
ps = b"\x00" * ps_len | |
# Step 2c | |
db = lHash + ps + b"\x01" + _copy_bytes(None, None, message) | |
# Step 2d | |
ros = self._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 = b"\x00" + maskedSeed + maskedDB | |
# 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) | |
k = ceil_div(modBits, 8) # Convert from bits to bytes | |
hLen = self._hashObj.digest_size | |
# Step 1b and 1c | |
if len(ciphertext) != k or k < hLen + 2: | |
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) | |
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 | |
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.") | |
# Step 4 | |
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) | |