Skip to content

Commit

Permalink
Merge branch 'rsa_pkcs8_params'
Browse files Browse the repository at this point in the history
  • Loading branch information
Legrandin committed Jan 8, 2024
2 parents 3246f91 + df3d9a5 commit 03a66b2
Show file tree
Hide file tree
Showing 40 changed files with 910 additions and 226 deletions.
13 changes: 11 additions & 2 deletions Changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,24 @@ Under Development

New features
---------------
* Addde support for TurboSHAKE128 and TurboSHAKE256.
* Added support for TurboSHAKE128 and TurboSHAKE256.
* Added method ``Crypto.Hash.new()`` to generate a hash
object given a hash name.
* Added support for AES-GCM encryption of PBES2 and PKCS#8
containers.
* Added support for SHA-2 and SHA-3 algorithms in PBKDF2
when creating PBES2 and PKCS#8 containers.
* Export of RSA keys accepts the ``prot_params`` dictionary
as parameter to control the number of iterations for PBKDF2
and scrypt.

3.19.1 (28 December 2023)
++++++++++++++++++++++++++

Resolved issues
---------------
* Fixed a side-channel leakage with OAEP decryption that could be
exploited to carry out a Manger attack. Thanks to Hubert Kario.
exploited to carry out a Manger attack (CVE-2023-52323). Thanks to Hubert Kario.

3.19.0 (16 September 2023)
++++++++++++++++++++++++++
Expand Down
2 changes: 2 additions & 0 deletions Doc/src/cipher/oaep.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.. _rsa_oaep:

PKCS#1 OAEP (RSA)
=================

Expand Down
98 changes: 82 additions & 16 deletions Doc/src/io/pkcs8.rst
Original file line number Diff line number Diff line change
@@ -1,28 +1,94 @@
PKCS#8
======

`PKCS#8`_ is a standard for storing and transferring private key information.
The wrapped key can either be clear or encrypted.
`PKCS#8`_ is a standard for encoding asymmetric private keys,
such as RSA or ECC, so that they can be stored or exchanged.
The private key can either be encrypted with a passphrase or
left in the clear.

All encryption algorithms are based on passphrase-based key derivation.
The following mechanisms are fully supported:
Example of how to encrypt an ECC private key (even though
normally you would use the ``export_key`` method of the key itself)::

* *PBKDF2WithHMAC-SHA1AndAES128-CBC*
* *PBKDF2WithHMAC-SHA1AndAES192-CBC*
* *PBKDF2WithHMAC-SHA1AndAES256-CBC*
* *PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC*
* *scryptAndAES128-CBC*
* *scryptAndAES192-CBC*
* *scryptAndAES256-CBC*
from Crypto.PublicKey import ECC
from Crypto.IO import PKCS8

The following mechanisms are only supported for importing keys.
key = ECC.generate(curve='p256')
pkey = key.export_key(format='DER'),
passphrase = b'secret santa'
encrypted_key = PKCS8.wrap(
pkey,
"1.2.840.10045.2.1", # unrestricted ECC
passphrase=passphrase,
protection='PBKDF2WithHMAC-SHA512AndAES256-CBC',
prot_params={'iteration_count': 210000}
)

.. _enc_params:

Encryption parameters
-----------------------

When creating an encrypted PKCS#8 container, the two parameters
``protection`` and ``prot_params`` drive the encryption algorithm:

* ``protection`` (mandatory), a string that defines how the encryption
key is derived from the passphrase, and which cipher to use.
The string must follow one of the two patterns:

#. ``'PBKDF2WithHMAC-'`` + **hash** + ``'And'`` + **cipher**
#. ``'scryptAnd'`` + **cipher**

where **hash** is the name of the cryptographic hash
(recommended: ``'SHA512'``) and **cipher** is the name
of the cipher mode to use (recommended: ``'AES256-CBC'``).

Other values for **hash** are ``'SHA1'``, ``'SHA224'``, ``'SHA256'``,
``'SHA384'``, ``'SHA512-224'``, ``'SHA512-256'``, ``'SHA3-224'``,
``'SHA3-256'``, ``'SHA3-384'``, ``'SHA3-512'``.

Other values for **cipher** are
``'AES128-GCM'``, ``'AES192-GCM'``, ``'AES256-GCM'``,
``'AES128-CBC'``, ``'AES192-CBC'`` or ``'DES-EDE3-CBC'``.

* ``prot_params`` (optional), a dictionary to override the parameters of the
key derivation function:

