]>
gitweb.pimeys.fr Git - NK2015_Client_Python_Alpha.git/blob - rsa_source/rsa/pkcs1.py
1 # -*- coding: utf-8 -*-
3 # Copyright 2011 Sybren A. Stüvel <sybren@stuvel.eu>
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
9 # http://www.apache.org/licenses/LICENSE-2.0
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.
17 '''Functions for PKCS#1 version 1.5 encryption and signing
19 This module implements certain functionality from PKCS#1 version 1.5. For a
20 very clear example, read http://www.di-mgt.com.au/rsa_alg.html#pkcs1schemes
22 At least 8 bytes of random padding is used when encrypting a message. This makes
23 these methods much more secure than the ones in the ``rsa`` module.
25 WARNING: this module leaks information when decryption or verification fails.
26 The exceptions that are raised contain the Python traceback information, which
27 can be used to deduce where in the process the failure occurred. DO NOT PASS
28 SUCH INFORMATION to your users.
34 from rsa
import common
, transform
, core
, varblock
36 # ASN.1 codes that describe the hash algorithm used.
38 'MD5': '\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x05\x05\x00\x04\x10',
39 'SHA-1': '\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14',
40 'SHA-256': '\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20',
41 'SHA-384': '\x30\x41\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02\x05\x00\x04\x30',
42 'SHA-512': '\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x05\x00\x04\x40',
47 'SHA-1': hashlib
.sha1
,
48 'SHA-256': hashlib
.sha256
,
49 'SHA-384': hashlib
.sha384
,
50 'SHA-512': hashlib
.sha512
,
53 class CryptoError(Exception):
54 '''Base class for all exceptions in this module.'''
56 class DecryptionError(CryptoError
):
57 '''Raised when decryption fails.'''
59 class VerificationError(CryptoError
):
60 '''Raised when verification fails.'''
62 def _pad_for_encryption(message
, target_length
):
63 r
'''Pads the message for encryption, returning the padded message.
65 :return: 00 02 RANDOM_DATA 00 MESSAGE
67 >>> block = _pad_for_encryption('hello', 16)
77 max_msglength
= target_length
- 11
78 msglength
= len(message
)
80 if msglength
> max_msglength
:
81 raise OverflowError('%i bytes needed for message, but there is only'
82 ' space for %i' % (msglength
, max_msglength
))
86 padding_length
= target_length
- msglength
- 3
88 # We remove 0-bytes, so we'll end up with less padding than we've asked for,
89 # so keep adding data until we're at the correct length.
90 while len(padding
) < padding_length
:
91 needed_bytes
= padding_length
- len(padding
)
93 # Always read at least 8 bytes more than we need, and trim off the rest
94 # after removing the 0-bytes. This increases the chance of getting
95 # enough bytes, especially when needed_bytes is small
96 new_padding
= os
.urandom(needed_bytes
+ 5)
97 new_padding
= new_padding
.replace('\x00', '')
98 padding
= padding
+ new_padding
[:needed_bytes
]
100 assert len(padding
) == padding_length
102 return ''.join(['\x00\x02',
108 def _pad_for_signing(message
, target_length
):
109 r
'''Pads the message for signing, returning the padded message.
111 The padding is always a repetition of FF bytes.
113 :return: 00 01 PADDING 00 MESSAGE
115 >>> block = _pad_for_signing('hello', 16)
123 '\xff\xff\xff\xff\xff\xff\xff\xff'
127 max_msglength
= target_length
- 11
128 msglength
= len(message
)
130 if msglength
> max_msglength
:
131 raise OverflowError('%i bytes needed for message, but there is only'
132 ' space for %i' % (msglength
, max_msglength
))
134 padding_length
= target_length
- msglength
- 3
136 return ''.join(['\x00\x01',
137 padding_length
* '\xff',
142 def encrypt(message
, pub_key
):
143 '''Encrypts the given message using PKCS#1 v1.5
145 :param message: the message to encrypt. Must be a byte string no longer than
146 ``k-11`` bytes, where ``k`` is the number of bytes needed to encode
147 the ``n`` component of the public key.
148 :param pub_key: the :py:class:`rsa.PublicKey` to encrypt with.
149 :raise OverflowError: when the message is too large to fit in the padded
152 >>> from rsa import key, common
153 >>> (pub_key, priv_key) = key.newkeys(256)
154 >>> message = 'hello'
155 >>> crypto = encrypt(message, pub_key)
157 The crypto text should be just as long as the public key 'n' component:
159 >>> len(crypto) == common.byte_size(pub_key.n)
164 keylength
= common
.byte_size(pub_key
.n
)
165 padded
= _pad_for_encryption(message
, keylength
)
167 payload
= transform
.bytes2int(padded
)
168 encrypted
= core
.encrypt_int(payload
, pub_key
.e
, pub_key
.n
)
169 block
= transform
.int2bytes(encrypted
, keylength
)
173 def decrypt(crypto
, priv_key
):
174 r
'''Decrypts the given message using PKCS#1 v1.5
176 The decryption is considered 'failed' when the resulting cleartext doesn't
177 start with the bytes 00 02, or when the 00 byte between the padding and
178 the message cannot be found.
180 :param crypto: the crypto text as returned by :py:func:`rsa.encrypt`
181 :param priv_key: the :py:class:`rsa.PrivateKey` to decrypt with.
182 :raise DecryptionError: when the decryption fails. No details are given as
183 to why the code thinks the decryption fails, as this would leak
184 information about the private key.
188 >>> (pub_key, priv_key) = rsa.newkeys(256)
190 It works with strings:
192 >>> crypto = encrypt('hello', pub_key)
193 >>> decrypt(crypto, priv_key)
196 And with binary data:
198 >>> crypto = encrypt('\x00\x00\x00\x00\x01', pub_key)
199 >>> decrypt(crypto, priv_key)
200 '\x00\x00\x00\x00\x01'
202 Altering the encrypted information will *likely* cause a
203 :py:class:`rsa.pkcs1.DecryptionError`. If you want to be *sure*, use
209 Never display the stack trace of a
210 :py:class:`rsa.pkcs1.DecryptionError` exception. It shows where in the
211 code the exception occurred, and thus leaks information about the key.
212 It's only a tiny bit of information, but every bit makes cracking the
215 >>> crypto = encrypt('hello', pub_key)
216 >>> crypto = 'X' + crypto[1:] # change the first byte
217 >>> decrypt(crypto, priv_key)
218 Traceback (most recent call last):
220 DecryptionError: Decryption failed
224 blocksize
= common
.byte_size(priv_key
.n
)
225 encrypted
= transform
.bytes2int(crypto
)
226 decrypted
= core
.decrypt_int(encrypted
, priv_key
.d
, priv_key
.n
)
227 cleartext
= transform
.int2bytes(decrypted
, blocksize
)
229 # If we can't find the cleartext marker, decryption failed.
230 if cleartext
[0:2] != '\x00\x02':
231 raise DecryptionError('Decryption failed')
233 # Find the 00 separator between the padding and the message
235 sep_idx
= cleartext
.index('\x00', 2)
237 raise DecryptionError('Decryption failed')
239 return cleartext
[sep_idx
+1:]
241 def sign(message
, priv_key
, hash):
242 '''Signs the message with the private key.
244 Hashes the message, then signs the hash with the given key. This is known
245 as a "detached signature", because the message itself isn't altered.
247 :param message: the message to sign. Can be an 8-bit string or a file-like
248 object. If ``message`` has a ``read()`` method, it is assumed to be a
250 :param priv_key: the :py:class:`rsa.PrivateKey` to sign with
251 :param hash: the hash method used on the message. Use 'MD5', 'SHA-1',
252 'SHA-256', 'SHA-384' or 'SHA-512'.
253 :return: a message signature block.
254 :raise OverflowError: if the private key is too small to contain the
259 # Get the ASN1 code for this hash method
260 if hash not in HASH_ASN1
:
261 raise ValueError('Invalid hash method: %s' % hash)
262 asn1code
= HASH_ASN1
[hash]
265 hash = _hash(message
, hash)
267 # Encrypt the hash with the private key
268 cleartext
= asn1code
+ hash
269 keylength
= common
.byte_size(priv_key
.n
)
270 padded
= _pad_for_signing(cleartext
, keylength
)
272 payload
= transform
.bytes2int(padded
)
273 encrypted
= core
.encrypt_int(payload
, priv_key
.d
, priv_key
.n
)
274 block
= transform
.int2bytes(encrypted
, keylength
)
278 def verify(message
, signature
, pub_key
):
279 '''Verifies that the signature matches the message.
281 The hash method is detected automatically from the signature.
283 :param message: the signed message. Can be an 8-bit string or a file-like
284 object. If ``message`` has a ``read()`` method, it is assumed to be a
286 :param signature: the signature block, as created with :py:func:`rsa.sign`.
287 :param pub_key: the :py:class:`rsa.PublicKey` of the person signing the message.
288 :raise VerificationError: when the signature doesn't match the message.
292 Never display the stack trace of a
293 :py:class:`rsa.pkcs1.VerificationError` exception. It shows where in
294 the code the exception occurred, and thus leaks information about the
295 key. It's only a tiny bit of information, but every bit makes cracking
300 blocksize
= common
.byte_size(pub_key
.n
)
301 encrypted
= transform
.bytes2int(signature
)
302 decrypted
= core
.decrypt_int(encrypted
, pub_key
.e
, pub_key
.n
)
303 clearsig
= transform
.int2bytes(decrypted
, blocksize
)
305 # If we can't find the signature marker, verification failed.
306 if clearsig
[0:2] != '\x00\x01':
307 raise VerificationError('Verification failed')
309 # Find the 00 separator between the padding and the payload
311 sep_idx
= clearsig
.index('\x00', 2)
313 raise VerificationError('Verification failed')
315 # Get the hash and the hash method
316 (method_name
, signature_hash
) = _find_method_hash(clearsig
[sep_idx
+1:])
317 message_hash
= _hash(message
, method_name
)
319 # Compare the real hash to the hash in the signature
320 if message_hash
!= signature_hash
:
321 raise VerificationError('Verification failed')
323 def _hash(message
, method_name
):
324 '''Returns the message digest.
326 :param message: the signed message. Can be an 8-bit string or a file-like
327 object. If ``message`` has a ``read()`` method, it is assumed to be a
329 :param method_name: the hash method, must be a key of
330 :py:const:`HASH_METHODS`.
334 if method_name
not in HASH_METHODS
:
335 raise ValueError('Invalid hash method: %s' % method_name
)
337 method
= HASH_METHODS
[method_name
]
340 if hasattr(message
, 'read') and hasattr(message
.read
, '__call__'):
342 for block
in varblock
.yield_fixedblocks(message
, 1024):
345 # hash the message object itself.
346 hasher
.update(message
)
348 return hasher
.digest()
351 def _find_method_hash(method_hash
):
352 '''Finds the hash method and the hash itself.
354 :param method_hash: ASN1 code for the hash method concatenated with the
357 :return: tuple (method, hash) where ``method`` is the used hash method, and
358 ``hash`` is the hash itself.
360 :raise VerificationFailed: when the hash method cannot be found
364 for (hashname
, asn1code
) in HASH_ASN1
.iteritems():
365 if not method_hash
.startswith(asn1code
):
368 return (hashname
, method_hash
[len(asn1code
):])
370 raise VerificationError('Verification failed')
373 __all__
= ['encrypt', 'decrypt', 'sign', 'verify',
374 'DecryptionError', 'VerificationError', 'CryptoError']
376 if __name__
== '__main__':
377 print 'Running doctests 1000x or until failure'
380 for count
in range(1000):
381 (failures
, tests
) = doctest
.testmod()
385 if count
and count
% 100 == 0:
386 print '%i times' % count
388 print 'Doctests done'