1# -*- coding: utf-8 -*- 2# 3# Copyright 2011 Sybren A. Stüvel <sybren@stuvel.eu> 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# https://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17"""Functions for PKCS#1 version 1.5 encryption and signing 18 19This module implements certain functionality from PKCS#1 version 1.5. For a 20very clear example, read http://www.di-mgt.com.au/rsa_alg.html#pkcs1schemes 21 22At least 8 bytes of random padding is used when encrypting a message. This makes 23these methods much more secure than the ones in the ``rsa`` module. 24 25WARNING: this module leaks information when decryption fails. The exceptions 26that are raised contain the Python traceback information, which can be used to 27deduce where in the process the failure occurred. DO NOT PASS SUCH INFORMATION 28to your users. 29""" 30 31import hashlib 32import os 33 34from rsa._compat import range 35from rsa import common, transform, core 36 37# ASN.1 codes that describe the hash algorithm used. 38HASH_ASN1 = { 39 'MD5': b'\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x05\x05\x00\x04\x10', 40 'SHA-1': b'\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14', 41 'SHA-224': b'\x30\x2d\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x04\x05\x00\x04\x1c', 42 'SHA-256': b'\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20', 43 'SHA-384': b'\x30\x41\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02\x05\x00\x04\x30', 44 'SHA-512': b'\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x05\x00\x04\x40', 45} 46 47HASH_METHODS = { 48 'MD5': hashlib.md5, 49 'SHA-1': hashlib.sha1, 50 'SHA-224': hashlib.sha224, 51 'SHA-256': hashlib.sha256, 52 'SHA-384': hashlib.sha384, 53 'SHA-512': hashlib.sha512, 54} 55 56 57class CryptoError(Exception): 58 """Base class for all exceptions in this module.""" 59 60 61class DecryptionError(CryptoError): 62 """Raised when decryption fails.""" 63 64 65class VerificationError(CryptoError): 66 """Raised when verification fails.""" 67 68 69def _pad_for_encryption(message, target_length): 70 r"""Pads the message for encryption, returning the padded message. 71 72 :return: 00 02 RANDOM_DATA 00 MESSAGE 73 74 >>> block = _pad_for_encryption(b'hello', 16) 75 >>> len(block) 76 16 77 >>> block[0:2] 78 b'\x00\x02' 79 >>> block[-6:] 80 b'\x00hello' 81 82 """ 83 84 max_msglength = target_length - 11 85 msglength = len(message) 86 87 if msglength > max_msglength: 88 raise OverflowError('%i bytes needed for message, but there is only' 89 ' space for %i' % (msglength, max_msglength)) 90 91 # Get random padding 92 padding = b'' 93 padding_length = target_length - msglength - 3 94 95 # We remove 0-bytes, so we'll end up with less padding than we've asked for, 96 # so keep adding data until we're at the correct length. 97 while len(padding) < padding_length: 98 needed_bytes = padding_length - len(padding) 99 100 # Always read at least 8 bytes more than we need, and trim off the rest 101 # after removing the 0-bytes. This increases the chance of getting 102 # enough bytes, especially when needed_bytes is small 103 new_padding = os.urandom(needed_bytes + 5) 104 new_padding = new_padding.replace(b'\x00', b'') 105 padding = padding + new_padding[:needed_bytes] 106 107 assert len(padding) == padding_length 108 109 return b''.join([b'\x00\x02', 110 padding, 111 b'\x00', 112 message]) 113 114 115def _pad_for_signing(message, target_length): 116 r"""Pads the message for signing, returning the padded message. 117 118 The padding is always a repetition of FF bytes. 119 120 :return: 00 01 PADDING 00 MESSAGE 121 122 >>> block = _pad_for_signing(b'hello', 16) 123 >>> len(block) 124 16 125 >>> block[0:2] 126 b'\x00\x01' 127 >>> block[-6:] 128 b'\x00hello' 129 >>> block[2:-6] 130 b'\xff\xff\xff\xff\xff\xff\xff\xff' 131 132 """ 133 134 max_msglength = target_length - 11 135 msglength = len(message) 136 137 if msglength > max_msglength: 138 raise OverflowError('%i bytes needed for message, but there is only' 139 ' space for %i' % (msglength, max_msglength)) 140 141 padding_length = target_length - msglength - 3 142 143 return b''.join([b'\x00\x01', 144 padding_length * b'\xff', 145 b'\x00', 146 message]) 147 148 149def encrypt(message, pub_key): 150 """Encrypts the given message using PKCS#1 v1.5 151 152 :param message: the message to encrypt. Must be a byte string no longer than 153 ``k-11`` bytes, where ``k`` is the number of bytes needed to encode 154 the ``n`` component of the public key. 155 :param pub_key: the :py:class:`rsa.PublicKey` to encrypt with. 156 :raise OverflowError: when the message is too large to fit in the padded 157 block. 158 159 >>> from rsa import key, common 160 >>> (pub_key, priv_key) = key.newkeys(256) 161 >>> message = b'hello' 162 >>> crypto = encrypt(message, pub_key) 163 164 The crypto text should be just as long as the public key 'n' component: 165 166 >>> len(crypto) == common.byte_size(pub_key.n) 167 True 168 169 """ 170 171 keylength = common.byte_size(pub_key.n) 172 padded = _pad_for_encryption(message, keylength) 173 174 payload = transform.bytes2int(padded) 175 encrypted = core.encrypt_int(payload, pub_key.e, pub_key.n) 176 block = transform.int2bytes(encrypted, keylength) 177 178 return block 179 180 181def decrypt(crypto, priv_key): 182 r"""Decrypts the given message using PKCS#1 v1.5 183 184 The decryption is considered 'failed' when the resulting cleartext doesn't 185 start with the bytes 00 02, or when the 00 byte between the padding and 186 the message cannot be found. 187 188 :param crypto: the crypto text as returned by :py:func:`rsa.encrypt` 189 :param priv_key: the :py:class:`rsa.PrivateKey` to decrypt with. 190 :raise DecryptionError: when the decryption fails. No details are given as 191 to why the code thinks the decryption fails, as this would leak 192 information about the private key. 193 194 195 >>> import rsa 196 >>> (pub_key, priv_key) = rsa.newkeys(256) 197 198 It works with strings: 199 200 >>> crypto = encrypt(b'hello', pub_key) 201 >>> decrypt(crypto, priv_key) 202 b'hello' 203 204 And with binary data: 205 206 >>> crypto = encrypt(b'\x00\x00\x00\x00\x01', pub_key) 207 >>> decrypt(crypto, priv_key) 208 b'\x00\x00\x00\x00\x01' 209 210 Altering the encrypted information will *likely* cause a 211 :py:class:`rsa.pkcs1.DecryptionError`. If you want to be *sure*, use 212 :py:func:`rsa.sign`. 213 214 215 .. warning:: 216 217 Never display the stack trace of a 218 :py:class:`rsa.pkcs1.DecryptionError` exception. It shows where in the 219 code the exception occurred, and thus leaks information about the key. 220 It's only a tiny bit of information, but every bit makes cracking the 221 keys easier. 222 223 >>> crypto = encrypt(b'hello', pub_key) 224 >>> crypto = crypto[0:5] + b'X' + crypto[6:] # change a byte 225 >>> decrypt(crypto, priv_key) 226 Traceback (most recent call last): 227 ... 228 rsa.pkcs1.DecryptionError: Decryption failed 229 230 """ 231 232 blocksize = common.byte_size(priv_key.n) 233 encrypted = transform.bytes2int(crypto) 234 decrypted = priv_key.blinded_decrypt(encrypted) 235 cleartext = transform.int2bytes(decrypted, blocksize) 236 237 # If we can't find the cleartext marker, decryption failed. 238 if cleartext[0:2] != b'\x00\x02': 239 raise DecryptionError('Decryption failed') 240 241 # Find the 00 separator between the padding and the message 242 try: 243 sep_idx = cleartext.index(b'\x00', 2) 244 except ValueError: 245 raise DecryptionError('Decryption failed') 246 247 return cleartext[sep_idx + 1:] 248 249 250def sign_hash(hash_value, priv_key, hash_method): 251 """Signs a precomputed hash with the private key. 252 253 Hashes the message, then signs the hash with the given key. This is known 254 as a "detached signature", because the message itself isn't altered. 255 256 :param hash_value: A precomputed hash to sign (ignores message). Should be set to 257 None if needing to hash and sign message. 258 :param priv_key: the :py:class:`rsa.PrivateKey` to sign with 259 :param hash_method: the hash method used on the message. Use 'MD5', 'SHA-1', 260 'SHA-224', SHA-256', 'SHA-384' or 'SHA-512'. 261 :return: a message signature block. 262 :raise OverflowError: if the private key is too small to contain the 263 requested hash. 264 265 """ 266 267 # Get the ASN1 code for this hash method 268 if hash_method not in HASH_ASN1: 269 raise ValueError('Invalid hash method: %s' % hash_method) 270 asn1code = HASH_ASN1[hash_method] 271 272 # Encrypt the hash with the private key 273 cleartext = asn1code + hash_value 274 keylength = common.byte_size(priv_key.n) 275 padded = _pad_for_signing(cleartext, keylength) 276 277 payload = transform.bytes2int(padded) 278 encrypted = priv_key.blinded_encrypt(payload) 279 block = transform.int2bytes(encrypted, keylength) 280 281 return block 282 283 284def sign(message, priv_key, hash_method): 285 """Signs the message with the private key. 286 287 Hashes the message, then signs the hash with the given key. This is known 288 as a "detached signature", because the message itself isn't altered. 289 290 :param message: the message to sign. Can be an 8-bit string or a file-like 291 object. If ``message`` has a ``read()`` method, it is assumed to be a 292 file-like object. 293 :param priv_key: the :py:class:`rsa.PrivateKey` to sign with 294 :param hash_method: the hash method used on the message. Use 'MD5', 'SHA-1', 295 'SHA-224', SHA-256', 'SHA-384' or 'SHA-512'. 296 :return: a message signature block. 297 :raise OverflowError: if the private key is too small to contain the 298 requested hash. 299 300 """ 301 302 msg_hash = compute_hash(message, hash_method) 303 return sign_hash(msg_hash, priv_key, hash_method) 304 305 306def verify(message, signature, pub_key): 307 """Verifies that the signature matches the message. 308 309 The hash method is detected automatically from the signature. 310 311 :param message: the signed message. Can be an 8-bit string or a file-like 312 object. If ``message`` has a ``read()`` method, it is assumed to be a 313 file-like object. 314 :param signature: the signature block, as created with :py:func:`rsa.sign`. 315 :param pub_key: the :py:class:`rsa.PublicKey` of the person signing the message. 316 :raise VerificationError: when the signature doesn't match the message. 317 :returns: the name of the used hash. 318 319 """ 320 321 keylength = common.byte_size(pub_key.n) 322 encrypted = transform.bytes2int(signature) 323 decrypted = core.decrypt_int(encrypted, pub_key.e, pub_key.n) 324 clearsig = transform.int2bytes(decrypted, keylength) 325 326 # Get the hash method 327 method_name = _find_method_hash(clearsig) 328 message_hash = compute_hash(message, method_name) 329 330 # Reconstruct the expected padded hash 331 cleartext = HASH_ASN1[method_name] + message_hash 332 expected = _pad_for_signing(cleartext, keylength) 333 334 # Compare with the signed one 335 if expected != clearsig: 336 raise VerificationError('Verification failed') 337 338 return method_name 339 340 341def find_signature_hash(signature, pub_key): 342 """Returns the hash name detected from the signature. 343 344 If you also want to verify the message, use :py:func:`rsa.verify()` instead. 345 It also returns the name of the used hash. 346 347 :param signature: the signature block, as created with :py:func:`rsa.sign`. 348 :param pub_key: the :py:class:`rsa.PublicKey` of the person signing the message. 349 :returns: the name of the used hash. 350 """ 351 352 keylength = common.byte_size(pub_key.n) 353 encrypted = transform.bytes2int(signature) 354 decrypted = core.decrypt_int(encrypted, pub_key.e, pub_key.n) 355 clearsig = transform.int2bytes(decrypted, keylength) 356 357 return _find_method_hash(clearsig) 358 359 360def yield_fixedblocks(infile, blocksize): 361 """Generator, yields each block of ``blocksize`` bytes in the input file. 362 363 :param infile: file to read and separate in blocks. 364 :param blocksize: block size in bytes. 365 :returns: a generator that yields the contents of each block 366 """ 367 368 while True: 369 block = infile.read(blocksize) 370 371 read_bytes = len(block) 372 if read_bytes == 0: 373 break 374 375 yield block 376 377 if read_bytes < blocksize: 378 break 379 380 381def compute_hash(message, method_name): 382 """Returns the message digest. 383 384 :param message: the signed message. Can be an 8-bit string or a file-like 385 object. If ``message`` has a ``read()`` method, it is assumed to be a 386 file-like object. 387 :param method_name: the hash method, must be a key of 388 :py:const:`HASH_METHODS`. 389 390 """ 391 392 if method_name not in HASH_METHODS: 393 raise ValueError('Invalid hash method: %s' % method_name) 394 395 method = HASH_METHODS[method_name] 396 hasher = method() 397 398 if hasattr(message, 'read') and hasattr(message.read, '__call__'): 399 # read as 1K blocks 400 for block in yield_fixedblocks(message, 1024): 401 hasher.update(block) 402 else: 403 # hash the message object itself. 404 hasher.update(message) 405 406 return hasher.digest() 407 408 409def _find_method_hash(clearsig): 410 """Finds the hash method. 411 412 :param clearsig: full padded ASN1 and hash. 413 :return: the used hash method. 414 :raise VerificationFailed: when the hash method cannot be found 415 """ 416 417 for (hashname, asn1code) in HASH_ASN1.items(): 418 if asn1code in clearsig: 419 return hashname 420 421 raise VerificationError('Verification failed') 422 423 424__all__ = ['encrypt', 'decrypt', 'sign', 'verify', 425 'DecryptionError', 'VerificationError', 'CryptoError'] 426 427if __name__ == '__main__': 428 print('Running doctests 1000x or until failure') 429 import doctest 430 431 for count in range(1000): 432 (failures, tests) = doctest.testmod() 433 if failures: 434 break 435 436 if count % 100 == 0 and count: 437 print('%i times' % count) 438 439 print('Doctests done') 440