+------------------+-----------------------------------------------+
| Key | Description |
+==================+===============================================+
| iteration_count | The KDF algorithm is repeated several times to|
| | slow down brute force attacks on passwords |
| | (called *N* or CPU/memory cost in scrypt). |
| | |
| | **For PBKDF2 with SHA512 the recommended |
| | value is 210 000** (default is 1 000). |
| | |
| | **For scrypt the recommended value is |
| | 131 072** (default value is 16 384). |
+------------------+-----------------------------------------------+
| salt_size | Salt is used to thwart dictionary and rainbow |
| | attacks on passwords. The default value is 8 |
| | bytes. |
+------------------+-----------------------------------------------+
| block_size | *(scrypt only)* Memory-cost (r). The default |
| | value is 8. |
+------------------+-----------------------------------------------+
| parallelization | *(scrypt only)* CPU-cost (p). The default |
| | value is 1. |
+------------------+-----------------------------------------------+


Legacy algorithms
-----------------

The following ``protection`` mechanisms are only supported for importing keys.
They are much weaker than the ones listed above, and they are provided
for backward compatibility only:

* *pbeWithMD5AndRC2-CBC*
* *pbeWithMD5AndDES-CBC*
* *pbeWithSHA1AndRC2-CBC*
* *pbeWithSHA1AndDES-CBC*
* ``pbeWithMD5AndRC2-CBC``
* ``pbeWithMD5AndDES-CBC``
* ``pbeWithSHA1AndRC2-CBC``
* ``pbeWithSHA1AndDES-CBC``

.. _`PKCS#8`: http://www.ietf.org/rfc/rfc5208.txt

Expand Down
44 changes: 28 additions & 16 deletions Doc/src/public_key/ecc.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,34 @@ is significantly smaller at the same security level.
For instance, a 3072-bit RSA key takes 768 bytes whereas the equally strong NIST P-256
private key only takes 32 bytes (that is, 256 bits).

This module provides mechanisms for generating new ECC keys, exporting and importing them
using widely supported formats like PEM or DER.
With this module you can generate new ECC keys::

>>> from Crypto.PublicKey import ECC
>>>
>>> mykey = ECC.generate(curve='p256')

export an ECC private key and protect it with a password, so that it is
resistant to brute force attacks::

>>> pwd = b'secret'
>>> with open("myprivatekey.pem", "wt") as f:
>>> data = mykey.export_key(format='PEM'
passphrase=pwd,
protection='PBKDF2WithHMAC-SHA512AndAES256-CBC',
prot_params={'iteration_count':131072})
>>> f.write(data)

and reimport it later::

>>> pwd = b'secret'
>>> with open("myprivatekey.pem", "rt") as f:
>>> data = f.read()
>>> mykey = ECC.import_key(data, pwd)

You can also export the public key, which is not sensitive::

