Permalink
Comparing changes
Choose two branches to see what’s changed or to start a new pull request.
If you need to, you can also .
Open a pull request
Create a new pull request by comparing changes across two branches. If you need to, you can also .
7
contributors
Commits on Feb 22, 2020
Commits on Feb 23, 2020
Commits on Feb 29, 2020
Commits on Apr 14, 2020
The receiver should use the same hashing method as the sender
Closing the files after writing to not lost data. Consider using context, e.g.: with open(...) as file_out:
Commits on Apr 18, 2020
Commits on May 09, 2020
Commits on Jun 14, 2020
Commits on Jun 19, 2020
🗝️ length must be 16 or 24 bytes long, not 8
Commits on Jun 21, 2020
Commits on Jun 22, 2020
Incorrect CFB decryption when the output (plaintext) goes into the input buffer (ciphertext), before the ciphertext is taken for contribution into the next cipher operation.
Unified
Split
Showing
with
605 additions
and 547 deletions.
- +14 −0 Changelog.rst
- +6 −1 Doc/src/examples.rst
- +1 −1 Doc/src/protocol/kdf.rst
- +94 −5 Doc/src/protocol/ss.rst
- +1 −1 Doc/src/signature/pkcs1_v1_5.rst
- BIN Doc/src/util/counter_be.png
- +3 −3 INSTALL.rst
- +1 −1 README.rst
- +8 −1 lib/Crypto/Cipher/ARC2.py
- +1 −1 lib/Crypto/Cipher/DES3.py
- +3 −3 lib/Crypto/Math/_IntegerGMP.py
- +71 −117 lib/Crypto/Protocol/SecretSharing.py
- +5 −3 lib/Crypto/Protocol/SecretSharing.pyi
- +20 −0 lib/Crypto/SelfTest/Cipher/test_CBC.py
- +3 −0 lib/Crypto/SelfTest/Cipher/test_OpenPGP.py
- +5 −3 lib/Crypto/SelfTest/Math/test_Numbers.py
- +98 −17 lib/Crypto/SelfTest/Protocol/test_SecretSharing.py
- +11 −6 lib/Crypto/Signature/DSS.py
- +7 −2 lib/Crypto/Util/_raw_api.py
- +1 −1 lib/Crypto/__init__.py
- +3 −1 setup.py
- +7 −16 src/Makefile
- +4 −13 src/ec_ws.c
- +163 −0 src/make_ecc_table.py
- +0 −110 src/make_p256_table.c
- +0 −110 src/make_p384_table.c
- +0 −110 src/make_p521_table.c
- +5 −5 src/p256_table.c
- +7 −0 src/p256_table.h
- +5 −5 src/p384_table.c
- +7 −0 src/p384_table.h
- +5 −5 src/p521_table.c
- +7 −0 src/p521_table.h
- +26 −4 src/raw_cfb.c
- +13 −2 src/test/Makefile
| @@ -1,6 +1,20 @@ | ||
| Changelog | ||
| ========= | ||
|
|
||
| 3.9.8 (23 June 2020) | ||
| ++++++++++++++++++++ | ||
|
|
||
| Resolved issues | ||
| --------------- | ||
| * GH#426: The Shamir's secret sharing implementation is not actually compatible with ``ssss``. | ||
| Added an optional parameter to enable interoperability. | ||
| * GH#427: Skip altogether loading of ``gmp.dll`` on Windows. | ||
| * GH#420: Fix incorrect CFB decryption when the input and the output are the same buffer. | ||
|
|
||
| New features | ||
| ------------ | ||
| * Speed up Shamir's secret sharing routines. Thanks to ncarve. | ||
|
|
||
| 3.9.7 (20 February 2020) | ||
| ++++++++++++++++++++++++ | ||
|
|
||
| @@ -20,6 +20,7 @@ encryption modes`_ like `GCM`_, `CCM`_ or `SIV`_). | ||
| file_out = open("encrypted.bin", "wb") | ||
| [ file_out.write(x) for x in (cipher.nonce, tag, ciphertext) ] | ||
| file_out.close() | ||
| At the other end, the receiver can securely load the piece of data back (if they know the key!). | ||
| Note that the code generates a ``ValueError`` exception when tampering is detected. | ||
| @@ -53,7 +54,8 @@ At the end, the code prints our the RSA public key in ASCII/PEM format: | ||
| file_out = open("rsa_key.bin", "wb") | ||
| file_out.write(encrypted_key) | ||
| file_out.close() | ||
| print(key.publickey().export_key()) | ||
| The following code reads the private RSA key back in, and then prints again the public key: | ||
| @@ -82,10 +84,12 @@ The following code generates public key stored in ``receiver.pem`` and private k | ||
| private_key = key.export_key() | ||
| file_out = open("private.pem", "wb") | ||
| file_out.write(private_key) | ||
| file_out.close() | ||
| public_key = key.publickey().export_key() | ||
| file_out = open("receiver.pem", "wb") | ||
| file_out.write(public_key) | ||
| file_out.close() | ||
| Encrypt data with RSA | ||
| ~~~~~~~~~~~~~~~~~~~~~ | ||
| @@ -119,6 +123,7 @@ As in the first example, we use the EAX mode to allow detection of unauthorized | ||
| cipher_aes = AES.new(session_key, AES.MODE_EAX) | ||
| ciphertext, tag = cipher_aes.encrypt_and_digest(data) | ||
| [ file_out.write(x) for x in (enc_session_key, cipher_aes.nonce, tag, ciphertext) ] | ||
| file_out.close() | ||
| The receiver has the private RSA key. They will use it to decrypt the session key | ||
| first, and with that the rest of the file: | ||
| @@ -114,7 +114,7 @@ This KDF is not suitable for deriving keys from a password or for key stretching | ||
|
|
||
| Example, for deriving two AES256 keys:: | ||
|
|
||
| from Crypto.Protocol import HKDF | ||
| from Crypto.Protocol.KDF import HKDF | ||
| from Crypto.Hash import SHA512 | ||
| from Crypto.Random import get_random_bytes | ||
|
|
||
| @@ -1,12 +1,101 @@ | ||
| Secret Sharing Schemes | ||
| ====================== | ||
| This file implements secret sharing protocols. | ||
|
|
||
| In a *(k, n)* secret sharing protocol, a honest dealer breaks a secret | ||
| into multiple shares that are distributed amongst *n* players. | ||
| This module implements the Shamir's secret sharing protocol | ||
| described in the paper `"How to share a secret"`__. | ||
|
|
||
| The protocol guarantees that nobody can learn anything about the | ||
| secret, unless *k* players gather together to assemble their shares. | ||
| The secret can be split into an arbitrary number of shares (``n``), | ||
| such that it is sufficient to collect just ``k`` of them to reconstruct it (``k < n``). | ||
| For instance, one may want to grant 16 people the ability to access a system | ||
| with a pass code, at the condition that at least 3 of them are present at | ||
| the same time. As they join their shares, the pass code is revealed. | ||
| In that case, ``n=16`` and ``k=3``. | ||
|
|
||
| In the Shamir's secret sharing scheme, the ``n`` shares are created by first | ||
| defining a polynomial of degree ``k-1``: | ||
|
|
||
| :math:`q(x) = a_0 + a_1 x + a_2 x^2 + \ldots + a_{k-1} x^{k-1}` | ||
|
|
||
| The coefficient :math:`a_0` is fixed with the secret value. | ||
| The coefficients :math:`a_1 \ldots a_{k-1}` are random and they are discarded as soon as the shares are created. | ||
|
|
||
| Each share is a pair :math:`(x_i, y_i)`, where :math:`x_i` is an arbitrary | ||
| but unique number assigned to the share's recipient and :math:`y_i=q(x_i)`. | ||
|
|
||
| This implementation has the following properties: | ||
|
|
||
| * The secret is a byte string of 16 bytes (e.g. an AES 128 key). | ||
| * Each share is a byte string of 16 bytes. | ||
| * The recipients of the shares are assigned an integer starting from 1 (share number :math:`x_i`). | ||
| * The polynomial :math:`q(x)` is defined over the field GF(:math:`2^{128}`) with | ||
| the same irriducible polynomial as used in AES-GCM: :math:`1 + x + x^2 + x^7 + x^{128}`. | ||
| * It can be compatible with the popular `ssss`_ tool when used with the 128 bit security level | ||
| and no dispersion: the command line arguments must include ``-s 128 -D``. | ||
| Note that ``ssss`` uses a slightly different polynomial: | ||
|
|
||
| :math:`r(x) = a_0 + a_1 x + a_2 x^2 + \ldots + a_{k-1} x^{k-1} + x^k` | ||
|
|
||
| which requires you to specify ``ssss=True`` when calling ``split()`` and ``combine()``. | ||
|
|
||
| Each recipient needs to hold both the share number (:math:`x_i`, which is not confidential) and | ||
| the secret (which needs to be protected securely). | ||
|
|
||
| As an example, the following code shows how to protect a file meant | ||
| for 5 people, in such a way that any 2 of them are sufficient to | ||
| reassemble it:: | ||
|
|
||
| >>> from binascii import hexlify | ||
| >>> from Crypto.Cipher import AES | ||
| >>> from Crypto.Random import get_random_bytes | ||
| >>> from Crypto.Protocol.SecretSharing import Shamir | ||
| >>> | ||
| >>> key = get_random_bytes(16) | ||
| >>> shares = Shamir.split(2, 5, key) | ||
| >>> for idx, share in shares: | ||
| >>> print "Index #%d: %s" % (idx, hexlify(share)) | ||
| >>> | ||
| >>> with open("clear.txt", "rb") as fi, open("enc.txt", "wb") as fo: | ||
| >>> cipher = AES.new(key, AES.MODE_EAX) | ||
| >>> ct, tag = cipher.encrypt(fi.read()), cipher.digest() | ||
| >>> fo.write(nonce + tag + ct) | ||
|
|
||
| Each person can be given one share and the encrypted file. | ||
|
|
||
| When 2 people gather together with their shares, they can | ||
| decrypt the file:: | ||
|
|
||
| >>> from binascii import unhexlify | ||
| >>> from Crypto.Cipher import AES | ||
| >>> from Crypto.Protocol.SecretSharing import Shamir | ||
| >>> | ||
| >>> shares = [] | ||
| >>> for x in range(2): | ||
| >>> in_str = raw_input("Enter index and share separated by comma: ") | ||
| >>> idx, share = [ strip(s) for s in in_str.split(",") ] | ||
| >>> shares.append((idx, unhexlify(share))) | ||
| >>> key = Shamir.combine(shares) | ||
| >>> | ||
| >>> with open("enc.txt", "rb") as fi: | ||
| >>> nonce, tag = [ fi.read(16) for x in range(2) ] | ||
| >>> cipher = AES.new(key, AES.MODE_EAX, nonce) | ||
| >>> try: | ||
| >>> result = cipher.decrypt(fi.read()) | ||
| >>> cipher.verify(tag) | ||
| >>> with open("clear2.txt", "wb") as fo: | ||
| >>> fo.write(result) | ||
| >>> except ValueError: | ||
| >>> print "The shares were incorrect" | ||
|
|
||
| .. attention:: | ||
| Reconstruction may succeed but still produce the incorrect secret | ||
| if any of the presented shares is incorrect (due to data corruption | ||
| or to a malicious participant). | ||
|
|
||
| It is extremely important to also use an authentication mechanism | ||
| (such as the EAX cipher mode in the example). | ||
|
|
||
| .. __: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.80.8910&rep=rep1&type=pdf | ||
| .. _ssss: http://point-at-infinity.org/ssss/ | ||
|
|
||
| .. automodule:: Crypto.Protocol.SecretSharing | ||
| :members: |
| @@ -22,7 +22,7 @@ At the other end, the receiver can verify the signature (and therefore | ||
| the authenticity of the message) using the matching *public* RSA key:: | ||
|
|
||
| >>> key = RSA.import_key(open('public_key.der').read()) | ||
| >>> h = SHA.new(message) | ||
| >>> h = SHA256.new(message) | ||
| >>> try: | ||
| >>> pkcs1_15.new(key).verify(h, signature) | ||
| >>> print "The signature is valid." | ||
Binary file not shown.
| @@ -197,12 +197,12 @@ The simplest way to compile the *PyCryptodome* extensions from | ||
| source code is to install the minimum set of Visual Studio | ||
| components freely made available by Microsoft. | ||
|
|
||
| #. **[Once only]** Download `MS Visual Studio 2015 <https://www.visualstudio.com/en-us/downloads/download-visual-studio-vs.aspx>`_ | ||
| (Community Edition) and install the C/C++ compilers and the redistributable only. | ||
| #. **[Once only]** Download `Build Tools for Visual Studio 2019 <https://visualstudio.microsoft.com/downloads/#build-tools-for-visual-studio-2019>`_. | ||
| In the installer, select the *C++ build tools*, the *Windows 10 SDK*, and the latest version of *MSVC v142 x64/x86 build tools*. | ||
|
|
||
| #. Compile and install PyCryptodome:: | ||
|
|
||
| > pip install pycryptodomex --no-use-wheel | ||
| > pip install pycryptodomex --no-binary :all: | ||
|
|
||
| #. To make sure everything work fine, run the test suite:: | ||
|
|
||
| @@ -70,6 +70,6 @@ For more information, see the `homepage`_. | ||
|
|
||
| All the code can be downloaded from `GitHub`_. | ||
|
|
||
| .. _`homepage`: http://www.pycryptodome.org | ||
| .. _`homepage`: https://www.pycryptodome.org | ||
| .. _`GMP`: https://gmplib.org | ||
| .. _GitHub: https://github.com/Legrandin/pycryptodome | ||
| @@ -97,7 +97,8 @@ def new(key, mode, *args, **kwargs): | ||
| :param key: | ||
| The secret key to use in the symmetric cipher. | ||
| Its length can vary from 5 to 128 bytes. | ||
| Its length can vary from 5 to 128 bytes; the actual search space | ||
| (and the cipher strength) can be reduced with the ``effective_keylen`` parameter. | ||
| :type key: bytes, bytearray, memoryview | ||
| :param mode: | ||
| @@ -135,6 +136,12 @@ def new(key, mode, *args, **kwargs): | ||
| If not provided for ``MODE_EAX``, a random byte string is generated (you | ||
| can read it back via the ``nonce`` attribute). | ||
| * **effective_keylen** (*integer*) -- | ||
| Optional. Maximum strength in bits of the actual key used by the ARC2 algorithm. | ||
| If the supplied ``key`` parameter is longer (in bits) of the value specified | ||
| here, it will be weakened to match it. | ||
| If not specified, no limitation is applied. | ||
| * **segment_size** (*integer*) -- | ||
| (Only ``MODE_CFB``).The number of **bits** the plaintext and ciphertext | ||
| are segmented in. It must be a multiple of 8. | ||
| @@ -116,7 +116,7 @@ def new(key, mode, *args, **kwargs): | ||
| :param key: | ||
| The secret key to use in the symmetric cipher. | ||
| It must be 8 byte long. The parity bits will be ignored. | ||
| It must be 16 or 24 byte long. The parity bits will be ignored. | ||
| :type key: bytes/bytearray/memoryview | ||
| :param mode: | ||
| @@ -92,15 +92,15 @@ | ||
| int __gmpz_divisible_ui_p (const mpz_t n, UNIX_ULONG d); | ||
| """ | ||
|
|
||
| if sys.platform == "win32": | ||
| raise ImportError("Not using GMP on Windows") | ||
|
|
||
| lib = load_lib("gmp", gmp_defs) | ||
| implementation = {"library": "gmp", "api": backend} | ||
|
|
||
| if hasattr(lib, "__mpir_version"): | ||
| raise ImportError("MPIR library detected") | ||
|
|
||
| if sys.platform == "win32": | ||
| raise ImportError("Not using GMP on Windows") | ||
|
|
||
| # In order to create a function that returns a pointer to | ||
| # a new MPZ structure, we need to break the abstraction | ||
| # and know exactly what ffi backend we have | ||
Oops, something went wrong.