>>> with open("mypublickey.pem", "wbt) as f:
>>> data = mykey.public_key().export_key()

.. _ECC table:
.. csv-table::
Expand All @@ -32,20 +58,6 @@ For more information about each NIST curve see `FIPS 186-4`_, Section D.1.2.

The Ed25519 and the Ed448 curves are defined in RFC8032_.

The following example demonstrates how to generate a new ECC key, export it,
and subsequently reload it back into the application::

>>> from Crypto.PublicKey import ECC
>>>
>>> key = ECC.generate(curve='P-256')
>>>
>>> f = open('myprivatekey.pem','wt')
>>> f.write(key.export_key(format='PEM'))
>>> f.close()
...
>>> f = open('myprivatekey.pem','rt')
>>> key = ECC.import_key(f.read())

The ECC key can be used to perform or verify signatures, using the modules
:mod:`Crypto.Signature.DSS` (ECDSA; NIST curves only)
or :mod:`Crypto.Signature.eddsa` (EdDSA; Ed25519 and Ed448 curve only).
Expand Down
4 changes: 2 additions & 2 deletions Doc/src/public_key/public_key.rst
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,12 @@ Available key types
:hidden:

rsa
dsa
ecc
dsa

* :doc:`RSA keys <rsa>`
* :doc:`DSA keys <dsa>`
* :doc:`Elliptic Curve keys <ecc>`
* :doc:`DSA keys (obsolete) <dsa>`

Obsolete key type
-----------------
Expand Down
63 changes: 41 additions & 22 deletions Doc/src/public_key/rsa.rst
Original file line number Diff line number Diff line change
@@ -1,40 +1,59 @@
RSA
===

RSA_ is the most widespread and used public key algorithm. Its security is
RSA_ is one of the most widespread and public key algorithms. Its security is
based on the difficulty of factoring large integers. The algorithm has
withstood attacks for more than 30 years, and it is therefore considered
reasonably secure for new designs.
reasonably secure.

The algorithm can be used for both confidentiality (encryption) and
authentication (digital signature). It is worth noting that signing and
However, for new designs, it is recommended to use :doc:`ECC <ecc>`,
because keys are smaller and private key operations are faster.

The RSA algorithm can be used for both confidentiality (encryption) and
authentication (digital signature). Signing and
decryption are significantly slower than verification and encryption.

The cryptographic strength is primarily linked to the length of the RSA modulus *n*.
In 2017, a sufficient length is deemed to be 2048 bits. For more information,
see the most recent ECRYPT_ report.

Both RSA ciphertexts and RSA signatures are as large as the RSA modulus *n* (256
bytes if *n* is 2048 bit long).

The module :mod:`Crypto.PublicKey.RSA` provides facilities for generating new RSA keys,
reconstructing them from known components, exporting them, and importing them.
In 2023, a sufficient length is deemed to be 3072 bits. For more information,
see the most recent NIST_ report.
Both RSA ciphertexts and RSA signatures are as large as the RSA modulus *n* (384
bytes if *n* is 3072 bit long).

As an example, this is how you generate a new RSA key pair, save it in a file
called ``mykey.pem``, and then read it back::
With this module you can generate new RSA keys::

>>> from Crypto.PublicKey import RSA
>>>
>>> key = RSA.generate(2048)
>>> f = open('mykey.pem','wb')
>>> f.write(key.export_key('PEM'))
>>> f.close()
...
>>> f = open('mykey.pem','r')
>>> key = RSA.import_key(f.read())
>>> mykey = RSA.generate(3072)

export an RSA private key and protect it with a password, so that it is
resistant to brute force attacks::

>>> pwd = b'secret'
>>> with open("myprivatekey.pem", "wb") as f:
>>> data = mykey.export_key(passphrase=pwd,
pkcs=8,
protection='PBKDF2WithHMAC-SHA512AndAES256-CBC',
prot_params={'iteration_count':131072})
>>> f.write(data)

and reimport it later::

>>> pwd = b'secret'
>>> with open("myprivatekey.pem", "rb") as f:
>>> data = f.read()
>>> mykey = RSA.import_key(data, pwd)

You can also export the public key, which is not sensitive::

>>> with open("mypublickey.pem", "wb") as f:
>>> data = mykey.public_key().export_key()

For signing data with RSA, use a higher level module such as :ref:`rsa_pss`.

For encrypting data with RSA, use :ref:`rsa_oaep`.

.. _RSA: http://en.wikipedia.org/wiki/RSA_%28algorithm%29
.. _ECRYPT: http://www.ecrypt.eu.org/ecrypt2/documents/D.SPA.20.pdf
.. _NIST: https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-57pt1r5.pdf

.. automodule:: Crypto.PublicKey.RSA
:members:
2 changes: 2 additions & 0 deletions Doc/src/signature/pkcs1_pss.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.. _rsa_pss:

PKCS#1 PSS (RSA)
================

Expand Down
27 changes: 26 additions & 1 deletion lib/Crypto/Hash/HMAC.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,25 +35,44 @@

from binascii import unhexlify

from Crypto.Hash import MD5
from Crypto.Hash import BLAKE2s
from Crypto.Util.strxor import strxor
from Crypto.Random import get_random_bytes

__all__ = ['new', 'HMAC']

_hash2hmac_oid = {
'1.3.14.3.2.26': '1.2.840.113549.2.7', # SHA-1
'2.16.840.1.101.3.4.2.4': '1.2.840.113549.2.8', # SHA-224
'2.16.840.1.101.3.4.2.1': '1.2.840.113549.2.9', # SHA-256
'2.16.840.1.101.3.4.2.2': '1.2.840.113549.2.10', # SHA-384
'2.16.840.1.101.3.4.2.3': '1.2.840.113549.2.11', # SHA-512
'2.16.840.1.101.3.4.2.5': '1.2.840.113549.2.12', # SHA-512_224
'2.16.840.1.101.3.4.2.6': '1.2.840.113549.2.13', # SHA-512_256
'2.16.840.1.101.3.4.2.7': '2.16.840.1.101.3.4.2.13', # SHA-3 224
'2.16.840.1.101.3.4.2.8': '2.16.840.1.101.3.4.2.14', # SHA-3 256
'2.16.840.1.101.3.4.2.9': '2.16.840.1.101.3.4.2.15', # SHA-3 384
'2.16.840.1.101.3.4.2.10': '2.16.840.1.101.3.4.2.16', # SHA-3 512
}

_hmac2hash_oid = {v: k for k, v in _hash2hmac_oid.items()}


class HMAC(object):
"""An HMAC hash object.
Do not instantiate directly. Use the :func:`new` function.
:ivar digest_size: the size in bytes of the resulting MAC tag
:vartype digest_size: integer
:ivar oid: the ASN.1 object ID of the HMAC algorithm.
Only present if the algorithm was officially assigned one.
"""

def __init__(self, key, msg=b"", digestmod=None):

if digestmod is None:
from Crypto.Hash import MD5
digestmod = MD5

if msg is None:
Expand All @@ -64,6 +83,12 @@ def __init__(self, key, msg=b"", digestmod=None):

self._digestmod = digestmod

# Hash OID --> HMAC OID
try:
self.oid = _hash2hmac_oid[digestmod.oid]
except (KeyError, AttributeError):
pass

if isinstance(key, memoryview):
key = key.tobytes()

Expand Down
Loading

0 comments on commit 03a66b2

Please sign in to comment.