summaryrefslogtreecommitdiff
path: root/src/cryptography/hazmat
diff options
context:
space:
mode:
Diffstat (limited to 'src/cryptography/hazmat')
-rw-r--r--src/cryptography/hazmat/_der.py156
-rw-r--r--src/cryptography/hazmat/_oid.py34
-rw-r--r--src/cryptography/hazmat/backends/__init__.py8
-rw-r--r--src/cryptography/hazmat/backends/interfaces.py9
-rw-r--r--src/cryptography/hazmat/backends/openssl/aead.py33
-rw-r--r--src/cryptography/hazmat/backends/openssl/backend.py1410
-rw-r--r--src/cryptography/hazmat/backends/openssl/ciphers.py134
-rw-r--r--src/cryptography/hazmat/backends/openssl/cmac.py32
-rw-r--r--src/cryptography/hazmat/backends/openssl/decode_asn1.py260
-rw-r--r--src/cryptography/hazmat/backends/openssl/dh.py123
-rw-r--r--src/cryptography/hazmat/backends/openssl/dsa.py39
-rw-r--r--src/cryptography/hazmat/backends/openssl/ec.py91
-rw-r--r--src/cryptography/hazmat/backends/openssl/ed25519.py145
-rw-r--r--src/cryptography/hazmat/backends/openssl/ed448.py146
-rw-r--r--src/cryptography/hazmat/backends/openssl/encode_asn1.py100
-rw-r--r--src/cryptography/hazmat/backends/openssl/hashes.py34
-rw-r--r--src/cryptography/hazmat/backends/openssl/hmac.py31
-rw-r--r--src/cryptography/hazmat/backends/openssl/ocsp.py74
-rw-r--r--src/cryptography/hazmat/backends/openssl/poly1305.py65
-rw-r--r--src/cryptography/hazmat/backends/openssl/rsa.py270
-rw-r--r--src/cryptography/hazmat/backends/openssl/utils.py15
-rw-r--r--src/cryptography/hazmat/backends/openssl/x25519.py74
-rw-r--r--src/cryptography/hazmat/backends/openssl/x448.py48
-rw-r--r--src/cryptography/hazmat/backends/openssl/x509.py157
-rw-r--r--src/cryptography/hazmat/bindings/_constant_time.sobin26832 -> 0 bytes
-rwxr-xr-x[-rw-r--r--]src/cryptography/hazmat/bindings/_openssl.sobin5709758 -> 2519552 bytes
-rwxr-xr-x[-rw-r--r--]src/cryptography/hazmat/bindings/_padding.sobin28951 -> 33048 bytes
-rw-r--r--src/cryptography/hazmat/bindings/openssl/_conditional.py235
-rw-r--r--src/cryptography/hazmat/bindings/openssl/binding.py108
-rw-r--r--src/cryptography/hazmat/primitives/asymmetric/dh.py46
-rw-r--r--src/cryptography/hazmat/primitives/asymmetric/dsa.py37
-rw-r--r--src/cryptography/hazmat/primitives/asymmetric/ec.py87
-rw-r--r--src/cryptography/hazmat/primitives/asymmetric/ed25519.py87
-rw-r--r--src/cryptography/hazmat/primitives/asymmetric/ed448.py82
-rw-r--r--src/cryptography/hazmat/primitives/asymmetric/padding.py9
-rw-r--r--src/cryptography/hazmat/primitives/asymmetric/rsa.py94
-rw-r--r--src/cryptography/hazmat/primitives/asymmetric/utils.py51
-rw-r--r--src/cryptography/hazmat/primitives/asymmetric/x25519.py11
-rw-r--r--src/cryptography/hazmat/primitives/asymmetric/x448.py9
-rw-r--r--src/cryptography/hazmat/primitives/ciphers/__init__.py9
-rw-r--r--src/cryptography/hazmat/primitives/ciphers/aead.py30
-rw-r--r--src/cryptography/hazmat/primitives/ciphers/algorithms.py11
-rw-r--r--src/cryptography/hazmat/primitives/ciphers/base.py22
-rw-r--r--src/cryptography/hazmat/primitives/ciphers/modes.py33
-rw-r--r--src/cryptography/hazmat/primitives/cmac.py21
-rw-r--r--src/cryptography/hazmat/primitives/constant_time.py29
-rw-r--r--src/cryptography/hazmat/primitives/hashes.py10
-rw-r--r--src/cryptography/hazmat/primitives/hmac.py15
-rw-r--r--src/cryptography/hazmat/primitives/kdf/concatkdf.py35
-rw-r--r--src/cryptography/hazmat/primitives/kdf/hkdf.py23
-rw-r--r--src/cryptography/hazmat/primitives/kdf/kbkdf.py45
-rw-r--r--src/cryptography/hazmat/primitives/kdf/pbkdf2.py20
-rw-r--r--src/cryptography/hazmat/primitives/kdf/scrypt.py11
-rw-r--r--src/cryptography/hazmat/primitives/kdf/x963kdf.py18
-rw-r--r--src/cryptography/hazmat/primitives/keywrap.py37
-rw-r--r--src/cryptography/hazmat/primitives/mac.py37
-rw-r--r--src/cryptography/hazmat/primitives/padding.py50
-rw-r--r--src/cryptography/hazmat/primitives/poly1305.py58
-rw-r--r--src/cryptography/hazmat/primitives/serialization/__init__.py40
-rw-r--r--src/cryptography/hazmat/primitives/serialization/base.py21
-rw-r--r--src/cryptography/hazmat/primitives/serialization/pkcs12.py43
-rw-r--r--src/cryptography/hazmat/primitives/serialization/pkcs7.py132
-rw-r--r--src/cryptography/hazmat/primitives/serialization/ssh.py731
-rw-r--r--src/cryptography/hazmat/primitives/twofactor/hotp.py23
-rw-r--r--src/cryptography/hazmat/primitives/twofactor/totp.py29
-rw-r--r--src/cryptography/hazmat/primitives/twofactor/utils.py7
66 files changed, 3933 insertions, 1951 deletions
diff --git a/src/cryptography/hazmat/_der.py b/src/cryptography/hazmat/_der.py
new file mode 100644
index 000000000..462b911b4
--- /dev/null
+++ b/src/cryptography/hazmat/_der.py
@@ -0,0 +1,156 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import absolute_import, division, print_function
+
+import six
+
+from cryptography.utils import int_from_bytes, int_to_bytes
+
+
+# This module contains a lightweight DER encoder and decoder. See X.690 for the
+# specification. This module intentionally does not implement the more complex
+# BER encoding, only DER.
+#
+# Note this implementation treats an element's constructed bit as part of the
+# tag. This is fine for DER, where the bit is always computable from the type.
+
+
+CONSTRUCTED = 0x20
+CONTEXT_SPECIFIC = 0x80
+
+INTEGER = 0x02
+BIT_STRING = 0x03
+OCTET_STRING = 0x04
+NULL = 0x05
+OBJECT_IDENTIFIER = 0x06
+SEQUENCE = 0x10 | CONSTRUCTED
+SET = 0x11 | CONSTRUCTED
+PRINTABLE_STRING = 0x13
+UTC_TIME = 0x17
+GENERALIZED_TIME = 0x18
+
+
+class DERReader(object):
+ def __init__(self, data):
+ self.data = memoryview(data)
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, exc_type, exc_value, tb):
+ if exc_value is None:
+ self.check_empty()
+
+ def is_empty(self):
+ return len(self.data) == 0
+
+ def check_empty(self):
+ if not self.is_empty():
+ raise ValueError("Invalid DER input: trailing data")
+
+ def read_byte(self):
+ if len(self.data) < 1:
+ raise ValueError("Invalid DER input: insufficient data")
+ ret = six.indexbytes(self.data, 0)
+ self.data = self.data[1:]
+ return ret
+
+ def read_bytes(self, n):
+ if len(self.data) < n:
+ raise ValueError("Invalid DER input: insufficient data")
+ ret = self.data[:n]
+ self.data = self.data[n:]
+ return ret
+
+ def read_any_element(self):
+ tag = self.read_byte()
+ # Tag numbers 31 or higher are stored in multiple bytes. No supported
+ # ASN.1 types use such tags, so reject these.
+ if tag & 0x1F == 0x1F:
+ raise ValueError("Invalid DER input: unexpected high tag number")
+ length_byte = self.read_byte()
+ if length_byte & 0x80 == 0:
+ # If the high bit is clear, the first length byte is the length.
+ length = length_byte
+ else:
+ # If the high bit is set, the first length byte encodes the length
+ # of the length.
+ length_byte &= 0x7F
+ if length_byte == 0:
+ raise ValueError(
+ "Invalid DER input: indefinite length form is not allowed "
+ "in DER"
+ )
+ length = 0
+ for i in range(length_byte):
+ length <<= 8
+ length |= self.read_byte()
+ if length == 0:
+ raise ValueError(
+ "Invalid DER input: length was not minimally-encoded"
+ )
+ if length < 0x80:
+ # If the length could have been encoded in short form, it must
+ # not use long form.
+ raise ValueError(
+ "Invalid DER input: length was not minimally-encoded"
+ )
+ body = self.read_bytes(length)
+ return tag, DERReader(body)
+
+ def read_element(self, expected_tag):
+ tag, body = self.read_any_element()
+ if tag != expected_tag:
+ raise ValueError("Invalid DER input: unexpected tag")
+ return body
+
+ def read_single_element(self, expected_tag):
+ with self:
+ return self.read_element(expected_tag)
+
+ def read_optional_element(self, expected_tag):
+ if len(self.data) > 0 and six.indexbytes(self.data, 0) == expected_tag:
+ return self.read_element(expected_tag)
+ return None
+
+ def as_integer(self):
+ if len(self.data) == 0:
+ raise ValueError("Invalid DER input: empty integer contents")
+ first = six.indexbytes(self.data, 0)
+ if first & 0x80 == 0x80:
+ raise ValueError("Negative DER integers are not supported")
+ # The first 9 bits must not all be zero or all be ones. Otherwise, the
+ # encoding should have been one byte shorter.
+ if len(self.data) > 1:
+ second = six.indexbytes(self.data, 1)
+ if first == 0 and second & 0x80 == 0:
+ raise ValueError(
+ "Invalid DER input: integer not minimally-encoded"
+ )
+ return int_from_bytes(self.data, "big")
+
+
+def encode_der_integer(x):
+ if not isinstance(x, six.integer_types):
+ raise ValueError("Value must be an integer")
+ if x < 0:
+ raise ValueError("Negative integers are not supported")
+ n = x.bit_length() // 8 + 1
+ return int_to_bytes(x, n)
+
+
+def encode_der(tag, *children):
+ length = 0
+ for child in children:
+ length += len(child)
+ chunks = [six.int2byte(tag)]
+ if length < 0x80:
+ chunks.append(six.int2byte(length))
+ else:
+ length_bytes = int_to_bytes(length)
+ chunks.append(six.int2byte(0x80 | len(length_bytes)))
+ chunks.append(length_bytes)
+ chunks.extend(children)
+ return b"".join(chunks)
diff --git a/src/cryptography/hazmat/_oid.py b/src/cryptography/hazmat/_oid.py
index cfe906cd3..de2771a73 100644
--- a/src/cryptography/hazmat/_oid.py
+++ b/src/cryptography/hazmat/_oid.py
@@ -19,26 +19,36 @@ class ObjectIdentifier(object):
# range 0..39. All nodes must be integers.
for node in nodes:
try:
- intnodes.append(int(node, 0))
+ node_value = int(node, 10)
except ValueError:
raise ValueError(
- "Malformed OID: %s (non-integer nodes)" % (
- self._dotted_string))
+ "Malformed OID: %s (non-integer nodes)"
+ % (self._dotted_string)
+ )
+ if node_value < 0:
+ raise ValueError(
+ "Malformed OID: %s (negative-integer nodes)"
+ % (self._dotted_string)
+ )
+ intnodes.append(node_value)
if len(nodes) < 2:
raise ValueError(
- "Malformed OID: %s (insufficient number of nodes)" % (
- self._dotted_string))
+ "Malformed OID: %s (insufficient number of nodes)"
+ % (self._dotted_string)
+ )
if intnodes[0] > 2:
raise ValueError(
- "Malformed OID: %s (first node outside valid range)" % (
- self._dotted_string))
+ "Malformed OID: %s (first node outside valid range)"
+ % (self._dotted_string)
+ )
if intnodes[0] < 2 and intnodes[1] >= 40:
raise ValueError(
- "Malformed OID: %s (second node outside valid range)" % (
- self._dotted_string))
+ "Malformed OID: %s (second node outside valid range)"
+ % (self._dotted_string)
+ )
def __eq__(self, other):
if not isinstance(other, ObjectIdentifier):
@@ -50,9 +60,8 @@ class ObjectIdentifier(object):
return not self == other
def __repr__(self):
- return "<ObjectIdentifier(oid={0}, name={1})>".format(
- self.dotted_string,
- self._name
+ return "<ObjectIdentifier(oid={}, name={})>".format(
+ self.dotted_string, self._name
)
def __hash__(self):
@@ -62,6 +71,7 @@ class ObjectIdentifier(object):
def _name(self):
# Lazy import to avoid an import cycle
from cryptography.x509.oid import _OID_NAMES
+
return _OID_NAMES.get(self, "Unknown OID")
dotted_string = utils.read_only_property("_dotted_string")
diff --git a/src/cryptography/hazmat/backends/__init__.py b/src/cryptography/hazmat/backends/__init__.py
index 565bde788..1563936dd 100644
--- a/src/cryptography/hazmat/backends/__init__.py
+++ b/src/cryptography/hazmat/backends/__init__.py
@@ -13,6 +13,14 @@ def default_backend():
if _default_backend is None:
from cryptography.hazmat.backends.openssl.backend import backend
+
_default_backend = backend
return _default_backend
+
+
+def _get_backend(backend):
+ if backend is None:
+ return default_backend()
+ else:
+ return backend
diff --git a/src/cryptography/hazmat/backends/interfaces.py b/src/cryptography/hazmat/backends/interfaces.py
index 0a476b991..418980a34 100644
--- a/src/cryptography/hazmat/backends/interfaces.py
+++ b/src/cryptography/hazmat/backends/interfaces.py
@@ -57,7 +57,7 @@ class HMACBackend(object):
@abc.abstractmethod
def create_hmac_ctx(self, key, algorithm):
"""
- Create a MACContext for calculating a message authentication code.
+ Create a context for calculating a message authentication code.
"""
@@ -72,7 +72,7 @@ class CMACBackend(object):
@abc.abstractmethod
def create_cmac_ctx(self, algorithm):
"""
- Create a MACContext for calculating a message authentication code.
+ Create a context for calculating a message authentication code.
"""
@@ -86,8 +86,9 @@ class PBKDF2HMACBackend(object):
"""
@abc.abstractmethod
- def derive_pbkdf2_hmac(self, algorithm, length, salt, iterations,
- key_material):
+ def derive_pbkdf2_hmac(
+ self, algorithm, length, salt, iterations, key_material
+ ):
"""
Return length bytes derived from provided PBKDF2 parameters.
"""
diff --git a/src/cryptography/hazmat/backends/openssl/aead.py b/src/cryptography/hazmat/backends/openssl/aead.py
index 73195ff3e..449491685 100644
--- a/src/cryptography/hazmat/backends/openssl/aead.py
+++ b/src/cryptography/hazmat/backends/openssl/aead.py
@@ -13,15 +13,18 @@ _DECRYPT = 0
def _aead_cipher_name(cipher):
from cryptography.hazmat.primitives.ciphers.aead import (
- AESCCM, AESGCM, ChaCha20Poly1305
+ AESCCM,
+ AESGCM,
+ ChaCha20Poly1305,
)
+
if isinstance(cipher, ChaCha20Poly1305):
return b"chacha20-poly1305"
elif isinstance(cipher, AESCCM):
- return "aes-{0}-ccm".format(len(cipher._key) * 8).encode("ascii")
+ return "aes-{}-ccm".format(len(cipher._key) * 8).encode("ascii")
else:
assert isinstance(cipher, AESGCM)
- return "aes-{0}-gcm".format(len(cipher._key) * 8).encode("ascii")
+ return "aes-{}-gcm".format(len(cipher._key) * 8).encode("ascii")
def _aead_setup(backend, cipher_name, key, nonce, tag, tag_len, operation):
@@ -30,18 +33,21 @@ def _aead_setup(backend, cipher_name, key, nonce, tag, tag_len, operation):
ctx = backend._lib.EVP_CIPHER_CTX_new()
ctx = backend._ffi.gc(ctx, backend._lib.EVP_CIPHER_CTX_free)
res = backend._lib.EVP_CipherInit_ex(
- ctx, evp_cipher,
+ ctx,
+ evp_cipher,
backend._ffi.NULL,
backend._ffi.NULL,
backend._ffi.NULL,
- int(operation == _ENCRYPT)
+ int(operation == _ENCRYPT),
)
backend.openssl_assert(res != 0)
res = backend._lib.EVP_CIPHER_CTX_set_key_length(ctx, len(key))
backend.openssl_assert(res != 0)
res = backend._lib.EVP_CIPHER_CTX_ctrl(
- ctx, backend._lib.EVP_CTRL_AEAD_SET_IVLEN, len(nonce),
- backend._ffi.NULL
+ ctx,
+ backend._lib.EVP_CTRL_AEAD_SET_IVLEN,
+ len(nonce),
+ backend._ffi.NULL,
)
backend.openssl_assert(res != 0)
if operation == _DECRYPT:
@@ -49,10 +55,11 @@ def _aead_setup(backend, cipher_name, key, nonce, tag, tag_len, operation):
ctx, backend._lib.EVP_CTRL_AEAD_SET_TAG, len(tag), tag
)
backend.openssl_assert(res != 0)
- else:
+ elif cipher_name.endswith(b"-ccm"):
res = backend._lib.EVP_CIPHER_CTX_ctrl(
ctx, backend._lib.EVP_CTRL_AEAD_SET_TAG, tag_len, backend._ffi.NULL
)
+ backend.openssl_assert(res != 0)
nonce_ptr = backend._ffi.from_buffer(nonce)
key_ptr = backend._ffi.from_buffer(key)
@@ -62,7 +69,7 @@ def _aead_setup(backend, cipher_name, key, nonce, tag, tag_len, operation):
backend._ffi.NULL,
key_ptr,
nonce_ptr,
- int(operation == _ENCRYPT)
+ int(operation == _ENCRYPT),
)
backend.openssl_assert(res != 0)
return ctx
@@ -71,11 +78,7 @@ def _aead_setup(backend, cipher_name, key, nonce, tag, tag_len, operation):
def _set_length(backend, ctx, data_len):
intptr = backend._ffi.new("int *")
res = backend._lib.EVP_CipherUpdate(
- ctx,
- backend._ffi.NULL,
- intptr,
- backend._ffi.NULL,
- data_len
+ ctx, backend._ffi.NULL, intptr, backend._ffi.NULL, data_len
)
backend.openssl_assert(res != 0)
@@ -98,6 +101,7 @@ def _process_data(backend, ctx, data):
def _encrypt(backend, cipher, nonce, data, associated_data, tag_length):
from cryptography.hazmat.primitives.ciphers.aead import AESCCM
+
cipher_name = _aead_cipher_name(cipher)
ctx = _aead_setup(
backend, cipher_name, cipher._key, nonce, None, tag_length, _ENCRYPT
@@ -125,6 +129,7 @@ def _encrypt(backend, cipher, nonce, data, associated_data, tag_length):
def _decrypt(backend, cipher, nonce, data, associated_data, tag_length):
from cryptography.hazmat.primitives.ciphers.aead import AESCCM
+
if len(data) < tag_length:
raise InvalidTag
tag = data[-tag_length:]
diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py
index 0a9bc53ae..45d4a1a1e 100644
--- a/src/cryptography/hazmat/backends/openssl/backend.py
+++ b/src/cryptography/hazmat/backends/openssl/backend.py
@@ -4,84 +4,166 @@
from __future__ import absolute_import, division, print_function
-import base64
import collections
import contextlib
import itertools
+import warnings
from contextlib import contextmanager
-import asn1crypto.core
-
import six
from six.moves import range
from cryptography import utils, x509
from cryptography.exceptions import UnsupportedAlgorithm, _Reasons
+from cryptography.hazmat._der import (
+ INTEGER,
+ NULL,
+ SEQUENCE,
+ encode_der,
+ encode_der_integer,
+)
from cryptography.hazmat.backends.interfaces import (
- CMACBackend, CipherBackend, DERSerializationBackend, DHBackend, DSABackend,
- EllipticCurveBackend, HMACBackend, HashBackend, PBKDF2HMACBackend,
- PEMSerializationBackend, RSABackend, ScryptBackend, X509Backend
+ CMACBackend,
+ CipherBackend,
+ DERSerializationBackend,
+ DHBackend,
+ DSABackend,
+ EllipticCurveBackend,
+ HMACBackend,
+ HashBackend,
+ PBKDF2HMACBackend,
+ PEMSerializationBackend,
+ RSABackend,
+ ScryptBackend,
+ X509Backend,
)
from cryptography.hazmat.backends.openssl import aead
from cryptography.hazmat.backends.openssl.ciphers import _CipherContext
from cryptography.hazmat.backends.openssl.cmac import _CMACContext
from cryptography.hazmat.backends.openssl.decode_asn1 import (
- _CRL_ENTRY_REASON_ENUM_TO_CODE, _Integers
+ _CRL_ENTRY_REASON_ENUM_TO_CODE,
+ _CRL_EXTENSION_HANDLERS,
+ _EXTENSION_HANDLERS_BASE,
+ _EXTENSION_HANDLERS_SCT,
+ _OCSP_BASICRESP_EXTENSION_HANDLERS,
+ _OCSP_REQ_EXTENSION_HANDLERS,
+ _OCSP_SINGLERESP_EXTENSION_HANDLERS_SCT,
+ _REVOKED_EXTENSION_HANDLERS,
+ _X509ExtensionParser,
)
from cryptography.hazmat.backends.openssl.dh import (
- _DHParameters, _DHPrivateKey, _DHPublicKey, _dh_params_dup
+ _DHParameters,
+ _DHPrivateKey,
+ _DHPublicKey,
+ _dh_params_dup,
)
from cryptography.hazmat.backends.openssl.dsa import (
- _DSAParameters, _DSAPrivateKey, _DSAPublicKey
+ _DSAParameters,
+ _DSAPrivateKey,
+ _DSAPublicKey,
)
from cryptography.hazmat.backends.openssl.ec import (
- _EllipticCurvePrivateKey, _EllipticCurvePublicKey
+ _EllipticCurvePrivateKey,
+ _EllipticCurvePublicKey,
+)
+from cryptography.hazmat.backends.openssl.ed25519 import (
+ _Ed25519PrivateKey,
+ _Ed25519PublicKey,
+)
+from cryptography.hazmat.backends.openssl.ed448 import (
+ _ED448_KEY_SIZE,
+ _Ed448PrivateKey,
+ _Ed448PublicKey,
)
from cryptography.hazmat.backends.openssl.encode_asn1 import (
_CRL_ENTRY_EXTENSION_ENCODE_HANDLERS,
- _CRL_EXTENSION_ENCODE_HANDLERS, _EXTENSION_ENCODE_HANDLERS,
+ _CRL_EXTENSION_ENCODE_HANDLERS,
+ _EXTENSION_ENCODE_HANDLERS,
_OCSP_BASICRESP_EXTENSION_ENCODE_HANDLERS,
_OCSP_REQUEST_EXTENSION_ENCODE_HANDLERS,
- _encode_asn1_int_gc, _encode_asn1_str_gc, _encode_name_gc, _txt2obj_gc,
+ _encode_asn1_int_gc,
+ _encode_asn1_str_gc,
+ _encode_name_gc,
+ _txt2obj_gc,
)
from cryptography.hazmat.backends.openssl.hashes import _HashContext
from cryptography.hazmat.backends.openssl.hmac import _HMACContext
from cryptography.hazmat.backends.openssl.ocsp import (
- _OCSPRequest, _OCSPResponse
+ _OCSPRequest,
+ _OCSPResponse,
+)
+from cryptography.hazmat.backends.openssl.poly1305 import (
+ _POLY1305_KEY_SIZE,
+ _Poly1305Context,
)
from cryptography.hazmat.backends.openssl.rsa import (
- _RSAPrivateKey, _RSAPublicKey
+ _RSAPrivateKey,
+ _RSAPublicKey,
)
from cryptography.hazmat.backends.openssl.x25519 import (
- _X25519PrivateKey, _X25519PublicKey
+ _X25519PrivateKey,
+ _X25519PublicKey,
)
from cryptography.hazmat.backends.openssl.x448 import (
- _X448PrivateKey, _X448PublicKey
+ _X448PrivateKey,
+ _X448PublicKey,
)
from cryptography.hazmat.backends.openssl.x509 import (
- _Certificate, _CertificateRevocationList,
- _CertificateSigningRequest, _RevokedCertificate
+ _Certificate,
+ _CertificateRevocationList,
+ _CertificateSigningRequest,
+ _RevokedCertificate,
)
from cryptography.hazmat.bindings.openssl import binding
from cryptography.hazmat.primitives import hashes, serialization
-from cryptography.hazmat.primitives.asymmetric import dsa, ec, rsa
+from cryptography.hazmat.primitives.asymmetric import (
+ dh,
+ dsa,
+ ec,
+ ed25519,
+ ed448,
+ rsa,
+)
from cryptography.hazmat.primitives.asymmetric.padding import (
- MGF1, OAEP, PKCS1v15, PSS
+ MGF1,
+ OAEP,
+ PKCS1v15,
+ PSS,
)
from cryptography.hazmat.primitives.ciphers.algorithms import (
- AES, ARC4, Blowfish, CAST5, Camellia, ChaCha20, IDEA, SEED, TripleDES
+ AES,
+ ARC4,
+ Blowfish,
+ CAST5,
+ Camellia,
+ ChaCha20,
+ IDEA,
+ SEED,
+ TripleDES,
)
from cryptography.hazmat.primitives.ciphers.modes import (
- CBC, CFB, CFB8, CTR, ECB, GCM, OFB, XTS
+ CBC,
+ CFB,
+ CFB8,
+ CTR,
+ ECB,
+ GCM,
+ OFB,
+ XTS,
)
from cryptography.hazmat.primitives.kdf import scrypt
-from cryptography.hazmat.primitives.serialization import ssh
+from cryptography.hazmat.primitives.serialization import pkcs7, ssh
from cryptography.x509 import ocsp
_MemoryBIO = collections.namedtuple("_MemoryBIO", ["bio", "char_ptr"])
+# Not actually supported, just used as a marker for some serialization tests.
+class _RC2(object):
+ pass
+
+
@utils.register_interface(CipherBackend)
@utils.register_interface(CMACBackend)
@utils.register_interface(DERSerializationBackend)
@@ -101,39 +183,93 @@ class Backend(object):
"""
OpenSSL API binding interfaces.
"""
+
name = "openssl"
+ # FIPS has opinions about acceptable algorithms and key sizes, but the
+ # disallowed algorithms are still present in OpenSSL. They just error if
+ # you try to use them. To avoid that we allowlist the algorithms in
+ # FIPS 140-3. This isn't ideal, but FIPS 140-3 is trash so here we are.
+ _fips_aead = {
+ b"aes-128-ccm",
+ b"aes-192-ccm",
+ b"aes-256-ccm",
+ b"aes-128-gcm",
+ b"aes-192-gcm",
+ b"aes-256-gcm",
+ }
+ _fips_ciphers = (AES, TripleDES)
+ _fips_hashes = (
+ hashes.SHA1,
+ hashes.SHA224,
+ hashes.SHA256,
+ hashes.SHA384,
+ hashes.SHA512,
+ hashes.SHA512_224,
+ hashes.SHA512_256,
+ hashes.SHA3_224,
+ hashes.SHA3_256,
+ hashes.SHA3_384,
+ hashes.SHA3_512,
+ hashes.SHAKE128,
+ hashes.SHAKE256,
+ )
+ _fips_rsa_min_key_size = 2048
+ _fips_rsa_min_public_exponent = 65537
+ _fips_dsa_min_modulus = 1 << 2048
+ _fips_dh_min_key_size = 2048
+ _fips_dh_min_modulus = 1 << _fips_dh_min_key_size
+
def __init__(self):
self._binding = binding.Binding()
self._ffi = self._binding.ffi
self._lib = self._binding.lib
+ self._fips_enabled = self._is_fips_enabled()
self._cipher_registry = {}
self._register_default_ciphers()
- self.activate_osrandom_engine()
+ self._register_x509_ext_parsers()
+ self._register_x509_encoders()
+ if self._fips_enabled and self._lib.CRYPTOGRAPHY_NEEDS_OSRANDOM_ENGINE:
+ warnings.warn(
+ "OpenSSL FIPS mode is enabled. Can't enable DRBG fork safety.",
+ UserWarning,
+ )
+ else:
+ self.activate_osrandom_engine()
self._dh_types = [self._lib.EVP_PKEY_DH]
if self._lib.Cryptography_HAS_EVP_PKEY_DHX:
self._dh_types.append(self._lib.EVP_PKEY_DHX)
- def openssl_assert(self, ok):
- return binding._openssl_assert(self._lib, ok)
+ def openssl_assert(self, ok, errors=None):
+ return binding._openssl_assert(self._lib, ok, errors=errors)
+
+ def _is_fips_enabled(self):
+ fips_mode = getattr(self._lib, "FIPS_mode", lambda: 0)
+ mode = fips_mode()
+ if mode == 0:
+ # OpenSSL without FIPS pushes an error on the error stack
+ self._lib.ERR_clear_error()
+ return bool(mode)
def activate_builtin_random(self):
- # Obtain a new structural reference.
- e = self._lib.ENGINE_get_default_RAND()
- if e != self._ffi.NULL:
- self._lib.ENGINE_unregister_RAND(e)
- # Reset the RNG to use the new engine.
- self._lib.RAND_cleanup()
- # decrement the structural reference from get_default_RAND
- res = self._lib.ENGINE_finish(e)
- self.openssl_assert(res == 1)
+ if self._lib.CRYPTOGRAPHY_NEEDS_OSRANDOM_ENGINE:
+ # Obtain a new structural reference.
+ e = self._lib.ENGINE_get_default_RAND()
+ if e != self._ffi.NULL:
+ self._lib.ENGINE_unregister_RAND(e)
+ # Reset the RNG to use the built-in.
+ res = self._lib.RAND_set_rand_method(self._ffi.NULL)
+ self.openssl_assert(res == 1)
+ # decrement the structural reference from get_default_RAND
+ res = self._lib.ENGINE_finish(e)
+ self.openssl_assert(res == 1)
@contextlib.contextmanager
def _get_osurandom_engine(self):
# Fetches an engine by id and returns it. This creates a structural
# reference.
- e = self._lib.ENGINE_by_id(self._binding._osrandom_engine_id)
+ e = self._lib.ENGINE_by_id(self._lib.Cryptography_osrandom_engine_id)
self.openssl_assert(e != self._ffi.NULL)
# Initialize the engine for use. This adds a functional reference.
res = self._lib.ENGINE_init(e)
@@ -150,30 +286,32 @@ class Backend(object):
self.openssl_assert(res == 1)
def activate_osrandom_engine(self):
- # Unregister and free the current engine.
- self.activate_builtin_random()
- with self._get_osurandom_engine() as e:
- # Set the engine as the default RAND provider.
- res = self._lib.ENGINE_set_default_RAND(e)
+ if self._lib.CRYPTOGRAPHY_NEEDS_OSRANDOM_ENGINE:
+ # Unregister and free the current engine.
+ self.activate_builtin_random()
+ with self._get_osurandom_engine() as e:
+ # Set the engine as the default RAND provider.
+ res = self._lib.ENGINE_set_default_RAND(e)
+ self.openssl_assert(res == 1)
+ # Reset the RNG to use the engine
+ res = self._lib.RAND_set_rand_method(self._ffi.NULL)
self.openssl_assert(res == 1)
- # Reset the RNG to use the new engine.
- self._lib.RAND_cleanup()
def osrandom_engine_implementation(self):
buf = self._ffi.new("char[]", 64)
with self._get_osurandom_engine() as e:
- res = self._lib.ENGINE_ctrl_cmd(e, b"get_implementation",
- len(buf), buf,
- self._ffi.NULL, 0)
+ res = self._lib.ENGINE_ctrl_cmd(
+ e, b"get_implementation", len(buf), buf, self._ffi.NULL, 0
+ )
self.openssl_assert(res > 0)
- return self._ffi.string(buf).decode('ascii')
+ return self._ffi.string(buf).decode("ascii")
def openssl_version_text(self):
"""
Friendly string name of the loaded OpenSSL library. This is not
necessarily the same version as it was compiled against.
- Example: OpenSSL 1.0.1e 11 Feb 2013
+ Example: OpenSSL 1.1.1d 10 Sep 2019
"""
return self._ffi.string(
self._lib.OpenSSL_version(self._lib.OPENSSL_VERSION)
@@ -187,7 +325,7 @@ class Backend(object):
def _evp_md_from_algorithm(self, algorithm):
if algorithm.name == "blake2b" or algorithm.name == "blake2s":
- alg = "{0}{1}".format(
+ alg = "{}{}".format(
algorithm.name, algorithm.digest_size * 8
).encode("ascii")
else:
@@ -202,6 +340,9 @@ class Backend(object):
return evp_md
def hash_supported(self, algorithm):
+ if self._fips_enabled and not isinstance(algorithm, self._fips_hashes):
+ return False
+
evp_md = self._evp_md_from_algorithm(algorithm)
return evp_md != self._ffi.NULL
@@ -212,6 +353,8 @@ class Backend(object):
return _HashContext(self, algorithm)
def cipher_supported(self, cipher, mode):
+ if self._fips_enabled and not isinstance(cipher, self._fips_ciphers):
+ return False
try:
adapter = self._cipher_registry[type(cipher), type(mode)]
except KeyError:
@@ -221,8 +364,10 @@ class Backend(object):
def register_cipher_adapter(self, cipher_cls, mode_cls, adapter):
if (cipher_cls, mode_cls) in self._cipher_registry:
- raise ValueError("Duplicate registration for: {0} {1}.".format(
- cipher_cls, mode_cls)
+ raise ValueError(
+ "Duplicate registration for: {} {}.".format(
+ cipher_cls, mode_cls
+ )
)
self._cipher_registry[cipher_cls, mode_cls] = adapter
@@ -231,36 +376,28 @@ class Backend(object):
self.register_cipher_adapter(
AES,
mode_cls,
- GetCipherByName("{cipher.name}-{cipher.key_size}-{mode.name}")
+ GetCipherByName("{cipher.name}-{cipher.key_size}-{mode.name}"),
)
for mode_cls in [CBC, CTR, ECB, OFB, CFB]:
self.register_cipher_adapter(
Camellia,
mode_cls,
- GetCipherByName("{cipher.name}-{cipher.key_size}-{mode.name}")
+ GetCipherByName("{cipher.name}-{cipher.key_size}-{mode.name}"),
)
for mode_cls in [CBC, CFB, CFB8, OFB]:
self.register_cipher_adapter(
- TripleDES,
- mode_cls,
- GetCipherByName("des-ede3-{mode.name}")
+ TripleDES, mode_cls, GetCipherByName("des-ede3-{mode.name}")
)
self.register_cipher_adapter(
- TripleDES,
- ECB,
- GetCipherByName("des-ede3")
+ TripleDES, ECB, GetCipherByName("des-ede3")
)
for mode_cls in [CBC, CFB, OFB, ECB]:
self.register_cipher_adapter(
- Blowfish,
- mode_cls,
- GetCipherByName("bf-{mode.name}")
+ Blowfish, mode_cls, GetCipherByName("bf-{mode.name}")
)
for mode_cls in [CBC, CFB, OFB, ECB]:
self.register_cipher_adapter(
- SEED,
- mode_cls,
- GetCipherByName("seed-{mode.name}")
+ SEED, mode_cls, GetCipherByName("seed-{mode.name}")
)
for cipher_cls, mode_cls in itertools.product(
[CAST5, IDEA],
@@ -269,20 +406,84 @@ class Backend(object):
self.register_cipher_adapter(
cipher_cls,
mode_cls,
- GetCipherByName("{cipher.name}-{mode.name}")
+ GetCipherByName("{cipher.name}-{mode.name}"),
)
+ self.register_cipher_adapter(ARC4, type(None), GetCipherByName("rc4"))
+ # We don't actually support RC2, this is just used by some tests.
+ self.register_cipher_adapter(_RC2, type(None), GetCipherByName("rc2"))
self.register_cipher_adapter(
- ARC4,
- type(None),
- GetCipherByName("rc4")
- )
- self.register_cipher_adapter(
- ChaCha20,
- type(None),
- GetCipherByName("chacha20")
+ ChaCha20, type(None), GetCipherByName("chacha20")
)
self.register_cipher_adapter(AES, XTS, _get_xts_cipher)
+ def _register_x509_ext_parsers(self):
+ ext_handlers = _EXTENSION_HANDLERS_BASE.copy()
+ # All revoked extensions are valid single response extensions, see:
+ # https://tools.ietf.org/html/rfc6960#section-4.4.5
+ singleresp_handlers = _REVOKED_EXTENSION_HANDLERS.copy()
+
+ if self._lib.Cryptography_HAS_SCT:
+ ext_handlers.update(_EXTENSION_HANDLERS_SCT)
+ singleresp_handlers.update(_OCSP_SINGLERESP_EXTENSION_HANDLERS_SCT)
+
+ self._certificate_extension_parser = _X509ExtensionParser(
+ self,
+ ext_count=self._lib.X509_get_ext_count,
+ get_ext=self._lib.X509_get_ext,
+ handlers=ext_handlers,
+ )
+ self._csr_extension_parser = _X509ExtensionParser(
+ self,
+ ext_count=self._lib.sk_X509_EXTENSION_num,
+ get_ext=self._lib.sk_X509_EXTENSION_value,
+ handlers=ext_handlers,
+ )
+ self._revoked_cert_extension_parser = _X509ExtensionParser(
+ self,
+ ext_count=self._lib.X509_REVOKED_get_ext_count,
+ get_ext=self._lib.X509_REVOKED_get_ext,
+ handlers=_REVOKED_EXTENSION_HANDLERS,
+ )
+ self._crl_extension_parser = _X509ExtensionParser(
+ self,
+ ext_count=self._lib.X509_CRL_get_ext_count,
+ get_ext=self._lib.X509_CRL_get_ext,
+ handlers=_CRL_EXTENSION_HANDLERS,
+ )
+ self._ocsp_req_ext_parser = _X509ExtensionParser(
+ self,
+ ext_count=self._lib.OCSP_REQUEST_get_ext_count,
+ get_ext=self._lib.OCSP_REQUEST_get_ext,
+ handlers=_OCSP_REQ_EXTENSION_HANDLERS,
+ )
+ self._ocsp_basicresp_ext_parser = _X509ExtensionParser(
+ self,
+ ext_count=self._lib.OCSP_BASICRESP_get_ext_count,
+ get_ext=self._lib.OCSP_BASICRESP_get_ext,
+ handlers=_OCSP_BASICRESP_EXTENSION_HANDLERS,
+ )
+ self._ocsp_singleresp_ext_parser = _X509ExtensionParser(
+ self,
+ ext_count=self._lib.OCSP_SINGLERESP_get_ext_count,
+ get_ext=self._lib.OCSP_SINGLERESP_get_ext,
+ handlers=singleresp_handlers,
+ )
+
+ def _register_x509_encoders(self):
+ self._extension_encode_handlers = _EXTENSION_ENCODE_HANDLERS.copy()
+ self._crl_extension_encode_handlers = (
+ _CRL_EXTENSION_ENCODE_HANDLERS.copy()
+ )
+ self._crl_entry_extension_encode_handlers = (
+ _CRL_ENTRY_EXTENSION_ENCODE_HANDLERS.copy()
+ )
+ self._ocsp_request_extension_encode_handlers = (
+ _OCSP_REQUEST_EXTENSION_ENCODE_HANDLERS.copy()
+ )
+ self._ocsp_basicresp_extension_encode_handlers = (
+ _OCSP_BASICRESP_EXTENSION_ENCODE_HANDLERS.copy()
+ )
+
def create_symmetric_encryption_ctx(self, cipher, mode):
return _CipherContext(self, cipher, mode, _CipherContext._ENCRYPT)
@@ -292,8 +493,9 @@ class Backend(object):
def pbkdf2_hmac_supported(self, algorithm):
return self.hmac_supported(algorithm)
- def derive_pbkdf2_hmac(self, algorithm, length, salt, iterations,
- key_material):
+ def derive_pbkdf2_hmac(
+ self, algorithm, length, salt, iterations, key_material
+ ):
buf = self._ffi.new("unsigned char[]", length)
evp_md = self._evp_md_non_null_from_algorithm(algorithm)
key_material_ptr = self._ffi.from_buffer(key_material)
@@ -305,7 +507,7 @@ class Backend(object):
iterations,
evp_md,
length,
- buf
+ buf,
)
self.openssl_assert(res == 1)
return self._ffi.buffer(buf)[:]
@@ -313,6 +515,9 @@ class Backend(object):
def _consume_errors(self):
return binding._consume_errors(self._lib)
+ def _consume_errors_with_text(self):
+ return binding._consume_errors_with_text(self._lib)
+
def _bn_to_int(self, bn):
assert bn != self._ffi.NULL
@@ -323,7 +528,10 @@ class Backend(object):
bin_len = self._lib.BN_bn2bin(bn, bin_ptr)
# A zero length means the BN has value 0
self.openssl_assert(bin_len >= 0)
- return int.from_bytes(self._ffi.buffer(bin_ptr)[:bin_len], "big")
+ val = int.from_bytes(self._ffi.buffer(bin_ptr)[:bin_len], "big")
+ if self._lib.BN_is_negative(bn):
+ val = -val
+ return val
else:
# Under Python 2 the best we can do is hex()
hex_cdata = self._lib.BN_bn2hex(bn)
@@ -382,8 +590,11 @@ class Backend(object):
return _RSAPrivateKey(self, rsa_cdata, evp_pkey)
def generate_rsa_parameters_supported(self, public_exponent, key_size):
- return (public_exponent >= 3 and public_exponent & 1 != 0 and
- key_size >= 512)
+ return (
+ public_exponent >= 3
+ and public_exponent & 1 != 0
+ and key_size >= 512
+ )
def load_rsa_private_numbers(self, numbers):
rsa._check_private_key_components(
@@ -394,7 +605,7 @@ class Backend(object):
numbers.dmq1,
numbers.iqmp,
numbers.public_numbers.e,
- numbers.public_numbers.n
+ numbers.public_numbers.n,
)
rsa_cdata = self._lib.RSA_new()
self.openssl_assert(rsa_cdata != self._ffi.NULL)
@@ -413,8 +624,6 @@ class Backend(object):
self.openssl_assert(res == 1)
res = self._lib.RSA_set0_crt_params(rsa_cdata, dmp1, dmq1, iqmp)
self.openssl_assert(res == 1)
- res = self._lib.RSA_blinding_on(rsa_cdata, self._ffi.NULL)
- self.openssl_assert(res == 1)
evp_pkey = self._rsa_cdata_to_evp_pkey(rsa_cdata)
return _RSAPrivateKey(self, rsa_cdata, evp_pkey)
@@ -452,9 +661,7 @@ class Backend(object):
BIO is finished with.
"""
data_ptr = self._ffi.from_buffer(data)
- bio = self._lib.BIO_new_mem_buf(
- data_ptr, len(data)
- )
+ bio = self._lib.BIO_new_mem_buf(data_ptr, len(data))
self.openssl_assert(bio != self._ffi.NULL)
return _MemoryBIO(self._ffi.gc(bio, self._lib.BIO_free), data_ptr)
@@ -509,12 +716,18 @@ class Backend(object):
self.openssl_assert(dh_cdata != self._ffi.NULL)
dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free)
return _DHPrivateKey(self, dh_cdata, evp_pkey)
+ elif key_type == getattr(self._lib, "EVP_PKEY_ED25519", None):
+ # EVP_PKEY_ED25519 is not present in OpenSSL < 1.1.1
+ return _Ed25519PrivateKey(self, evp_pkey)
elif key_type == getattr(self._lib, "EVP_PKEY_X448", None):
# EVP_PKEY_X448 is not present in OpenSSL < 1.1.1
return _X448PrivateKey(self, evp_pkey)
elif key_type == getattr(self._lib, "EVP_PKEY_X25519", None):
# EVP_PKEY_X25519 is not present in OpenSSL < 1.1.0
return _X25519PrivateKey(self, evp_pkey)
+ elif key_type == getattr(self._lib, "EVP_PKEY_ED448", None):
+ # EVP_PKEY_ED448 is not present in OpenSSL < 1.1.1
+ return _Ed448PrivateKey(self, evp_pkey)
else:
raise UnsupportedAlgorithm("Unsupported key type.")
@@ -546,25 +759,32 @@ class Backend(object):
self.openssl_assert(dh_cdata != self._ffi.NULL)
dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free)
return _DHPublicKey(self, dh_cdata, evp_pkey)
+ elif key_type == getattr(self._lib, "EVP_PKEY_ED25519", None):
+ # EVP_PKEY_ED25519 is not present in OpenSSL < 1.1.1
+ return _Ed25519PublicKey(self, evp_pkey)
elif key_type == getattr(self._lib, "EVP_PKEY_X448", None):
# EVP_PKEY_X448 is not present in OpenSSL < 1.1.1
return _X448PublicKey(self, evp_pkey)
elif key_type == getattr(self._lib, "EVP_PKEY_X25519", None):
# EVP_PKEY_X25519 is not present in OpenSSL < 1.1.0
return _X25519PublicKey(self, evp_pkey)
+ elif key_type == getattr(self._lib, "EVP_PKEY_ED448", None):
+ # EVP_PKEY_X25519 is not present in OpenSSL < 1.1.1
+ return _Ed448PublicKey(self, evp_pkey)
else:
raise UnsupportedAlgorithm("Unsupported key type.")
def _oaep_hash_supported(self, algorithm):
if self._lib.Cryptography_HAS_RSA_OAEP_MD:
return isinstance(
- algorithm, (
+ algorithm,
+ (
hashes.SHA1,
hashes.SHA224,
hashes.SHA256,
hashes.SHA384,
hashes.SHA512,
- )
+ ),
)
else:
return isinstance(algorithm, hashes.SHA1)
@@ -576,27 +796,34 @@ class Backend(object):
return self.hash_supported(padding._mgf._algorithm)
elif isinstance(padding, OAEP) and isinstance(padding._mgf, MGF1):
return (
- self._oaep_hash_supported(padding._mgf._algorithm) and
- self._oaep_hash_supported(padding._algorithm) and
- (
- (padding._label is None or len(padding._label) == 0) or
- self._lib.Cryptography_HAS_RSA_OAEP_LABEL == 1
+ self._oaep_hash_supported(padding._mgf._algorithm)
+ and self._oaep_hash_supported(padding._algorithm)
+ and (
+ (padding._label is None or len(padding._label) == 0)
+ or self._lib.Cryptography_HAS_RSA_OAEP_LABEL == 1
)
)
else:
return False
def generate_dsa_parameters(self, key_size):
- if key_size not in (1024, 2048, 3072):
- raise ValueError("Key size must be 1024 or 2048 or 3072 bits.")
+ if key_size not in (1024, 2048, 3072, 4096):
+ raise ValueError(
+ "Key size must be 1024, 2048, 3072, or 4096 bits."
+ )
ctx = self._lib.DSA_new()
self.openssl_assert(ctx != self._ffi.NULL)
ctx = self._ffi.gc(ctx, self._lib.DSA_free)
res = self._lib.DSA_generate_parameters_ex(
- ctx, key_size, self._ffi.NULL, 0,
- self._ffi.NULL, self._ffi.NULL, self._ffi.NULL
+ ctx,
+ key_size,
+ self._ffi.NULL,
+ 0,
+ self._ffi.NULL,
+ self._ffi.NULL,
+ self._ffi.NULL,
)
self.openssl_assert(res == 1)
@@ -692,20 +919,37 @@ class Backend(object):
def create_cmac_ctx(self, algorithm):
return _CMACContext(self, algorithm)
- def create_x509_csr(self, builder, private_key, algorithm):
- if not isinstance(algorithm, hashes.HashAlgorithm):
- raise TypeError('Algorithm must be a registered hash algorithm.')
-
- if (
- isinstance(algorithm, hashes.MD5) and not
- isinstance(private_key, rsa.RSAPrivateKey)
+ def _x509_check_signature_params(self, private_key, algorithm):
+ if isinstance(
+ private_key, (ed25519.Ed25519PrivateKey, ed448.Ed448PrivateKey)
+ ):
+ if algorithm is not None:
+ raise ValueError(
+ "algorithm must be None when signing via ed25519 or ed448"
+ )
+ elif not isinstance(
+ private_key,
+ (rsa.RSAPrivateKey, dsa.DSAPrivateKey, ec.EllipticCurvePrivateKey),
+ ):
+ raise TypeError(
+ "Key must be an rsa, dsa, ec, ed25519, or ed448 private key."
+ )
+ elif not isinstance(algorithm, hashes.HashAlgorithm):
+ raise TypeError("Algorithm must be a registered hash algorithm.")
+ elif isinstance(algorithm, hashes.MD5) and not isinstance(
+ private_key, rsa.RSAPrivateKey
):
raise ValueError(
- "MD5 is not a supported hash algorithm for EC/DSA CSRs"
+ "MD5 hash algorithm is only supported with RSA keys"
)
+ def create_x509_csr(self, builder, private_key, algorithm):
+ if not isinstance(builder, x509.CertificateSigningRequestBuilder):
+ raise TypeError("Builder type mismatch.")
+ self._x509_check_signature_params(private_key, algorithm)
+
# Resolve the signature algorithm.
- evp_md = self._evp_md_non_null_from_algorithm(algorithm)
+ evp_md = self._evp_md_x509_null_if_eddsa(private_key, algorithm)
# Create an empty request.
x509_req = self._lib.X509_REQ_new()
@@ -724,9 +968,7 @@ class Backend(object):
# Set subject public key.
public_key = private_key.public_key()
- res = self._lib.X509_REQ_set_pubkey(
- x509_req, public_key._evp_pkey
- )
+ res = self._lib.X509_REQ_set_pubkey(x509_req, public_key._evp_pkey)
self.openssl_assert(res == 1)
# Add extensions.
@@ -735,60 +977,55 @@ class Backend(object):
sk_extension = self._ffi.gc(
sk_extension,
lambda x: self._lib.sk_X509_EXTENSION_pop_free(
- x, self._ffi.addressof(
+ x,
+ self._ffi.addressof(
self._lib._original_lib, "X509_EXTENSION_free"
- )
- )
+ ),
+ ),
)
# Don't GC individual extensions because the memory is owned by
# sk_extensions and will be freed along with it.
self._create_x509_extensions(
extensions=builder._extensions,
- handlers=_EXTENSION_ENCODE_HANDLERS,
+ handlers=self._extension_encode_handlers,
x509_obj=sk_extension,
add_func=self._lib.sk_X509_EXTENSION_insert,
- gc=False
+ gc=False,
)
res = self._lib.X509_REQ_add_extensions(x509_req, sk_extension)
self.openssl_assert(res == 1)
- # Sign the request using the requester's private key.
- res = self._lib.X509_REQ_sign(
- x509_req, private_key._evp_pkey, evp_md
- )
- if res == 0:
- errors = self._consume_errors()
- self.openssl_assert(
- errors[0]._lib_reason_match(
- self._lib.ERR_LIB_RSA,
- self._lib.RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY
- )
+ # Add attributes (all bytes encoded as ASN1 UTF8_STRING)
+ for attr_oid, attr_val in builder._attributes:
+ obj = _txt2obj_gc(self, attr_oid.dotted_string)
+ res = self._lib.X509_REQ_add1_attr_by_OBJ(
+ x509_req,
+ obj,
+ x509.name._ASN1Type.UTF8String.value,
+ attr_val,
+ len(attr_val),
)
+ self.openssl_assert(res == 1)
- raise ValueError("Digest too big for RSA key")
+ # Sign the request using the requester's private key.
+ res = self._lib.X509_REQ_sign(x509_req, private_key._evp_pkey, evp_md)
+ if res == 0:
+ errors = self._consume_errors_with_text()
+ raise ValueError("Signing failed", errors)
return _CertificateSigningRequest(self, x509_req)
def create_x509_certificate(self, builder, private_key, algorithm):
if not isinstance(builder, x509.CertificateBuilder):
- raise TypeError('Builder type mismatch.')
- if not isinstance(algorithm, hashes.HashAlgorithm):
- raise TypeError('Algorithm must be a registered hash algorithm.')
-
- if (
- isinstance(algorithm, hashes.MD5) and not
- isinstance(private_key, rsa.RSAPrivateKey)
- ):
- raise ValueError(
- "MD5 is not a supported hash algorithm for EC/DSA certificates"
- )
+ raise TypeError("Builder type mismatch.")
+ self._x509_check_signature_params(private_key, algorithm)
# Resolve the signature algorithm.
- evp_md = self._evp_md_non_null_from_algorithm(algorithm)
+ evp_md = self._evp_md_x509_null_if_eddsa(private_key, algorithm)
# Create an empty certificate.
x509_cert = self._lib.X509_new()
- x509_cert = self._ffi.gc(x509_cert, backend._lib.X509_free)
+ x509_cert = self._ffi.gc(x509_cert, self._lib.X509_free)
# Set the x509 version.
res = self._lib.X509_set_version(x509_cert, builder._version.value)
@@ -813,21 +1050,21 @@ class Backend(object):
# Set the "not before" time.
self._set_asn1_time(
- self._lib.X509_get_notBefore(x509_cert), builder._not_valid_before
+ self._lib.X509_getm_notBefore(x509_cert), builder._not_valid_before
)
# Set the "not after" time.
self._set_asn1_time(
- self._lib.X509_get_notAfter(x509_cert), builder._not_valid_after
+ self._lib.X509_getm_notAfter(x509_cert), builder._not_valid_after
)
# Add extensions.
self._create_x509_extensions(
extensions=builder._extensions,
- handlers=_EXTENSION_ENCODE_HANDLERS,
+ handlers=self._extension_encode_handlers,
x509_obj=x509_cert,
add_func=self._lib.X509_add_ext,
- gc=True
+ gc=True,
)
# Set the issuer name.
@@ -837,26 +1074,27 @@ class Backend(object):
self.openssl_assert(res == 1)
# Sign the certificate with the issuer's private key.
- res = self._lib.X509_sign(
- x509_cert, private_key._evp_pkey, evp_md
- )
+ res = self._lib.X509_sign(x509_cert, private_key._evp_pkey, evp_md)
if res == 0:
- errors = self._consume_errors()
- self.openssl_assert(
- errors[0]._lib_reason_match(
- self._lib.ERR_LIB_RSA,
- self._lib.RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY
- )
- )
- raise ValueError("Digest too big for RSA key")
+ errors = self._consume_errors_with_text()
+ raise ValueError("Signing failed", errors)
return _Certificate(self, x509_cert)
+ def _evp_md_x509_null_if_eddsa(self, private_key, algorithm):
+ if isinstance(
+ private_key, (ed25519.Ed25519PrivateKey, ed448.Ed448PrivateKey)
+ ):
+ # OpenSSL requires us to pass NULL for EVP_MD for ed25519/ed448
+ return self._ffi.NULL
+ else:
+ return self._evp_md_non_null_from_algorithm(algorithm)
+
def _set_asn1_time(self, asn1_time, time):
if time.year >= 2050:
- asn1_str = time.strftime('%Y%m%d%H%M%SZ').encode('ascii')
+ asn1_str = time.strftime("%Y%m%d%H%M%SZ").encode("ascii")
else:
- asn1_str = time.strftime('%y%m%d%H%M%SZ').encode('ascii')
+ asn1_str = time.strftime("%y%m%d%H%M%SZ").encode("ascii")
res = self._lib.ASN1_TIME_set_string(asn1_time, asn1_str)
self.openssl_assert(res == 1)
@@ -869,23 +1107,14 @@ class Backend(object):
def create_x509_crl(self, builder, private_key, algorithm):
if not isinstance(builder, x509.CertificateRevocationListBuilder):
- raise TypeError('Builder type mismatch.')
- if not isinstance(algorithm, hashes.HashAlgorithm):
- raise TypeError('Algorithm must be a registered hash algorithm.')
-
- if (
- isinstance(algorithm, hashes.MD5) and not
- isinstance(private_key, rsa.RSAPrivateKey)
- ):
- raise ValueError(
- "MD5 is not a supported hash algorithm for EC/DSA CRLs"
- )
+ raise TypeError("Builder type mismatch.")
+ self._x509_check_signature_params(private_key, algorithm)
- evp_md = self._evp_md_non_null_from_algorithm(algorithm)
+ evp_md = self._evp_md_x509_null_if_eddsa(private_key, algorithm)
# Create an empty CRL.
x509_crl = self._lib.X509_CRL_new()
- x509_crl = self._ffi.gc(x509_crl, backend._lib.X509_CRL_free)
+ x509_crl = self._ffi.gc(x509_crl, self._lib.X509_CRL_free)
# Set the x509 CRL version. We only support v2 (integer value 1).
res = self._lib.X509_CRL_set_version(x509_crl, 1)
@@ -910,44 +1139,33 @@ class Backend(object):
# Add extensions.
self._create_x509_extensions(
extensions=builder._extensions,
- handlers=_CRL_EXTENSION_ENCODE_HANDLERS,
+ handlers=self._crl_extension_encode_handlers,
x509_obj=x509_crl,
add_func=self._lib.X509_CRL_add_ext,
- gc=True
+ gc=True,
)
# add revoked certificates
for revoked_cert in builder._revoked_certificates:
# Duplicating because the X509_CRL takes ownership and will free
# this memory when X509_CRL_free is called.
- revoked = self._lib.Cryptography_X509_REVOKED_dup(
- revoked_cert._x509_revoked
- )
+ revoked = self._lib.X509_REVOKED_dup(revoked_cert._x509_revoked)
self.openssl_assert(revoked != self._ffi.NULL)
res = self._lib.X509_CRL_add0_revoked(x509_crl, revoked)
self.openssl_assert(res == 1)
- res = self._lib.X509_CRL_sign(
- x509_crl, private_key._evp_pkey, evp_md
- )
+ res = self._lib.X509_CRL_sign(x509_crl, private_key._evp_pkey, evp_md)
if res == 0:
- errors = self._consume_errors()
- self.openssl_assert(
- errors[0]._lib_reason_match(
- self._lib.ERR_LIB_RSA,
- self._lib.RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY
- )
- )
- raise ValueError("Digest too big for RSA key")
+ errors = self._consume_errors_with_text()
+ raise ValueError("Signing failed", errors)
return _CertificateRevocationList(self, x509_crl)
- def _create_x509_extensions(self, extensions, handlers, x509_obj,
- add_func, gc):
+ def _create_x509_extensions(
+ self, extensions, handlers, x509_obj, add_func, gc
+ ):
for i, extension in enumerate(extensions):
- x509_extension = self._create_x509_extension(
- handlers, extension
- )
+ x509_extension = self._create_x509_extension(handlers, extension)
self.openssl_assert(x509_extension != self._ffi.NULL)
if gc:
@@ -968,33 +1186,38 @@ class Backend(object):
value = _encode_asn1_str_gc(self, extension.value.value)
return self._create_raw_x509_extension(extension, value)
elif isinstance(extension.value, x509.TLSFeature):
- asn1 = _Integers([x.value for x in extension.value]).dump()
+ asn1 = encode_der(
+ SEQUENCE,
+ *[
+ encode_der(INTEGER, encode_der_integer(x.value))
+ for x in extension.value
+ ]
+ )
value = _encode_asn1_str_gc(self, asn1)
return self._create_raw_x509_extension(extension, value)
elif isinstance(extension.value, x509.PrecertPoison):
- asn1 = asn1crypto.core.Null().dump()
- value = _encode_asn1_str_gc(self, asn1)
+ value = _encode_asn1_str_gc(self, encode_der(NULL))
return self._create_raw_x509_extension(extension, value)
else:
try:
encode = handlers[extension.oid]
except KeyError:
raise NotImplementedError(
- 'Extension not supported: {0}'.format(extension.oid)
+ "Extension not supported: {}".format(extension.oid)
)
ext_struct = encode(self, extension.value)
nid = self._lib.OBJ_txt2nid(
extension.oid.dotted_string.encode("ascii")
)
- backend.openssl_assert(nid != self._lib.NID_undef)
+ self.openssl_assert(nid != self._lib.NID_undef)
return self._lib.X509V3_EXT_i2d(
nid, 1 if extension.critical else 0, ext_struct
)
def create_x509_revoked_certificate(self, builder):
if not isinstance(builder, x509.RevokedCertificateBuilder):
- raise TypeError('Builder type mismatch.')
+ raise TypeError("Builder type mismatch.")
x509_revoked = self._lib.X509_REVOKED_new()
self.openssl_assert(x509_revoked != self._ffi.NULL)
@@ -1010,10 +1233,10 @@ class Backend(object):
# add CRL entry extensions
self._create_x509_extensions(
extensions=builder._extensions,
- handlers=_CRL_ENTRY_EXTENSION_ENCODE_HANDLERS,
+ handlers=self._crl_entry_extension_encode_handlers,
x509_obj=x509_revoked,
add_func=self._lib.X509_REVOKED_add_ext,
- gc=True
+ gc=True,
)
return _RevokedCertificate(self, None, x509_revoked)
@@ -1054,7 +1277,8 @@ class Backend(object):
mem_bio = self._bytes_to_bio(data)
# only DH is supported currently
dh_cdata = self._lib.PEM_read_bio_DHparams(
- mem_bio.bio, self._ffi.NULL, self._ffi.NULL, self._ffi.NULL)
+ mem_bio.bio, self._ffi.NULL, self._ffi.NULL, self._ffi.NULL
+ )
if dh_cdata != self._ffi.NULL:
dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free)
return _DHParameters(self, dh_cdata)
@@ -1119,9 +1343,7 @@ class Backend(object):
def load_der_parameters(self, data):
mem_bio = self._bytes_to_bio(data)
- dh_cdata = self._lib.d2i_DHparams_bio(
- mem_bio.bio, self._ffi.NULL
- )
+ dh_cdata = self._lib.d2i_DHparams_bio(mem_bio.bio, self._ffi.NULL)
if dh_cdata != self._ffi.NULL:
dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free)
return _DHParameters(self, dh_cdata)
@@ -1147,8 +1369,9 @@ class Backend(object):
if x509 == self._ffi.NULL:
self._consume_errors()
raise ValueError(
- "Unable to load certificate. See https://cryptography.io/en/la"
- "test/faq/#why-can-t-i-import-my-pem-file for more details."
+ "Unable to load certificate. See https://cryptography.io/en/"
+ "latest/faq.html#why-can-t-i-import-my-pem-file for more"
+ " details."
)
x509 = self._ffi.gc(x509, self._lib.X509_free)
@@ -1173,7 +1396,8 @@ class Backend(object):
self._consume_errors()
raise ValueError(
"Unable to load CRL. See https://cryptography.io/en/la"
- "test/faq/#why-can-t-i-import-my-pem-file for more details."
+ "test/faq.html#why-can-t-i-import-my-pem-file for more"
+ " details."
)
x509_crl = self._ffi.gc(x509_crl, self._lib.X509_CRL_free)
@@ -1197,8 +1421,9 @@ class Backend(object):
if x509_req == self._ffi.NULL:
self._consume_errors()
raise ValueError(
- "Unable to load request. See https://cryptography.io/en/la"
- "test/faq/#why-can-t-i-import-my-pem-file for more details."
+ "Unable to load request. See https://cryptography.io/en/"
+ "latest/faq.html#why-can-t-i-import-my-pem-file for more"
+ " details."
)
x509_req = self._ffi.gc(x509_req, self._lib.X509_REQ_free)
@@ -1235,8 +1460,7 @@ class Backend(object):
if evp_pkey == self._ffi.NULL:
if userdata.error != 0:
- errors = self._consume_errors()
- self.openssl_assert(errors)
+ self._consume_errors()
if userdata.error == -1:
raise TypeError(
"Password was not given but private key is encrypted"
@@ -1244,7 +1468,7 @@ class Backend(object):
else:
assert userdata.error == -2
raise ValueError(
- "Passwords longer than {0} bytes are not supported "
+ "Passwords longer than {} bytes are not supported "
"by this backend.".format(userdata.maxsize - 1)
)
else:
@@ -1254,12 +1478,12 @@ class Backend(object):
if password is not None and userdata.called == 0:
raise TypeError(
- "Password was given but private key is not encrypted.")
+ "Password was given but private key is not encrypted."
+ )
assert (
- (password is not None and userdata.called == 1) or
- password is None
- )
+ password is not None and userdata.called == 1
+ ) or password is None
return convert_func(evp_pkey)
@@ -1267,46 +1491,34 @@ class Backend(object):
errors = self._consume_errors()
if not errors:
- raise ValueError("Could not deserialize key data.")
-
- elif (
- errors[0]._lib_reason_match(
- self._lib.ERR_LIB_EVP, self._lib.EVP_R_BAD_DECRYPT
- ) or errors[0]._lib_reason_match(
- self._lib.ERR_LIB_PKCS12,
- self._lib.PKCS12_R_PKCS12_CIPHERFINAL_ERROR
+ raise ValueError(
+ "Could not deserialize key data. The data may be in an "
+ "incorrect format or it may be encrypted with an unsupported "
+ "algorithm."
)
+ elif errors[0]._lib_reason_match(
+ self._lib.ERR_LIB_EVP, self._lib.EVP_R_BAD_DECRYPT
+ ) or errors[0]._lib_reason_match(
+ self._lib.ERR_LIB_PKCS12,
+ self._lib.PKCS12_R_PKCS12_CIPHERFINAL_ERROR,
):
raise ValueError("Bad decrypt. Incorrect password?")
- elif (
- errors[0]._lib_reason_match(
- self._lib.ERR_LIB_EVP, self._lib.EVP_R_UNKNOWN_PBE_ALGORITHM
- ) or errors[0]._lib_reason_match(
- self._lib.ERR_LIB_PEM, self._lib.PEM_R_UNSUPPORTED_ENCRYPTION
- )
- ):
- raise UnsupportedAlgorithm(
- "PEM data is encrypted with an unsupported cipher",
- _Reasons.UNSUPPORTED_CIPHER
- )
-
elif any(
error._lib_reason_match(
self._lib.ERR_LIB_EVP,
- self._lib.EVP_R_UNSUPPORTED_PRIVATE_KEY_ALGORITHM
+ self._lib.EVP_R_UNSUPPORTED_PRIVATE_KEY_ALGORITHM,
)
for error in errors
):
raise ValueError("Unsupported public key algorithm.")
else:
- assert errors[0].lib in (
- self._lib.ERR_LIB_EVP,
- self._lib.ERR_LIB_PEM,
- self._lib.ERR_LIB_ASN1,
+ raise ValueError(
+ "Could not deserialize key data. The data may be in an "
+ "incorrect format or it may be encrypted with an unsupported "
+ "algorithm."
)
- raise ValueError("Could not deserialize key data.")
def elliptic_curve_supported(self, curve):
try:
@@ -1317,14 +1529,7 @@ class Backend(object):
group = self._lib.EC_GROUP_new_by_curve_name(curve_nid)
if group == self._ffi.NULL:
- errors = self._consume_errors()
- self.openssl_assert(
- curve_nid == self._lib.NID_undef or
- errors[0]._lib_reason_match(
- self._lib.ERR_LIB_EC,
- self._lib.EC_R_UNKNOWN_GROUP
- )
- )
+ self._consume_errors()
return False
else:
self.openssl_assert(curve_nid != self._lib.NID_undef)
@@ -1356,8 +1561,8 @@ class Backend(object):
return _EllipticCurvePrivateKey(self, ec_cdata, evp_pkey)
else:
raise UnsupportedAlgorithm(
- "Backend object does not support {0}.".format(curve.name),
- _Reasons.UNSUPPORTED_ELLIPTIC_CURVE
+ "Backend object does not support {}.".format(curve.name),
+ _Reasons.UNSUPPORTED_ELLIPTIC_CURVE,
)
def load_elliptic_curve_private_numbers(self, numbers):
@@ -1372,7 +1577,8 @@ class Backend(object):
self.openssl_assert(res == 1)
ec_cdata = self._ec_key_set_public_key_affine_coordinates(
- ec_cdata, public.x, public.y)
+ ec_cdata, public.x, public.y
+ )
evp_pkey = self._ec_cdata_to_evp_pkey(ec_cdata)
@@ -1381,7 +1587,8 @@ class Backend(object):
def load_elliptic_curve_public_numbers(self, numbers):
ec_cdata = self._ec_key_new_by_curve(numbers.curve)
ec_cdata = self._ec_key_set_public_key_affine_coordinates(
- ec_cdata, numbers.x, numbers.y)
+ ec_cdata, numbers.x, numbers.y
+ )
evp_pkey = self._ec_cdata_to_evp_pkey(ec_cdata)
return _EllipticCurvePublicKey(self, ec_cdata, evp_pkey)
@@ -1419,8 +1626,9 @@ class Backend(object):
value = self._ffi.gc(value, self._lib.BN_clear_free)
with self._tmp_bn_ctx() as bn_ctx:
- res = self._lib.EC_POINT_mul(group, point, value, self._ffi.NULL,
- self._ffi.NULL, bn_ctx)
+ res = self._lib.EC_POINT_mul(
+ group, point, value, self._ffi.NULL, self._ffi.NULL, bn_ctx
+ )
self.openssl_assert(res == 1)
bn_x = self._lib.BN_CTX_get(bn_ctx)
@@ -1442,6 +1650,9 @@ class Backend(object):
def _ec_key_new_by_curve(self, curve):
curve_nid = self._elliptic_curve_to_nid(curve)
+ return self._ec_key_new_by_curve_nid(curve_nid)
+
+ def _ec_key_new_by_curve_nid(self, curve_nid):
ec_cdata = self._lib.EC_KEY_new_by_curve_name(curve_nid)
self.openssl_assert(ec_cdata != self._ffi.NULL)
return self._ffi.gc(ec_cdata, self._lib.EC_KEY_free)
@@ -1472,15 +1683,13 @@ class Backend(object):
ocsp_req = self._ffi.gc(ocsp_req, self._lib.OCSP_REQUEST_free)
cert, issuer, algorithm = builder._request
evp_md = self._evp_md_non_null_from_algorithm(algorithm)
- certid = self._lib.OCSP_cert_to_id(
- evp_md, cert._x509, issuer._x509
- )
+ certid = self._lib.OCSP_cert_to_id(evp_md, cert._x509, issuer._x509)
self.openssl_assert(certid != self._ffi.NULL)
onereq = self._lib.OCSP_request_add0_id(ocsp_req, certid)
self.openssl_assert(onereq != self._ffi.NULL)
self._create_x509_extensions(
extensions=builder._extensions,
- handlers=_OCSP_REQUEST_EXTENSION_ENCODE_HANDLERS,
+ handlers=self._ocsp_request_extension_encode_handlers,
x509_obj=ocsp_req,
add_func=self._lib.OCSP_REQUEST_add_ext,
gc=True,
@@ -1488,6 +1697,8 @@ class Backend(object):
return _OCSPRequest(self, ocsp_req)
def _create_ocsp_basic_response(self, builder, private_key, algorithm):
+ self._x509_check_signature_params(private_key, algorithm)
+
basic = self._lib.OCSP_BASICRESP_new()
self.openssl_assert(basic != self._ffi.NULL)
basic = self._ffi.gc(basic, self._lib.OCSP_BASICRESP_free)
@@ -1495,8 +1706,9 @@ class Backend(object):
builder._response._algorithm
)
certid = self._lib.OCSP_cert_to_id(
- evp_md, builder._response._cert._x509,
- builder._response._issuer._x509
+ evp_md,
+ builder._response._cert._x509,
+ builder._response._issuer._x509,
)
self.openssl_assert(certid != self._ffi.NULL)
certid = self._ffi.gc(certid, self._lib.OCSP_CERTID_free)
@@ -1528,11 +1740,11 @@ class Backend(object):
reason,
rev_time,
this_update,
- next_update
+ next_update,
)
self.openssl_assert(res != self._ffi.NULL)
# okay, now sign the basic structure
- evp_md = self._evp_md_non_null_from_algorithm(algorithm)
+ evp_md = self._evp_md_x509_null_if_eddsa(private_key, algorithm)
responder_cert, responder_encoding = builder._responder_id
flags = self._lib.OCSP_NOCERTS
if responder_encoding is ocsp.OCSPResponderEncoding.HASH:
@@ -1545,30 +1757,33 @@ class Backend(object):
self._create_x509_extensions(
extensions=builder._extensions,
- handlers=_OCSP_BASICRESP_EXTENSION_ENCODE_HANDLERS,
+ handlers=self._ocsp_basicresp_extension_encode_handlers,
x509_obj=basic,
add_func=self._lib.OCSP_BASICRESP_add_ext,
gc=True,
)
res = self._lib.OCSP_basic_sign(
- basic, responder_cert._x509, private_key._evp_pkey,
- evp_md, self._ffi.NULL, flags
+ basic,
+ responder_cert._x509,
+ private_key._evp_pkey,
+ evp_md,
+ self._ffi.NULL,
+ flags,
)
if res != 1:
- errors = self._consume_errors()
- self.openssl_assert(
- errors[0]._lib_reason_match(
- self._lib.ERR_LIB_X509,
- self._lib.X509_R_KEY_VALUES_MISMATCH
- )
+ errors = self._consume_errors_with_text()
+ raise ValueError(
+ "Error while signing. responder_cert must be signed "
+ "by private_key",
+ errors,
)
- raise ValueError("responder_cert must be signed by private_key")
return basic
- def create_ocsp_response(self, response_status, builder, private_key,
- algorithm):
+ def create_ocsp_response(
+ self, response_status, builder, private_key, algorithm
+ ):
if response_status is ocsp.OCSPResponseStatus.SUCCESSFUL:
basic = self._create_ocsp_basic_response(
builder, private_key, algorithm
@@ -1584,9 +1799,8 @@ class Backend(object):
return _OCSPResponse(self, ocsp_resp)
def elliptic_curve_exchange_algorithm_supported(self, algorithm, curve):
- return (
- self.elliptic_curve_supported(curve) and
- isinstance(algorithm, ec.ECDH)
+ return self.elliptic_curve_supported(curve) and isinstance(
+ algorithm, ec.ECDH
)
def _ec_cdata_to_evp_pkey(self, ec_cdata):
@@ -1600,18 +1814,15 @@ class Backend(object):
Get the NID for a curve name.
"""
- curve_aliases = {
- "secp192r1": "prime192v1",
- "secp256r1": "prime256v1"
- }
+ curve_aliases = {"secp192r1": "prime192v1", "secp256r1": "prime256v1"}
curve_name = curve_aliases.get(curve.name, curve.name)
curve_nid = self._lib.OBJ_sn2nid(curve_name.encode())
if curve_nid == self._lib.NID_undef:
raise UnsupportedAlgorithm(
- "{0} is not a supported elliptic curve".format(curve.name),
- _Reasons.UNSUPPORTED_ELLIPTIC_CURVE
+ "{} is not a supported elliptic curve".format(curve.name),
+ _Reasons.UNSUPPORTED_ELLIPTIC_CURVE,
)
return curve_nid
@@ -1674,47 +1885,32 @@ class Backend(object):
return ctx
- def _private_key_bytes(self, encoding, format, encryption_algorithm,
- evp_pkey, cdata):
+ def _private_key_bytes(
+ self, encoding, format, encryption_algorithm, key, evp_pkey, cdata
+ ):
+ # validate argument types
+ if not isinstance(encoding, serialization.Encoding):
+ raise TypeError("encoding must be an item from the Encoding enum")
if not isinstance(format, serialization.PrivateFormat):
raise TypeError(
"format must be an item from the PrivateFormat enum"
)
-
- # X9.62 encoding is only valid for EC public keys
- if encoding is serialization.Encoding.X962:
- raise ValueError("X9.62 format is only valid for EC public keys")
-
- # Raw format and encoding are only valid for X25519, Ed25519, X448, and
- # Ed448 keys. We capture those cases before this method is called so if
- # we see those enum values here it means the caller has passed them to
- # a key that doesn't support raw type
- if format is serialization.PrivateFormat.Raw:
- raise ValueError("raw format is invalid with this key or encoding")
-
- if encoding is serialization.Encoding.Raw:
- raise ValueError("raw encoding is invalid with this key or format")
-
- if not isinstance(encryption_algorithm,
- serialization.KeySerializationEncryption):
+ if not isinstance(
+ encryption_algorithm, serialization.KeySerializationEncryption
+ ):
raise TypeError(
"Encryption algorithm must be a KeySerializationEncryption "
"instance"
)
+ # validate password
if isinstance(encryption_algorithm, serialization.NoEncryption):
password = b""
- passlen = 0
- evp_cipher = self._ffi.NULL
- elif isinstance(encryption_algorithm,
- serialization.BestAvailableEncryption):
- # This is a curated value that we will update over time.
- evp_cipher = self._lib.EVP_get_cipherbyname(
- b"aes-256-cbc"
- )
+ elif isinstance(
+ encryption_algorithm, serialization.BestAvailableEncryption
+ ):
password = encryption_algorithm.password
- passlen = len(password)
- if passlen > 1023:
+ if len(password) > 1023:
raise ValueError(
"Passwords longer than 1023 bytes are not supported by "
"this backend"
@@ -1722,183 +1918,156 @@ class Backend(object):
else:
raise ValueError("Unsupported encryption type")
- key_type = self._lib.EVP_PKEY_id(evp_pkey)
- if encoding is serialization.Encoding.PEM:
- if format is serialization.PrivateFormat.PKCS8:
+ # PKCS8 + PEM/DER
+ if format is serialization.PrivateFormat.PKCS8:
+ if encoding is serialization.Encoding.PEM:
write_bio = self._lib.PEM_write_bio_PKCS8PrivateKey
- key = evp_pkey
+ elif encoding is serialization.Encoding.DER:
+ write_bio = self._lib.i2d_PKCS8PrivateKey_bio
else:
- assert format is serialization.PrivateFormat.TraditionalOpenSSL
+ raise ValueError("Unsupported encoding for PKCS8")
+ return self._private_key_bytes_via_bio(
+ write_bio, evp_pkey, password
+ )
+
+ # TraditionalOpenSSL + PEM/DER
+ if format is serialization.PrivateFormat.TraditionalOpenSSL:
+ if self._fips_enabled and not isinstance(
+ encryption_algorithm, serialization.NoEncryption
+ ):
+ raise ValueError(
+ "Encrypted traditional OpenSSL format is not "
+ "supported in FIPS mode."
+ )
+ key_type = self._lib.EVP_PKEY_id(evp_pkey)
+
+ if encoding is serialization.Encoding.PEM:
if key_type == self._lib.EVP_PKEY_RSA:
write_bio = self._lib.PEM_write_bio_RSAPrivateKey
elif key_type == self._lib.EVP_PKEY_DSA:
write_bio = self._lib.PEM_write_bio_DSAPrivateKey
- else:
- assert key_type == self._lib.EVP_PKEY_EC
+ elif key_type == self._lib.EVP_PKEY_EC:
write_bio = self._lib.PEM_write_bio_ECPrivateKey
+ else:
+ raise ValueError(
+ "Unsupported key type for TraditionalOpenSSL"
+ )
+ return self._private_key_bytes_via_bio(
+ write_bio, cdata, password
+ )
- key = cdata
- elif encoding is serialization.Encoding.DER:
- if format is serialization.PrivateFormat.TraditionalOpenSSL:
- if not isinstance(
- encryption_algorithm, serialization.NoEncryption
- ):
+ if encoding is serialization.Encoding.DER:
+ if password:
raise ValueError(
"Encryption is not supported for DER encoded "
"traditional OpenSSL keys"
)
+ if key_type == self._lib.EVP_PKEY_RSA:
+ write_bio = self._lib.i2d_RSAPrivateKey_bio
+ elif key_type == self._lib.EVP_PKEY_EC:
+ write_bio = self._lib.i2d_ECPrivateKey_bio
+ elif key_type == self._lib.EVP_PKEY_DSA:
+ write_bio = self._lib.i2d_DSAPrivateKey_bio
+ else:
+ raise ValueError(
+ "Unsupported key type for TraditionalOpenSSL"
+ )
+ return self._bio_func_output(write_bio, cdata)
- return self._private_key_bytes_traditional_der(key_type, cdata)
- else:
- assert format is serialization.PrivateFormat.PKCS8
- write_bio = self._lib.i2d_PKCS8PrivateKey_bio
- key = evp_pkey
+ raise ValueError("Unsupported encoding for TraditionalOpenSSL")
+
+ # OpenSSH + PEM
+ if format is serialization.PrivateFormat.OpenSSH:
+ if encoding is serialization.Encoding.PEM:
+ return ssh.serialize_ssh_private_key(key, password)
+
+ raise ValueError(
+ "OpenSSH private key format can only be used"
+ " with PEM encoding"
+ )
+
+ # Anything that key-specific code was supposed to handle earlier,
+ # like Raw.
+ raise ValueError("format is invalid with this key")
+
+ def _private_key_bytes_via_bio(self, write_bio, evp_pkey, password):
+ if not password:
+ evp_cipher = self._ffi.NULL
else:
- raise TypeError("encoding must be Encoding.PEM or Encoding.DER")
+ # This is a curated value that we will update over time.
+ evp_cipher = self._lib.EVP_get_cipherbyname(b"aes-256-cbc")
- bio = self._create_mem_bio_gc()
- res = write_bio(
- bio,
- key,
+ return self._bio_func_output(
+ write_bio,
+ evp_pkey,
evp_cipher,
password,
- passlen,
+ len(password),
+ self._ffi.NULL,
self._ffi.NULL,
- self._ffi.NULL
)
- self.openssl_assert(res == 1)
- return self._read_mem_bio(bio)
-
- def _private_key_bytes_traditional_der(self, key_type, cdata):
- if key_type == self._lib.EVP_PKEY_RSA:
- write_bio = self._lib.i2d_RSAPrivateKey_bio
- elif key_type == self._lib.EVP_PKEY_EC:
- write_bio = self._lib.i2d_ECPrivateKey_bio
- else:
- self.openssl_assert(key_type == self._lib.EVP_PKEY_DSA)
- write_bio = self._lib.i2d_DSAPrivateKey_bio
+ def _bio_func_output(self, write_bio, *args):
bio = self._create_mem_bio_gc()
- res = write_bio(bio, cdata)
+ res = write_bio(bio, *args)
self.openssl_assert(res == 1)
return self._read_mem_bio(bio)
def _public_key_bytes(self, encoding, format, key, evp_pkey, cdata):
if not isinstance(encoding, serialization.Encoding):
raise TypeError("encoding must be an item from the Encoding enum")
+ if not isinstance(format, serialization.PublicFormat):
+ raise TypeError(
+ "format must be an item from the PublicFormat enum"
+ )
- # Compressed/UncompressedPoint are only valid for EC keys and those
- # cases are handled by the ECPublicKey public_bytes method before this
- # method is called
- if format in (serialization.PublicFormat.UncompressedPoint,
- serialization.PublicFormat.CompressedPoint):
- raise ValueError("Point formats are not valid for this key type")
-
- # Raw format and encoding are only valid for X25519, Ed25519, X448, and
- # Ed448 keys. We capture those cases before this method is called so if
- # we see those enum values here it means the caller has passed them to
- # a key that doesn't support raw type
- if format is serialization.PublicFormat.Raw:
- raise ValueError("raw format is invalid with this key or encoding")
-
- if encoding is serialization.Encoding.Raw:
- raise ValueError("raw encoding is invalid with this key or format")
-
- if (
- format is serialization.PublicFormat.OpenSSH or
- encoding is serialization.Encoding.OpenSSH
- ):
- if (
- format is not serialization.PublicFormat.OpenSSH or
- encoding is not serialization.Encoding.OpenSSH
- ):
- raise ValueError(
- "OpenSSH format must be used with OpenSSH encoding"
- )
- return self._openssh_public_key_bytes(key)
- elif format is serialization.PublicFormat.SubjectPublicKeyInfo:
+ # SubjectPublicKeyInfo + PEM/DER
+ if format is serialization.PublicFormat.SubjectPublicKeyInfo:
if encoding is serialization.Encoding.PEM:
write_bio = self._lib.PEM_write_bio_PUBKEY
- else:
- assert encoding is serialization.Encoding.DER
+ elif encoding is serialization.Encoding.DER:
write_bio = self._lib.i2d_PUBKEY_bio
+ else:
+ raise ValueError(
+ "SubjectPublicKeyInfo works only with PEM or DER encoding"
+ )
+ return self._bio_func_output(write_bio, evp_pkey)
- key = evp_pkey
- elif format is serialization.PublicFormat.PKCS1:
+ # PKCS1 + PEM/DER
+ if format is serialization.PublicFormat.PKCS1:
# Only RSA is supported here.
- assert self._lib.EVP_PKEY_id(evp_pkey) == self._lib.EVP_PKEY_RSA
+ key_type = self._lib.EVP_PKEY_id(evp_pkey)
+ if key_type != self._lib.EVP_PKEY_RSA:
+ raise ValueError("PKCS1 format is supported only for RSA keys")
+
if encoding is serialization.Encoding.PEM:
write_bio = self._lib.PEM_write_bio_RSAPublicKey
- else:
- assert encoding is serialization.Encoding.DER
+ elif encoding is serialization.Encoding.DER:
write_bio = self._lib.i2d_RSAPublicKey_bio
+ else:
+ raise ValueError("PKCS1 works only with PEM or DER encoding")
+ return self._bio_func_output(write_bio, cdata)
- key = cdata
- else:
- raise TypeError(
- "format must be an item from the PublicFormat enum"
- )
-
- bio = self._create_mem_bio_gc()
- res = write_bio(bio, key)
- self.openssl_assert(res == 1)
- return self._read_mem_bio(bio)
+ # OpenSSH + OpenSSH
+ if format is serialization.PublicFormat.OpenSSH:
+ if encoding is serialization.Encoding.OpenSSH:
+ return ssh.serialize_ssh_public_key(key)
- def _openssh_public_key_bytes(self, key):
- if isinstance(key, rsa.RSAPublicKey):
- public_numbers = key.public_numbers()
- return b"ssh-rsa " + base64.b64encode(
- ssh._ssh_write_string(b"ssh-rsa") +
- ssh._ssh_write_mpint(public_numbers.e) +
- ssh._ssh_write_mpint(public_numbers.n)
- )
- elif isinstance(key, dsa.DSAPublicKey):
- public_numbers = key.public_numbers()
- parameter_numbers = public_numbers.parameter_numbers
- return b"ssh-dss " + base64.b64encode(
- ssh._ssh_write_string(b"ssh-dss") +
- ssh._ssh_write_mpint(parameter_numbers.p) +
- ssh._ssh_write_mpint(parameter_numbers.q) +
- ssh._ssh_write_mpint(parameter_numbers.g) +
- ssh._ssh_write_mpint(public_numbers.y)
+ raise ValueError(
+ "OpenSSH format must be used with OpenSSH encoding"
)
- else:
- assert isinstance(key, ec.EllipticCurvePublicKey)
- public_numbers = key.public_numbers()
- try:
- curve_name = {
- ec.SECP256R1: b"nistp256",
- ec.SECP384R1: b"nistp384",
- ec.SECP521R1: b"nistp521",
- }[type(public_numbers.curve)]
- except KeyError:
- raise ValueError(
- "Only SECP256R1, SECP384R1, and SECP521R1 curves are "
- "supported by the SSH public key format"
- )
- point = key.public_bytes(
- serialization.Encoding.X962,
- serialization.PublicFormat.UncompressedPoint
- )
- return b"ecdsa-sha2-" + curve_name + b" " + base64.b64encode(
- ssh._ssh_write_string(b"ecdsa-sha2-" + curve_name) +
- ssh._ssh_write_string(curve_name) +
- ssh._ssh_write_string(point)
- )
+ # Anything that key-specific code was supposed to handle earlier,
+ # like Raw, CompressedPoint, UncompressedPoint
+ raise ValueError("format is invalid with this key")
def _parameter_bytes(self, encoding, format, cdata):
if encoding is serialization.Encoding.OpenSSH:
- raise TypeError(
- "OpenSSH encoding is not supported"
- )
+ raise TypeError("OpenSSH encoding is not supported")
# Only DH is supported here currently.
q = self._ffi.new("BIGNUM **")
- self._lib.DH_get0_pqg(cdata,
- self._ffi.NULL,
- q,
- self._ffi.NULL)
+ self._lib.DH_get0_pqg(cdata, self._ffi.NULL, q, self._ffi.NULL)
if encoding is serialization.Encoding.PEM:
if q[0] != self._ffi.NULL:
write_bio = self._lib.PEM_write_bio_DHxparams
@@ -1918,8 +2087,12 @@ class Backend(object):
return self._read_mem_bio(bio)
def generate_dh_parameters(self, generator, key_size):
- if key_size < 512:
- raise ValueError("DH key_size must be at least 512 bits")
+ if key_size < dh._MIN_MODULUS_SIZE:
+ raise ValueError(
+ "DH key_size must be at least {} bits".format(
+ dh._MIN_MODULUS_SIZE
+ )
+ )
if generator not in (2, 5):
raise ValueError("DH generator must be 2 or 5")
@@ -1929,10 +2102,7 @@ class Backend(object):
dh_param_cdata = self._ffi.gc(dh_param_cdata, self._lib.DH_free)
res = self._lib.DH_generate_parameters_ex(
- dh_param_cdata,
- key_size,
- generator,
- self._ffi.NULL
+ dh_param_cdata, key_size, generator, self._ffi.NULL
)
self.openssl_assert(res == 1)
@@ -1956,7 +2126,8 @@ class Backend(object):
def generate_dh_private_key_and_parameters(self, generator, key_size):
return self.generate_dh_private_key(
- self.generate_dh_parameters(generator, key_size))
+ self.generate_dh_parameters(generator, key_size)
+ )
def load_dh_private_numbers(self, numbers):
parameter_numbers = numbers.public_numbers.parameter_numbers
@@ -1995,12 +2166,10 @@ class Backend(object):
# the key to the attacker in exchange for having the full key space
# available. See: https://crypto.stackexchange.com/questions/12961
if codes[0] != 0 and not (
- parameter_numbers.g == 2 and
- codes[0] ^ self._lib.DH_NOT_SUITABLE_GENERATOR == 0
+ parameter_numbers.g == 2
+ and codes[0] ^ self._lib.DH_NOT_SUITABLE_GENERATOR == 0
):
- raise ValueError(
- "DH private numbers did not pass safety checks."
- )
+ raise ValueError("DH private numbers did not pass safety checks.")
evp_pkey = self._dh_cdata_to_evp_pkey(dh_cdata)
@@ -2095,11 +2264,11 @@ class Backend(object):
evp_pkey = self._create_evp_pkey_gc()
res = self._lib.EVP_PKEY_set_type(evp_pkey, self._lib.NID_X25519)
- backend.openssl_assert(res == 1)
+ self.openssl_assert(res == 1)
res = self._lib.EVP_PKEY_set1_tls_encodedpoint(
evp_pkey, data, len(data)
)
- backend.openssl_assert(res == 1)
+ self.openssl_assert(res == 1)
return _X25519PublicKey(self, evp_pkey)
def x25519_load_private_bytes(self, data):
@@ -2127,7 +2296,7 @@ class Backend(object):
ba[0:16] = pkcs8_prefix
ba[16:] = data
bio = self._bytes_to_bio(ba)
- evp_pkey = backend._lib.d2i_PrivateKey_bio(bio.bio, self._ffi.NULL)
+ evp_pkey = self._lib.d2i_PrivateKey_bio(bio.bio, self._ffi.NULL)
self.openssl_assert(evp_pkey != self._ffi.NULL)
evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free)
@@ -2154,7 +2323,9 @@ class Backend(object):
return _X25519PrivateKey(self, evp_pkey)
def x25519_supported(self):
- return self._lib.CRYPTOGRAPHY_OPENSSL_110_OR_GREATER
+ if self._fips_enabled:
+ return False
+ return not self._lib.CRYPTOGRAPHY_IS_LIBRESSL
def x448_load_public_bytes(self, data):
if len(data) != 56:
@@ -2184,44 +2355,115 @@ class Backend(object):
return _X448PrivateKey(self, evp_pkey)
def x448_supported(self):
+ if self._fips_enabled:
+ return False
return not self._lib.CRYPTOGRAPHY_OPENSSL_LESS_THAN_111
+ def ed25519_supported(self):
+ if self._fips_enabled:
+ return False
+ return not self._lib.CRYPTOGRAPHY_OPENSSL_LESS_THAN_111B
+
+ def ed25519_load_public_bytes(self, data):
+ utils._check_bytes("data", data)
+
+ if len(data) != ed25519._ED25519_KEY_SIZE:
+ raise ValueError("An Ed25519 public key is 32 bytes long")
+
+ evp_pkey = self._lib.EVP_PKEY_new_raw_public_key(
+ self._lib.NID_ED25519, self._ffi.NULL, data, len(data)
+ )
+ self.openssl_assert(evp_pkey != self._ffi.NULL)
+ evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free)
+
+ return _Ed25519PublicKey(self, evp_pkey)
+
+ def ed25519_load_private_bytes(self, data):
+ if len(data) != ed25519._ED25519_KEY_SIZE:
+ raise ValueError("An Ed25519 private key is 32 bytes long")
+
+ utils._check_byteslike("data", data)
+ data_ptr = self._ffi.from_buffer(data)
+ evp_pkey = self._lib.EVP_PKEY_new_raw_private_key(
+ self._lib.NID_ED25519, self._ffi.NULL, data_ptr, len(data)
+ )
+ self.openssl_assert(evp_pkey != self._ffi.NULL)
+ evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free)
+
+ return _Ed25519PrivateKey(self, evp_pkey)
+
+ def ed25519_generate_key(self):
+ evp_pkey = self._evp_pkey_keygen_gc(self._lib.NID_ED25519)
+ return _Ed25519PrivateKey(self, evp_pkey)
+
+ def ed448_supported(self):
+ if self._fips_enabled:
+ return False
+ return not self._lib.CRYPTOGRAPHY_OPENSSL_LESS_THAN_111B
+
+ def ed448_load_public_bytes(self, data):
+ utils._check_bytes("data", data)
+ if len(data) != _ED448_KEY_SIZE:
+ raise ValueError("An Ed448 public key is 57 bytes long")
+
+ evp_pkey = self._lib.EVP_PKEY_new_raw_public_key(
+ self._lib.NID_ED448, self._ffi.NULL, data, len(data)
+ )
+ self.openssl_assert(evp_pkey != self._ffi.NULL)
+ evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free)
+
+ return _Ed448PublicKey(self, evp_pkey)
+
+ def ed448_load_private_bytes(self, data):
+ utils._check_byteslike("data", data)
+ if len(data) != _ED448_KEY_SIZE:
+ raise ValueError("An Ed448 private key is 57 bytes long")
+
+ data_ptr = self._ffi.from_buffer(data)
+ evp_pkey = self._lib.EVP_PKEY_new_raw_private_key(
+ self._lib.NID_ED448, self._ffi.NULL, data_ptr, len(data)
+ )
+ self.openssl_assert(evp_pkey != self._ffi.NULL)
+ evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free)
+
+ return _Ed448PrivateKey(self, evp_pkey)
+
+ def ed448_generate_key(self):
+ evp_pkey = self._evp_pkey_keygen_gc(self._lib.NID_ED448)
+ return _Ed448PrivateKey(self, evp_pkey)
+
def derive_scrypt(self, key_material, salt, length, n, r, p):
buf = self._ffi.new("unsigned char[]", length)
key_material_ptr = self._ffi.from_buffer(key_material)
res = self._lib.EVP_PBE_scrypt(
- key_material_ptr, len(key_material), salt, len(salt), n, r, p,
- scrypt._MEM_LIMIT, buf, length
+ key_material_ptr,
+ len(key_material),
+ salt,
+ len(salt),
+ n,
+ r,
+ p,
+ scrypt._MEM_LIMIT,
+ buf,
+ length,
)
if res != 1:
- errors = self._consume_errors()
- if not self._lib.CRYPTOGRAPHY_OPENSSL_LESS_THAN_111:
- # This error is only added to the stack in 1.1.1+
- self.openssl_assert(
- errors[0]._lib_reason_match(
- self._lib.ERR_LIB_EVP,
- self._lib.ERR_R_MALLOC_FAILURE
- ) or
- errors[0]._lib_reason_match(
- self._lib.ERR_LIB_EVP,
- self._lib.EVP_R_MEMORY_LIMIT_EXCEEDED
- )
- )
-
+ errors = self._consume_errors_with_text()
# memory required formula explained here:
# https://blog.filippo.io/the-scrypt-parameters/
- min_memory = 128 * n * r // (1024**2)
+ min_memory = 128 * n * r // (1024 ** 2)
raise MemoryError(
"Not enough memory to derive key. These parameters require"
- " {} MB of memory.".format(min_memory)
+ " {} MB of memory.".format(min_memory),
+ errors,
)
return self._ffi.buffer(buf)[:]
def aead_cipher_supported(self, cipher):
cipher_name = aead._aead_cipher_name(cipher)
- return (
- self._lib.EVP_get_cipherbyname(cipher_name) != self._ffi.NULL
- )
+ if self._fips_enabled and cipher_name not in self._fips_aead:
+ return False
+ return self._lib.EVP_get_cipherbyname(cipher_name) != self._ffi.NULL
@contextlib.contextmanager
def _zeroed_bytearray(self, length):
@@ -2306,12 +2548,216 @@ class Backend(object):
num = self._lib.sk_X509_num(sk_x509_ptr[0])
for i in range(num):
x509 = self._lib.sk_X509_value(sk_x509, i)
- x509 = self._ffi.gc(x509, self._lib.X509_free)
self.openssl_assert(x509 != self._ffi.NULL)
+ x509 = self._ffi.gc(x509, self._lib.X509_free)
additional_certificates.append(_Certificate(self, x509))
return (key, cert, additional_certificates)
+ def serialize_key_and_certificates_to_pkcs12(
+ self, name, key, cert, cas, encryption_algorithm
+ ):
+ password = None
+ if name is not None:
+ utils._check_bytes("name", name)
+
+ if isinstance(encryption_algorithm, serialization.NoEncryption):
+ nid_cert = -1
+ nid_key = -1
+ pkcs12_iter = 0
+ mac_iter = 0
+ elif isinstance(
+ encryption_algorithm, serialization.BestAvailableEncryption
+ ):
+ # PKCS12 encryption is hopeless trash and can never be fixed.
+ # This is the least terrible option.
+ nid_cert = self._lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC
+ nid_key = self._lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC
+ # At least we can set this higher than OpenSSL's default
+ pkcs12_iter = 20000
+ # mac_iter chosen for compatibility reasons, see:
+ # https://www.openssl.org/docs/man1.1.1/man3/PKCS12_create.html
+ # Did we mention how lousy PKCS12 encryption is?
+ mac_iter = 1
+ password = encryption_algorithm.password
+ else:
+ raise ValueError("Unsupported key encryption type")
+
+ if cas is None or len(cas) == 0:
+ sk_x509 = self._ffi.NULL
+ else:
+ sk_x509 = self._lib.sk_X509_new_null()
+ sk_x509 = self._ffi.gc(sk_x509, self._lib.sk_X509_free)
+
+ # reverse the list when building the stack so that they're encoded
+ # in the order they were originally provided. it is a mystery
+ for ca in reversed(cas):
+ res = self._lib.sk_X509_push(sk_x509, ca._x509)
+ backend.openssl_assert(res >= 1)
+
+ with self._zeroed_null_terminated_buf(password) as password_buf:
+ with self._zeroed_null_terminated_buf(name) as name_buf:
+ p12 = self._lib.PKCS12_create(
+ password_buf,
+ name_buf,
+ key._evp_pkey if key else self._ffi.NULL,
+ cert._x509 if cert else self._ffi.NULL,
+ sk_x509,
+ nid_key,
+ nid_cert,
+ pkcs12_iter,
+ mac_iter,
+ 0,
+ )
+
+ self.openssl_assert(p12 != self._ffi.NULL)
+ p12 = self._ffi.gc(p12, self._lib.PKCS12_free)
+
+ bio = self._create_mem_bio_gc()
+ res = self._lib.i2d_PKCS12_bio(bio, p12)
+ self.openssl_assert(res > 0)
+ return self._read_mem_bio(bio)
+
+ def poly1305_supported(self):
+ if self._fips_enabled:
+ return False
+ return self._lib.Cryptography_HAS_POLY1305 == 1
+
+ def create_poly1305_ctx(self, key):
+ utils._check_byteslike("key", key)
+ if len(key) != _POLY1305_KEY_SIZE:
+ raise ValueError("A poly1305 key is 32 bytes long")
+
+ return _Poly1305Context(self, key)
+
+ def load_pem_pkcs7_certificates(self, data):
+ utils._check_bytes("data", data)
+ bio = self._bytes_to_bio(data)
+ p7 = self._lib.PEM_read_bio_PKCS7(
+ bio.bio, self._ffi.NULL, self._ffi.NULL, self._ffi.NULL
+ )
+ if p7 == self._ffi.NULL:
+ self._consume_errors()
+ raise ValueError("Unable to parse PKCS7 data")
+
+ p7 = self._ffi.gc(p7, self._lib.PKCS7_free)
+ return self._load_pkcs7_certificates(p7)
+
+ def load_der_pkcs7_certificates(self, data):
+ utils._check_bytes("data", data)
+ bio = self._bytes_to_bio(data)
+ p7 = self._lib.d2i_PKCS7_bio(bio.bio, self._ffi.NULL)
+ if p7 == self._ffi.NULL:
+ self._consume_errors()
+ raise ValueError("Unable to parse PKCS7 data")
+
+ p7 = self._ffi.gc(p7, self._lib.PKCS7_free)
+ return self._load_pkcs7_certificates(p7)
+
+ def _load_pkcs7_certificates(self, p7):
+ nid = self._lib.OBJ_obj2nid(p7.type)
+ self.openssl_assert(nid != self._lib.NID_undef)
+ if nid != self._lib.NID_pkcs7_signed:
+ raise UnsupportedAlgorithm(
+ "Only basic signed structures are currently supported. NID"
+ " for this data was {}".format(nid),
+ _Reasons.UNSUPPORTED_SERIALIZATION,
+ )
+
+ sk_x509 = p7.d.sign.cert
+ num = self._lib.sk_X509_num(sk_x509)
+ certs = []
+ for i in range(num):
+ x509 = self._lib.sk_X509_value(sk_x509, i)
+ self.openssl_assert(x509 != self._ffi.NULL)
+ res = self._lib.X509_up_ref(x509)
+ # When OpenSSL is less than 1.1.0 up_ref returns the current
+ # refcount. On 1.1.0+ it returns 1 for success.
+ self.openssl_assert(res >= 1)
+ x509 = self._ffi.gc(x509, self._lib.X509_free)
+ certs.append(_Certificate(self, x509))
+
+ return certs
+
+ def pkcs7_sign(self, builder, encoding, options):
+ bio = self._bytes_to_bio(builder._data)
+ init_flags = self._lib.PKCS7_PARTIAL
+ final_flags = 0
+
+ if len(builder._additional_certs) == 0:
+ certs = self._ffi.NULL
+ else:
+ certs = self._lib.sk_X509_new_null()
+ certs = self._ffi.gc(certs, self._lib.sk_X509_free)
+ for cert in builder._additional_certs:
+ res = self._lib.sk_X509_push(certs, cert._x509)
+ self.openssl_assert(res >= 1)
+
+ if pkcs7.PKCS7Options.DetachedSignature in options:
+ # Don't embed the data in the PKCS7 structure
+ init_flags |= self._lib.PKCS7_DETACHED
+ final_flags |= self._lib.PKCS7_DETACHED
+
+ # This just inits a structure for us. However, there
+ # are flags we need to set, joy.
+ p7 = self._lib.PKCS7_sign(
+ self._ffi.NULL,
+ self._ffi.NULL,
+ certs,
+ self._ffi.NULL,
+ init_flags,
+ )
+ self.openssl_assert(p7 != self._ffi.NULL)
+ p7 = self._ffi.gc(p7, self._lib.PKCS7_free)
+ signer_flags = 0
+ # These flags are configurable on a per-signature basis
+ # but we've deliberately chosen to make the API only allow
+ # setting it across all signatures for now.
+ if pkcs7.PKCS7Options.NoCapabilities in options:
+ signer_flags |= self._lib.PKCS7_NOSMIMECAP
+ elif pkcs7.PKCS7Options.NoAttributes in options:
+ signer_flags |= self._lib.PKCS7_NOATTR
+
+ if pkcs7.PKCS7Options.NoCerts in options:
+ signer_flags |= self._lib.PKCS7_NOCERTS
+
+ for certificate, private_key, hash_algorithm in builder._signers:
+ md = self._evp_md_non_null_from_algorithm(hash_algorithm)
+ p7signerinfo = self._lib.PKCS7_sign_add_signer(
+ p7, certificate._x509, private_key._evp_pkey, md, signer_flags
+ )
+ self.openssl_assert(p7signerinfo != self._ffi.NULL)
+
+ for option in options:
+ # DetachedSignature, NoCapabilities, and NoAttributes are already
+ # handled so we just need to check these last two options.
+ if option is pkcs7.PKCS7Options.Text:
+ final_flags |= self._lib.PKCS7_TEXT
+ elif option is pkcs7.PKCS7Options.Binary:
+ final_flags |= self._lib.PKCS7_BINARY
+
+ bio_out = self._create_mem_bio_gc()
+ if encoding is serialization.Encoding.SMIME:
+ # This finalizes the structure
+ res = self._lib.SMIME_write_PKCS7(
+ bio_out, p7, bio.bio, final_flags
+ )
+ elif encoding is serialization.Encoding.PEM:
+ res = self._lib.PKCS7_final(p7, bio.bio, final_flags)
+ self.openssl_assert(res == 1)
+ res = self._lib.PEM_write_bio_PKCS7_stream(
+ bio_out, p7, bio.bio, final_flags
+ )
+ else:
+ assert encoding is serialization.Encoding.DER
+ # We need to call finalize here becauase i2d_PKCS7_bio does not
+ # finalize.
+ res = self._lib.PKCS7_final(p7, bio.bio, final_flags)
+ self.openssl_assert(res == 1)
+ res = self._lib.i2d_PKCS7_bio(bio_out, p7)
+ self.openssl_assert(res == 1)
+ return self._read_mem_bio(bio_out)
+
class GetCipherByName(object):
def __init__(self, fmt):
@@ -2323,7 +2769,7 @@ class GetCipherByName(object):
def _get_xts_cipher(backend, cipher, mode):
- cipher_name = "aes-{0}-xts".format(cipher.key_size // 2)
+ cipher_name = "aes-{}-xts".format(cipher.key_size // 2)
return backend._lib.EVP_get_cipherbyname(cipher_name.encode("ascii"))
diff --git a/src/cryptography/hazmat/backends/openssl/ciphers.py b/src/cryptography/hazmat/backends/openssl/ciphers.py
index 66ac5fd69..ad5dad3f7 100644
--- a/src/cryptography/hazmat/backends/openssl/ciphers.py
+++ b/src/cryptography/hazmat/backends/openssl/ciphers.py
@@ -17,6 +17,7 @@ from cryptography.hazmat.primitives.ciphers import modes
class _CipherContext(object):
_ENCRYPT = 1
_DECRYPT = 0
+ _MAX_CHUNK_SIZE = 2 ** 30 - 1
def __init__(self, backend, cipher, mode, operation):
self._backend = backend
@@ -40,10 +41,11 @@ class _CipherContext(object):
adapter = registry[type(cipher), type(mode)]
except KeyError:
raise UnsupportedAlgorithm(
- "cipher {0} in {1} mode is not supported "
+ "cipher {} in {} mode is not supported "
"by this backend.".format(
- cipher.name, mode.name if mode else mode),
- _Reasons.UNSUPPORTED_CIPHER
+ cipher.name, mode.name if mode else mode
+ ),
+ _Reasons.UNSUPPORTED_CIPHER,
)
evp_cipher = adapter(self._backend, cipher, mode)
@@ -53,7 +55,7 @@ class _CipherContext(object):
msg += "in {0.name} mode ".format(mode)
msg += (
"is not supported by this backend (Your version of OpenSSL "
- "may be too old. Current version: {0}.)"
+ "may be too old. Current version: {}.)"
).format(self._backend.openssl_version_text())
raise UnsupportedAlgorithm(msg, _Reasons.UNSUPPORTED_CIPHER)
@@ -70,11 +72,14 @@ class _CipherContext(object):
else:
iv_nonce = self._backend._ffi.NULL
# begin init with cipher and operation type
- res = self._backend._lib.EVP_CipherInit_ex(ctx, evp_cipher,
- self._backend._ffi.NULL,
- self._backend._ffi.NULL,
- self._backend._ffi.NULL,
- operation)
+ res = self._backend._lib.EVP_CipherInit_ex(
+ ctx,
+ evp_cipher,
+ self._backend._ffi.NULL,
+ self._backend._ffi.NULL,
+ self._backend._ffi.NULL,
+ operation,
+ )
self._backend.openssl_assert(res != 0)
# set the key length to handle variable key ciphers
res = self._backend._lib.EVP_CIPHER_CTX_set_key_length(
@@ -83,26 +88,21 @@ class _CipherContext(object):
self._backend.openssl_assert(res != 0)
if isinstance(mode, modes.GCM):
res = self._backend._lib.EVP_CIPHER_CTX_ctrl(
- ctx, self._backend._lib.EVP_CTRL_AEAD_SET_IVLEN,
- len(iv_nonce), self._backend._ffi.NULL
+ ctx,
+ self._backend._lib.EVP_CTRL_AEAD_SET_IVLEN,
+ len(iv_nonce),
+ self._backend._ffi.NULL,
)
self._backend.openssl_assert(res != 0)
if mode.tag is not None:
res = self._backend._lib.EVP_CIPHER_CTX_ctrl(
- ctx, self._backend._lib.EVP_CTRL_AEAD_SET_TAG,
- len(mode.tag), mode.tag
+ ctx,
+ self._backend._lib.EVP_CTRL_AEAD_SET_TAG,
+ len(mode.tag),
+ mode.tag,
)
self._backend.openssl_assert(res != 0)
self._tag = mode.tag
- elif (
- self._operation == self._DECRYPT and
- self._backend._lib.CRYPTOGRAPHY_OPENSSL_LESS_THAN_102 and
- not self._backend._lib.CRYPTOGRAPHY_IS_LIBRESSL
- ):
- raise NotImplementedError(
- "delayed passing of GCM tag requires OpenSSL >= 1.0.2."
- " To use this feature please update OpenSSL"
- )
# pass key/iv
res = self._backend._lib.EVP_CipherInit_ex(
@@ -111,7 +111,7 @@ class _CipherContext(object):
self._backend._ffi.NULL,
self._backend._ffi.from_buffer(cipher.key),
iv_nonce,
- operation
+ operation,
)
self._backend.openssl_assert(res != 0)
# We purposely disable padding here as it's handled higher up in the
@@ -125,36 +125,38 @@ class _CipherContext(object):
return bytes(buf[:n])
def update_into(self, data, buf):
- if len(buf) < (len(data) + self._block_size_bytes - 1):
+ total_data_len = len(data)
+ if len(buf) < (total_data_len + self._block_size_bytes - 1):
raise ValueError(
- "buffer must be at least {0} bytes for this "
+ "buffer must be at least {} bytes for this "
"payload".format(len(data) + self._block_size_bytes - 1)
)
- buf = self._backend._ffi.cast(
- "unsigned char *", self._backend._ffi.from_buffer(buf)
- )
+ data_processed = 0
+ total_out = 0
outlen = self._backend._ffi.new("int *")
- res = self._backend._lib.EVP_CipherUpdate(
- self._ctx, buf, outlen,
- self._backend._ffi.from_buffer(data), len(data)
- )
- self._backend.openssl_assert(res != 0)
- return outlen[0]
+ baseoutbuf = self._backend._ffi.from_buffer(buf)
+ baseinbuf = self._backend._ffi.from_buffer(data)
- def finalize(self):
- # OpenSSL 1.0.1 on Ubuntu 12.04 (and possibly other distributions)
- # appears to have a bug where you must make at least one call to update
- # even if you are only using authenticate_additional_data or the
- # GCM tag will be wrong. An (empty) call to update resolves this
- # and is harmless for all other versions of OpenSSL.
- if isinstance(self._mode, modes.GCM):
- self.update(b"")
+ while data_processed != total_data_len:
+ outbuf = baseoutbuf + total_out
+ inbuf = baseinbuf + data_processed
+ inlen = min(self._MAX_CHUNK_SIZE, total_data_len - data_processed)
+ res = self._backend._lib.EVP_CipherUpdate(
+ self._ctx, outbuf, outlen, inbuf, inlen
+ )
+ self._backend.openssl_assert(res != 0)
+ data_processed += inlen
+ total_out += outlen[0]
+
+ return total_out
+
+ def finalize(self):
if (
- self._operation == self._DECRYPT and
- isinstance(self._mode, modes.ModeWithAuthenticationTag) and
- self.tag is None
+ self._operation == self._DECRYPT
+ and isinstance(self._mode, modes.ModeWithAuthenticationTag)
+ and self.tag is None
):
raise ValueError(
"Authentication tag must be provided when decrypting."
@@ -172,47 +174,44 @@ class _CipherContext(object):
self._backend.openssl_assert(
errors[0]._lib_reason_match(
self._backend._lib.ERR_LIB_EVP,
- self._backend._lib.EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH
- )
+ self._backend._lib.EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH,
+ ),
+ errors=errors,
)
raise ValueError(
"The length of the provided data is not a multiple of "
"the block length."
)
- if (isinstance(self._mode, modes.GCM) and
- self._operation == self._ENCRYPT):
+ if (
+ isinstance(self._mode, modes.GCM)
+ and self._operation == self._ENCRYPT
+ ):
tag_buf = self._backend._ffi.new(
"unsigned char[]", self._block_size_bytes
)
res = self._backend._lib.EVP_CIPHER_CTX_ctrl(
- self._ctx, self._backend._lib.EVP_CTRL_AEAD_GET_TAG,
- self._block_size_bytes, tag_buf
+ self._ctx,
+ self._backend._lib.EVP_CTRL_AEAD_GET_TAG,
+ self._block_size_bytes,
+ tag_buf,
)
self._backend.openssl_assert(res != 0)
self._tag = self._backend._ffi.buffer(tag_buf)[:]
res = self._backend._lib.EVP_CIPHER_CTX_cleanup(self._ctx)
self._backend.openssl_assert(res == 1)
- return self._backend._ffi.buffer(buf)[:outlen[0]]
+ return self._backend._ffi.buffer(buf)[: outlen[0]]
def finalize_with_tag(self, tag):
- if (
- self._backend._lib.CRYPTOGRAPHY_OPENSSL_LESS_THAN_102 and
- not self._backend._lib.CRYPTOGRAPHY_IS_LIBRESSL
- ):
- raise NotImplementedError(
- "finalize_with_tag requires OpenSSL >= 1.0.2. To use this "
- "method please update OpenSSL"
- )
if len(tag) < self._mode._min_tag_length:
raise ValueError(
- "Authentication tag must be {0} bytes or longer.".format(
- self._mode._min_tag_length)
+ "Authentication tag must be {} bytes or longer.".format(
+ self._mode._min_tag_length
+ )
)
res = self._backend._lib.EVP_CIPHER_CTX_ctrl(
- self._ctx, self._backend._lib.EVP_CTRL_AEAD_SET_TAG,
- len(tag), tag
+ self._ctx, self._backend._lib.EVP_CTRL_AEAD_SET_TAG, len(tag), tag
)
self._backend.openssl_assert(res != 0)
self._tag = tag
@@ -221,8 +220,11 @@ class _CipherContext(object):
def authenticate_additional_data(self, data):
outlen = self._backend._ffi.new("int *")
res = self._backend._lib.EVP_CipherUpdate(
- self._ctx, self._backend._ffi.NULL, outlen,
- self._backend._ffi.from_buffer(data), len(data)
+ self._ctx,
+ self._backend._ffi.NULL,
+ outlen,
+ self._backend._ffi.from_buffer(data),
+ len(data),
)
self._backend.openssl_assert(res != 0)
diff --git a/src/cryptography/hazmat/backends/openssl/cmac.py b/src/cryptography/hazmat/backends/openssl/cmac.py
index bc88f3364..195fc230f 100644
--- a/src/cryptography/hazmat/backends/openssl/cmac.py
+++ b/src/cryptography/hazmat/backends/openssl/cmac.py
@@ -7,18 +7,21 @@ from __future__ import absolute_import, division, print_function
from cryptography import utils
from cryptography.exceptions import (
- InvalidSignature, UnsupportedAlgorithm, _Reasons
+ InvalidSignature,
+ UnsupportedAlgorithm,
+ _Reasons,
)
-from cryptography.hazmat.primitives import constant_time, mac
+from cryptography.hazmat.primitives import constant_time
from cryptography.hazmat.primitives.ciphers.modes import CBC
-@utils.register_interface(mac.MACContext)
class _CMACContext(object):
def __init__(self, backend, algorithm, ctx=None):
if not backend.cmac_algorithm_supported(algorithm):
- raise UnsupportedAlgorithm("This backend does not support CMAC.",
- _Reasons.UNSUPPORTED_CIPHER)
+ raise UnsupportedAlgorithm(
+ "This backend does not support CMAC.",
+ _Reasons.UNSUPPORTED_CIPHER,
+ )
self._backend = backend
self._key = algorithm.key
@@ -38,8 +41,11 @@ class _CMACContext(object):
key_ptr = self._backend._ffi.from_buffer(self._key)
res = self._backend._lib.CMAC_Init(
- ctx, key_ptr, len(self._key),
- evp_cipher, self._backend._ffi.NULL
+ ctx,
+ key_ptr,
+ len(self._key),
+ evp_cipher,
+ self._backend._ffi.NULL,
)
self._backend.openssl_assert(res == 1)
@@ -54,9 +60,7 @@ class _CMACContext(object):
def finalize(self):
buf = self._backend._ffi.new("unsigned char[]", self._output_length)
length = self._backend._ffi.new("size_t *", self._output_length)
- res = self._backend._lib.CMAC_Final(
- self._ctx, buf, length
- )
+ res = self._backend._lib.CMAC_Final(self._ctx, buf, length)
self._backend.openssl_assert(res == 1)
self._ctx = None
@@ -68,13 +72,9 @@ class _CMACContext(object):
copied_ctx = self._backend._ffi.gc(
copied_ctx, self._backend._lib.CMAC_CTX_free
)
- res = self._backend._lib.CMAC_CTX_copy(
- copied_ctx, self._ctx
- )
+ res = self._backend._lib.CMAC_CTX_copy(copied_ctx, self._ctx)
self._backend.openssl_assert(res == 1)
- return _CMACContext(
- self._backend, self._algorithm, ctx=copied_ctx
- )
+ return _CMACContext(self._backend, self._algorithm, ctx=copied_ctx)
def verify(self, signature):
digest = self.finalize()
diff --git a/src/cryptography/hazmat/backends/openssl/decode_asn1.py b/src/cryptography/hazmat/backends/openssl/decode_asn1.py
index 007675d4e..cc9b8c0e3 100644
--- a/src/cryptography/hazmat/backends/openssl/decode_asn1.py
+++ b/src/cryptography/hazmat/backends/openssl/decode_asn1.py
@@ -7,23 +7,20 @@ from __future__ import absolute_import, division, print_function
import datetime
import ipaddress
-import asn1crypto.core
-
import six
from cryptography import x509
+from cryptography.hazmat._der import DERReader, INTEGER, NULL, SEQUENCE
from cryptography.x509.extensions import _TLS_FEATURE_TYPE_TO_ENUM
from cryptography.x509.name import _ASN1_TYPE_TO_ENUM
from cryptography.x509.oid import (
- CRLEntryExtensionOID, CertificatePoliciesOID, ExtensionOID,
+ CRLEntryExtensionOID,
+ CertificatePoliciesOID,
+ ExtensionOID,
OCSPExtensionOID,
)
-class _Integers(asn1crypto.core.SequenceOf):
- _child_spec = asn1crypto.core.Integer
-
-
def _obj2txt(backend, obj):
# Set to 80 on the recommendation of
# https://www.openssl.org/docs/crypto/OBJ_nid2ln.html#return_values
@@ -67,9 +64,9 @@ def _decode_x509_name(backend, x509_name):
for x in range(count):
entry = backend._lib.X509_NAME_get_entry(x509_name, x)
attribute = _decode_x509_name_entry(backend, entry)
- set_id = backend._lib.Cryptography_X509_NAME_ENTRY_set(entry)
+ set_id = backend._lib.X509_NAME_ENTRY_set(entry)
if set_id != prev_set_id:
- attributes.append(set([attribute]))
+ attributes.append({attribute})
else:
# is in the same RDN a previous entry
attributes[-1].add(attribute)
@@ -124,10 +121,10 @@ def _decode_general_name(backend, gn):
# netmask. To handle this we convert the netmask to integer, then
# find the first 0 bit, which will be the prefix. If another 1
# bit is present after that the netmask is invalid.
- base = ipaddress.ip_address(data[:data_len // 2])
- netmask = ipaddress.ip_address(data[data_len // 2:])
+ base = ipaddress.ip_address(data[: data_len // 2])
+ netmask = ipaddress.ip_address(data[data_len // 2 :])
bits = bin(int(netmask))[2:]
- prefix = bits.find('0')
+ prefix = bits.find("0")
# If no 0 bits are found it is a /32 or /128
if prefix == -1:
prefix = len(bits)
@@ -135,7 +132,7 @@ def _decode_general_name(backend, gn):
if "1" in bits[prefix:]:
raise ValueError("Invalid netmask")
- ip = ipaddress.ip_network(base.exploded + u"/{0}".format(prefix))
+ ip = ipaddress.ip_network(base.exploded + u"/{}".format(prefix))
else:
ip = ipaddress.ip_address(data)
@@ -160,10 +157,10 @@ def _decode_general_name(backend, gn):
else:
# x400Address or ediPartyName
raise x509.UnsupportedGeneralNameType(
- "{0} is not a supported type".format(
+ "{} is not a supported type".format(
x509._GENERAL_NAMES.get(gn.type, gn.type)
),
- gn.type
+ gn.type,
)
@@ -184,48 +181,57 @@ def _decode_delta_crl_indicator(backend, ext):
class _X509ExtensionParser(object):
- def __init__(self, ext_count, get_ext, handlers):
+ def __init__(self, backend, ext_count, get_ext, handlers):
self.ext_count = ext_count
self.get_ext = get_ext
self.handlers = handlers
+ self._backend = backend
- def parse(self, backend, x509_obj):
+ def parse(self, x509_obj):
extensions = []
seen_oids = set()
- for i in range(self.ext_count(backend, x509_obj)):
- ext = self.get_ext(backend, x509_obj, i)
- backend.openssl_assert(ext != backend._ffi.NULL)
- crit = backend._lib.X509_EXTENSION_get_critical(ext)
+ for i in range(self.ext_count(x509_obj)):
+ ext = self.get_ext(x509_obj, i)
+ self._backend.openssl_assert(ext != self._backend._ffi.NULL)
+ crit = self._backend._lib.X509_EXTENSION_get_critical(ext)
critical = crit == 1
oid = x509.ObjectIdentifier(
- _obj2txt(backend, backend._lib.X509_EXTENSION_get_object(ext))
+ _obj2txt(
+ self._backend,
+ self._backend._lib.X509_EXTENSION_get_object(ext),
+ )
)
if oid in seen_oids:
raise x509.DuplicateExtension(
- "Duplicate {0} extension found".format(oid), oid
+ "Duplicate {} extension found".format(oid), oid
)
# These OIDs are only supported in OpenSSL 1.1.0+ but we want
# to support them in all versions of OpenSSL so we decode them
# ourselves.
if oid == ExtensionOID.TLS_FEATURE:
- data = backend._lib.X509_EXTENSION_get_data(ext)
- parsed = _Integers.load(_asn1_string_to_bytes(backend, data))
+ # The extension contents are a SEQUENCE OF INTEGERs.
+ data = self._backend._lib.X509_EXTENSION_get_data(ext)
+ data_bytes = _asn1_string_to_bytes(self._backend, data)
+ features = DERReader(data_bytes).read_single_element(SEQUENCE)
+ parsed = []
+ while not features.is_empty():
+ parsed.append(features.read_element(INTEGER).as_integer())
+ # Map the features to their enum value.
value = x509.TLSFeature(
- [_TLS_FEATURE_TYPE_TO_ENUM[x.native] for x in parsed]
+ [_TLS_FEATURE_TYPE_TO_ENUM[x] for x in parsed]
)
extensions.append(x509.Extension(oid, critical, value))
seen_oids.add(oid)
continue
elif oid == ExtensionOID.PRECERT_POISON:
- data = backend._lib.X509_EXTENSION_get_data(ext)
- parsed = asn1crypto.core.Null.load(
- _asn1_string_to_bytes(backend, data)
+ data = self._backend._lib.X509_EXTENSION_get_data(ext)
+ # The contents of the extension must be an ASN.1 NULL.
+ reader = DERReader(_asn1_string_to_bytes(self._backend, data))
+ reader.read_single_element(NULL).check_empty()
+ extensions.append(
+ x509.Extension(oid, critical, x509.PrecertPoison())
)
- assert parsed == asn1crypto.core.Null()
- extensions.append(x509.Extension(
- oid, critical, x509.PrecertPoison()
- ))
seen_oids.add(oid)
continue
@@ -233,23 +239,21 @@ class _X509ExtensionParser(object):
handler = self.handlers[oid]
except KeyError:
# Dump the DER payload into an UnrecognizedExtension object
- data = backend._lib.X509_EXTENSION_get_data(ext)
- backend.openssl_assert(data != backend._ffi.NULL)
- der = backend._ffi.buffer(data.data, data.length)[:]
+ data = self._backend._lib.X509_EXTENSION_get_data(ext)
+ self._backend.openssl_assert(data != self._backend._ffi.NULL)
+ der = self._backend._ffi.buffer(data.data, data.length)[:]
unrecognized = x509.UnrecognizedExtension(oid, der)
- extensions.append(
- x509.Extension(oid, critical, unrecognized)
- )
+ extensions.append(x509.Extension(oid, critical, unrecognized))
else:
- ext_data = backend._lib.X509V3_EXT_d2i(ext)
- if ext_data == backend._ffi.NULL:
- backend._consume_errors()
+ ext_data = self._backend._lib.X509V3_EXT_d2i(ext)
+ if ext_data == self._backend._ffi.NULL:
+ self._backend._consume_errors()
raise ValueError(
- "The {0} extension is invalid and can't be "
+ "The {} extension is invalid and can't be "
"parsed".format(oid)
)
- value = handler(backend, ext_data)
+ value = handler(self._backend, ext_data)
extensions.append(x509.Extension(oid, critical, value))
seen_oids.add(oid)
@@ -271,16 +275,12 @@ def _decode_certificate_policies(backend, cp):
qnum = backend._lib.sk_POLICYQUALINFO_num(pi.qualifiers)
qualifiers = []
for j in range(qnum):
- pqi = backend._lib.sk_POLICYQUALINFO_value(
- pi.qualifiers, j
- )
- pqualid = x509.ObjectIdentifier(
- _obj2txt(backend, pqi.pqualid)
- )
+ pqi = backend._lib.sk_POLICYQUALINFO_value(pi.qualifiers, j)
+ pqualid = x509.ObjectIdentifier(_obj2txt(backend, pqi.pqualid))
if pqualid == CertificatePoliciesOID.CPS_QUALIFIER:
cpsuri = backend._ffi.buffer(
pqi.d.cpsuri.data, pqi.d.cpsuri.length
- )[:].decode('ascii')
+ )[:].decode("ascii")
qualifiers.append(cpsuri)
else:
assert pqualid == CertificatePoliciesOID.CPS_USER_NOTICE
@@ -289,9 +289,7 @@ def _decode_certificate_policies(backend, cp):
)
qualifiers.append(user_notice)
- certificate_policies.append(
- x509.PolicyInformation(oid, qualifiers)
- )
+ certificate_policies.append(x509.PolicyInformation(oid, qualifiers))
return x509.CertificatePolicies(certificate_policies)
@@ -304,13 +302,9 @@ def _decode_user_notice(backend, un):
explicit_text = _asn1_string_to_utf8(backend, un.exptext)
if un.noticeref != backend._ffi.NULL:
- organization = _asn1_string_to_utf8(
- backend, un.noticeref.organization
- )
+ organization = _asn1_string_to_utf8(backend, un.noticeref.organization)
- num = backend._lib.sk_ASN1_INTEGER_num(
- un.noticeref.noticenos
- )
+ num = backend._lib.sk_ASN1_INTEGER_num(un.noticeref.noticenos)
notice_numbers = []
for i in range(num):
asn1_int = backend._lib.sk_ASN1_INTEGER_value(
@@ -319,9 +313,7 @@ def _decode_user_notice(backend, un):
notice_num = _asn1_integer_to_int(backend, asn1_int)
notice_numbers.append(notice_num)
- notice_reference = x509.NoticeReference(
- organization, notice_numbers
- )
+ notice_reference = x509.NoticeReference(organization, notice_numbers)
return x509.UserNotice(notice_reference, explicit_text)
@@ -364,9 +356,7 @@ def _decode_authority_key_identifier(backend, akid):
)[:]
if akid.issuer != backend._ffi.NULL:
- authority_cert_issuer = _decode_general_names(
- backend, akid.issuer
- )
+ authority_cert_issuer = _decode_general_names(backend, akid.issuer)
authority_cert_serial_number = _asn1_integer_to_int_or_none(
backend, akid.serial
@@ -377,22 +367,40 @@ def _decode_authority_key_identifier(backend, akid):
)
-def _decode_authority_information_access(backend, aia):
- aia = backend._ffi.cast("Cryptography_STACK_OF_ACCESS_DESCRIPTION *", aia)
- aia = backend._ffi.gc(aia, backend._lib.sk_ACCESS_DESCRIPTION_free)
- num = backend._lib.sk_ACCESS_DESCRIPTION_num(aia)
+def _decode_information_access(backend, ia):
+ ia = backend._ffi.cast("Cryptography_STACK_OF_ACCESS_DESCRIPTION *", ia)
+ ia = backend._ffi.gc(
+ ia,
+ lambda x: backend._lib.sk_ACCESS_DESCRIPTION_pop_free(
+ x,
+ backend._ffi.addressof(
+ backend._lib._original_lib, "ACCESS_DESCRIPTION_free"
+ ),
+ ),
+ )
+ num = backend._lib.sk_ACCESS_DESCRIPTION_num(ia)
access_descriptions = []
for i in range(num):
- ad = backend._lib.sk_ACCESS_DESCRIPTION_value(aia, i)
+ ad = backend._lib.sk_ACCESS_DESCRIPTION_value(ia, i)
backend.openssl_assert(ad.method != backend._ffi.NULL)
oid = x509.ObjectIdentifier(_obj2txt(backend, ad.method))
backend.openssl_assert(ad.location != backend._ffi.NULL)
gn = _decode_general_name(backend, ad.location)
access_descriptions.append(x509.AccessDescription(oid, gn))
+ return access_descriptions
+
+
+def _decode_authority_information_access(backend, aia):
+ access_descriptions = _decode_information_access(backend, aia)
return x509.AuthorityInformationAccess(access_descriptions)
+def _decode_subject_information_access(backend, aia):
+ access_descriptions = _decode_information_access(backend, aia)
+ return x509.SubjectInformationAccess(access_descriptions)
+
+
def _decode_key_usage(backend, bit_string):
bit_string = backend._ffi.cast("ASN1_BIT_STRING *", bit_string)
bit_string = backend._ffi.gc(bit_string, backend._lib.ASN1_BIT_STRING_free)
@@ -415,7 +423,7 @@ def _decode_key_usage(backend, bit_string):
key_cert_sign,
crl_sign,
encipher_only,
- decipher_only
+ decipher_only,
)
@@ -483,8 +491,13 @@ def _decode_issuing_dist_point(backend, idp):
only_some_reasons = None
return x509.IssuingDistributionPoint(
- full_name, relative_name, only_user, only_ca, only_some_reasons,
- indirect_crl, only_attr
+ full_name,
+ relative_name,
+ only_user,
+ only_ca,
+ only_some_reasons,
+ indirect_crl,
+ only_attr,
)
@@ -605,13 +618,9 @@ def _decode_distpoint(backend, distpoint):
rnum = backend._lib.sk_X509_NAME_ENTRY_num(rns)
attributes = set()
for i in range(rnum):
- rn = backend._lib.sk_X509_NAME_ENTRY_value(
- rns, i
- )
+ rn = backend._lib.sk_X509_NAME_ENTRY_value(rns, i)
backend.openssl_assert(rn != backend._ffi.NULL)
- attributes.add(
- _decode_x509_name_entry(backend, rn)
- )
+ attributes.add(_decode_x509_name_entry(backend, rn))
relative_name = x509.RelativeDistinguishedName(attributes)
@@ -635,10 +644,11 @@ def _decode_inhibit_any_policy(backend, asn1_int):
return x509.InhibitAnyPolicy(skip_certs)
-def _decode_precert_signed_certificate_timestamps(backend, asn1_scts):
+def _decode_scts(backend, asn1_scts):
from cryptography.hazmat.backends.openssl.x509 import (
- _SignedCertificateTimestamp
+ _SignedCertificateTimestamp,
)
+
asn1_scts = backend._ffi.cast("Cryptography_STACK_OF_SCT *", asn1_scts)
asn1_scts = backend._ffi.gc(asn1_scts, backend._lib.SCT_LIST_free)
@@ -647,7 +657,17 @@ def _decode_precert_signed_certificate_timestamps(backend, asn1_scts):
sct = backend._lib.sk_SCT_value(asn1_scts, i)
scts.append(_SignedCertificateTimestamp(backend, asn1_scts, sct))
- return x509.PrecertificateSignedCertificateTimestamps(scts)
+ return scts
+
+
+def _decode_precert_signed_certificate_timestamps(backend, asn1_scts):
+ return x509.PrecertificateSignedCertificateTimestamps(
+ _decode_scts(backend, asn1_scts)
+ )
+
+
+def _decode_signed_certificate_timestamps(backend, asn1_scts):
+ return x509.SignedCertificateTimestamps(_decode_scts(backend, asn1_scts))
# CRLReason ::= ENUMERATED {
@@ -686,7 +706,7 @@ _CRL_ENTRY_REASON_ENUM_TO_CODE = {
x509.ReasonFlags.certificate_hold: 6,
x509.ReasonFlags.remove_from_crl: 8,
x509.ReasonFlags.privilege_withdrawn: 9,
- x509.ReasonFlags.aa_compromise: 10
+ x509.ReasonFlags.aa_compromise: 10,
}
@@ -698,13 +718,11 @@ def _decode_crl_reason(backend, enum):
try:
return x509.CRLReason(_CRL_ENTRY_REASON_CODE_TO_ENUM[code])
except KeyError:
- raise ValueError("Unsupported reason code: {0}".format(code))
+ raise ValueError("Unsupported reason code: {}".format(code))
def _decode_invalidity_date(backend, inv_date):
- generalized_time = backend._ffi.cast(
- "ASN1_GENERALIZEDTIME *", inv_date
- )
+ generalized_time = backend._ffi.cast("ASN1_GENERALIZEDTIME *", inv_date)
generalized_time = backend._ffi.gc(
generalized_time, backend._lib.ASN1_GENERALIZEDTIME_free
)
@@ -758,14 +776,14 @@ def _asn1_string_to_utf8(backend, asn1_string):
res = backend._lib.ASN1_STRING_to_UTF8(buf, asn1_string)
if res == -1:
raise ValueError(
- "Unsupported ASN1 string type. Type: {0}".format(asn1_string.type)
+ "Unsupported ASN1 string type. Type: {}".format(asn1_string.type)
)
backend.openssl_assert(buf[0] != backend._ffi.NULL)
buf = backend._ffi.gc(
buf, lambda buffer: backend._lib.OPENSSL_free(buffer[0])
)
- return backend._ffi.buffer(buf[0], res)[:].decode('utf8')
+ return backend._ffi.buffer(buf[0], res)[:].decode("utf8")
def _parse_asn1_time(backend, asn1_time):
@@ -799,7 +817,7 @@ def _decode_nonce(backend, nonce):
return x509.OCSPNonce(_asn1_string_to_bytes(backend, nonce))
-_EXTENSION_HANDLERS_NO_SCT = {
+_EXTENSION_HANDLERS_BASE = {
ExtensionOID.BASIC_CONSTRAINTS: _decode_basic_constraints,
ExtensionOID.SUBJECT_KEY_IDENTIFIER: _decode_subject_key_identifier,
ExtensionOID.KEY_USAGE: _decode_key_usage,
@@ -809,6 +827,9 @@ _EXTENSION_HANDLERS_NO_SCT = {
ExtensionOID.AUTHORITY_INFORMATION_ACCESS: (
_decode_authority_information_access
),
+ ExtensionOID.SUBJECT_INFORMATION_ACCESS: (
+ _decode_subject_information_access
+ ),
ExtensionOID.CERTIFICATE_POLICIES: _decode_certificate_policies,
ExtensionOID.CRL_DISTRIBUTION_POINTS: _decode_crl_distribution_points,
ExtensionOID.FRESHEST_CRL: _decode_freshest_crl,
@@ -818,11 +839,11 @@ _EXTENSION_HANDLERS_NO_SCT = {
ExtensionOID.NAME_CONSTRAINTS: _decode_name_constraints,
ExtensionOID.POLICY_CONSTRAINTS: _decode_policy_constraints,
}
-_EXTENSION_HANDLERS = _EXTENSION_HANDLERS_NO_SCT.copy()
-_EXTENSION_HANDLERS[
- ExtensionOID.PRECERT_SIGNED_CERTIFICATE_TIMESTAMPS
-] = _decode_precert_signed_certificate_timestamps
-
+_EXTENSION_HANDLERS_SCT = {
+ ExtensionOID.PRECERT_SIGNED_CERTIFICATE_TIMESTAMPS: (
+ _decode_precert_signed_certificate_timestamps
+ )
+}
_REVOKED_EXTENSION_HANDLERS = {
CRLEntryExtensionOID.CRL_REASON: _decode_crl_reason,
@@ -839,6 +860,7 @@ _CRL_EXTENSION_HANDLERS = {
_decode_authority_information_access
),
ExtensionOID.ISSUING_DISTRIBUTION_POINT: _decode_issuing_dist_point,
+ ExtensionOID.FRESHEST_CRL: _decode_freshest_crl,
}
_OCSP_REQ_EXTENSION_HANDLERS = {
@@ -849,44 +871,8 @@ _OCSP_BASICRESP_EXTENSION_HANDLERS = {
OCSPExtensionOID.NONCE: _decode_nonce,
}
-_CERTIFICATE_EXTENSION_PARSER_NO_SCT = _X509ExtensionParser(
- ext_count=lambda backend, x: backend._lib.X509_get_ext_count(x),
- get_ext=lambda backend, x, i: backend._lib.X509_get_ext(x, i),
- handlers=_EXTENSION_HANDLERS_NO_SCT
-)
-
-_CERTIFICATE_EXTENSION_PARSER = _X509ExtensionParser(
- ext_count=lambda backend, x: backend._lib.X509_get_ext_count(x),
- get_ext=lambda backend, x, i: backend._lib.X509_get_ext(x, i),
- handlers=_EXTENSION_HANDLERS
-)
-
-_CSR_EXTENSION_PARSER = _X509ExtensionParser(
- ext_count=lambda backend, x: backend._lib.sk_X509_EXTENSION_num(x),
- get_ext=lambda backend, x, i: backend._lib.sk_X509_EXTENSION_value(x, i),
- handlers=_EXTENSION_HANDLERS
-)
-
-_REVOKED_CERTIFICATE_EXTENSION_PARSER = _X509ExtensionParser(
- ext_count=lambda backend, x: backend._lib.X509_REVOKED_get_ext_count(x),
- get_ext=lambda backend, x, i: backend._lib.X509_REVOKED_get_ext(x, i),
- handlers=_REVOKED_EXTENSION_HANDLERS,
-)
-
-_CRL_EXTENSION_PARSER = _X509ExtensionParser(
- ext_count=lambda backend, x: backend._lib.X509_CRL_get_ext_count(x),
- get_ext=lambda backend, x, i: backend._lib.X509_CRL_get_ext(x, i),
- handlers=_CRL_EXTENSION_HANDLERS,
-)
-
-_OCSP_REQ_EXT_PARSER = _X509ExtensionParser(
- ext_count=lambda backend, x: backend._lib.OCSP_REQUEST_get_ext_count(x),
- get_ext=lambda backend, x, i: backend._lib.OCSP_REQUEST_get_ext(x, i),
- handlers=_OCSP_REQ_EXTENSION_HANDLERS,
-)
-
-_OCSP_BASICRESP_EXT_PARSER = _X509ExtensionParser(
- ext_count=lambda backend, x: backend._lib.OCSP_BASICRESP_get_ext_count(x),
- get_ext=lambda backend, x, i: backend._lib.OCSP_BASICRESP_get_ext(x, i),
- handlers=_OCSP_BASICRESP_EXTENSION_HANDLERS,
-)
+_OCSP_SINGLERESP_EXTENSION_HANDLERS_SCT = {
+ ExtensionOID.SIGNED_CERTIFICATE_TIMESTAMPS: (
+ _decode_signed_certificate_timestamps
+ )
+}
diff --git a/src/cryptography/hazmat/backends/openssl/dh.py b/src/cryptography/hazmat/backends/openssl/dh.py
index 095f06233..2862676c6 100644
--- a/src/cryptography/hazmat/backends/openssl/dh.py
+++ b/src/cryptography/hazmat/backends/openssl/dh.py
@@ -17,8 +17,8 @@ def _dh_params_dup(dh_cdata, backend):
param_cdata = lib.DHparams_dup(dh_cdata)
backend.openssl_assert(param_cdata != ffi.NULL)
param_cdata = ffi.gc(param_cdata, lib.DH_free)
- if lib.CRYPTOGRAPHY_OPENSSL_LESS_THAN_102:
- # In OpenSSL versions < 1.0.2 or libressl DHparams_dup don't copy q
+ if lib.CRYPTOGRAPHY_IS_LIBRESSL:
+ # In libressl DHparams_dup don't copy q
q = ffi.new("BIGNUM **")
lib.DH_get0_pqg(dh_cdata, ffi.NULL, q, ffi.NULL)
q_dup = lib.BN_dup(q[0])
@@ -53,7 +53,7 @@ class _DHParameters(object):
return dh.DHParameterNumbers(
p=self._backend._bn_to_int(p[0]),
g=self._backend._bn_to_int(g[0]),
- q=q_val
+ q=q_val,
)
def generate_private_key(self):
@@ -61,44 +61,27 @@ class _DHParameters(object):
def parameter_bytes(self, encoding, format):
if format is not serialization.ParameterFormat.PKCS3:
- raise ValueError(
- "Only PKCS3 serialization is supported"
- )
+ raise ValueError("Only PKCS3 serialization is supported")
if not self._backend._lib.Cryptography_HAS_EVP_PKEY_DHX:
q = self._backend._ffi.new("BIGNUM **")
- self._backend._lib.DH_get0_pqg(self._dh_cdata,
- self._backend._ffi.NULL,
- q,
- self._backend._ffi.NULL)
+ self._backend._lib.DH_get0_pqg(
+ self._dh_cdata,
+ self._backend._ffi.NULL,
+ q,
+ self._backend._ffi.NULL,
+ )
if q[0] != self._backend._ffi.NULL:
raise UnsupportedAlgorithm(
"DH X9.42 serialization is not supported",
- _Reasons.UNSUPPORTED_SERIALIZATION)
+ _Reasons.UNSUPPORTED_SERIALIZATION,
+ )
- return self._backend._parameter_bytes(
- encoding,
- format,
- self._dh_cdata
- )
-
-
-def _handle_dh_compute_key_error(errors, backend):
- lib = backend._lib
-
- backend.openssl_assert(
- errors[0]._lib_reason_match(
- lib.ERR_LIB_DH, lib.DH_R_INVALID_PUBKEY
- )
- )
-
- raise ValueError("Public key value is invalid for this exchange.")
+ return self._backend._parameter_bytes(encoding, format, self._dh_cdata)
def _get_dh_num_bits(backend, dh_cdata):
p = backend._ffi.new("BIGNUM **")
- backend._lib.DH_get0_pqg(dh_cdata, p,
- backend._ffi.NULL,
- backend._ffi.NULL)
+ backend._lib.DH_get0_pqg(dh_cdata, p, backend._ffi.NULL, backend._ffi.NULL)
backend.openssl_assert(p[0] != backend._ffi.NULL)
return backend._lib.BN_num_bits(p[0])
@@ -136,29 +119,32 @@ class _DHPrivateKey(object):
parameter_numbers=dh.DHParameterNumbers(
p=self._backend._bn_to_int(p[0]),
g=self._backend._bn_to_int(g[0]),
- q=q_val
+ q=q_val,
),
- y=self._backend._bn_to_int(pub_key[0])
+ y=self._backend._bn_to_int(pub_key[0]),
),
- x=self._backend._bn_to_int(priv_key[0])
+ x=self._backend._bn_to_int(priv_key[0]),
)
def exchange(self, peer_public_key):
buf = self._backend._ffi.new("unsigned char[]", self._key_size_bytes)
pub_key = self._backend._ffi.new("BIGNUM **")
- self._backend._lib.DH_get0_key(peer_public_key._dh_cdata, pub_key,
- self._backend._ffi.NULL)
+ self._backend._lib.DH_get0_key(
+ peer_public_key._dh_cdata, pub_key, self._backend._ffi.NULL
+ )
self._backend.openssl_assert(pub_key[0] != self._backend._ffi.NULL)
res = self._backend._lib.DH_compute_key(
- buf,
- pub_key[0],
- self._dh_cdata
+ buf, pub_key[0], self._dh_cdata
)
if res == -1:
- errors = self._backend._consume_errors()
- return _handle_dh_compute_key_error(errors, self._backend)
+ errors_with_text = self._backend._consume_errors_with_text()
+ raise ValueError(
+ "Error computing shared key. Public key is likely invalid "
+ "for this exchange.",
+ errors_with_text,
+ )
else:
self._backend.openssl_assert(res >= 1)
@@ -173,15 +159,16 @@ class _DHPrivateKey(object):
def public_key(self):
dh_cdata = _dh_params_dup(self._dh_cdata, self._backend)
pub_key = self._backend._ffi.new("BIGNUM **")
- self._backend._lib.DH_get0_key(self._dh_cdata,
- pub_key, self._backend._ffi.NULL)
+ self._backend._lib.DH_get0_key(
+ self._dh_cdata, pub_key, self._backend._ffi.NULL
+ )
self._backend.openssl_assert(pub_key[0] != self._backend._ffi.NULL)
pub_key_dup = self._backend._lib.BN_dup(pub_key[0])
self._backend.openssl_assert(pub_key_dup != self._backend._ffi.NULL)
- res = self._backend._lib.DH_set0_key(dh_cdata,
- pub_key_dup,
- self._backend._ffi.NULL)
+ res = self._backend._lib.DH_set0_key(
+ dh_cdata, pub_key_dup, self._backend._ffi.NULL
+ )
self._backend.openssl_assert(res == 1)
evp_pkey = self._backend._dh_cdata_to_evp_pkey(dh_cdata)
return _DHPublicKey(self._backend, dh_cdata, evp_pkey)
@@ -196,21 +183,25 @@ class _DHPrivateKey(object):
)
if not self._backend._lib.Cryptography_HAS_EVP_PKEY_DHX:
q = self._backend._ffi.new("BIGNUM **")
- self._backend._lib.DH_get0_pqg(self._dh_cdata,
- self._backend._ffi.NULL,
- q,
- self._backend._ffi.NULL)
+ self._backend._lib.DH_get0_pqg(
+ self._dh_cdata,
+ self._backend._ffi.NULL,
+ q,
+ self._backend._ffi.NULL,
+ )
if q[0] != self._backend._ffi.NULL:
raise UnsupportedAlgorithm(
"DH X9.42 serialization is not supported",
- _Reasons.UNSUPPORTED_SERIALIZATION)
+ _Reasons.UNSUPPORTED_SERIALIZATION,
+ )
return self._backend._private_key_bytes(
encoding,
format,
encryption_algorithm,
+ self,
self._evp_pkey,
- self._dh_cdata
+ self._dh_cdata,
)
@@ -238,16 +229,17 @@ class _DHPublicKey(object):
else:
q_val = self._backend._bn_to_int(q[0])
pub_key = self._backend._ffi.new("BIGNUM **")
- self._backend._lib.DH_get0_key(self._dh_cdata,
- pub_key, self._backend._ffi.NULL)
+ self._backend._lib.DH_get0_key(
+ self._dh_cdata, pub_key, self._backend._ffi.NULL
+ )
self._backend.openssl_assert(pub_key[0] != self._backend._ffi.NULL)
return dh.DHPublicNumbers(
parameter_numbers=dh.DHParameterNumbers(
p=self._backend._bn_to_int(p[0]),
g=self._backend._bn_to_int(g[0]),
- q=q_val
+ q=q_val,
),
- y=self._backend._bn_to_int(pub_key[0])
+ y=self._backend._bn_to_int(pub_key[0]),
)
def parameters(self):
@@ -262,19 +254,18 @@ class _DHPublicKey(object):
if not self._backend._lib.Cryptography_HAS_EVP_PKEY_DHX:
q = self._backend._ffi.new("BIGNUM **")
- self._backend._lib.DH_get0_pqg(self._dh_cdata,
- self._backend._ffi.NULL,
- q,
- self._backend._ffi.NULL)
+ self._backend._lib.DH_get0_pqg(
+ self._dh_cdata,
+ self._backend._ffi.NULL,
+ q,
+ self._backend._ffi.NULL,
+ )
if q[0] != self._backend._ffi.NULL:
raise UnsupportedAlgorithm(
"DH X9.42 serialization is not supported",
- _Reasons.UNSUPPORTED_SERIALIZATION)
+ _Reasons.UNSUPPORTED_SERIALIZATION,
+ )
return self._backend._public_key_bytes(
- encoding,
- format,
- self,
- self._evp_pkey,
- None
+ encoding, format, self, self._evp_pkey, None
)
diff --git a/src/cryptography/hazmat/backends/openssl/dsa.py b/src/cryptography/hazmat/backends/openssl/dsa.py
index de61f0894..0c5faba18 100644
--- a/src/cryptography/hazmat/backends/openssl/dsa.py
+++ b/src/cryptography/hazmat/backends/openssl/dsa.py
@@ -7,12 +7,15 @@ from __future__ import absolute_import, division, print_function
from cryptography import utils
from cryptography.exceptions import InvalidSignature
from cryptography.hazmat.backends.openssl.utils import (
- _calculate_digest_and_algorithm, _check_not_prehashed,
- _warn_sign_verify_deprecated
+ _calculate_digest_and_algorithm,
+ _check_not_prehashed,
+ _warn_sign_verify_deprecated,
)
-from cryptography.hazmat.primitives import hashes, serialization
+from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import (
- AsymmetricSignatureContext, AsymmetricVerificationContext, dsa
+ AsymmetricSignatureContext,
+ AsymmetricVerificationContext,
+ dsa,
)
@@ -29,7 +32,7 @@ def _dsa_sig_sign(backend, private_key, data):
backend.openssl_assert(res == 1)
backend.openssl_assert(buflen[0])
- return backend._ffi.buffer(sig_buf)[:buflen[0]]
+ return backend._ffi.buffer(sig_buf)[: buflen[0]]
def _dsa_sig_verify(backend, public_key, signature, data):
@@ -98,7 +101,7 @@ class _DSAParameters(object):
return dsa.DSAParameterNumbers(
p=self._backend._bn_to_int(p[0]),
q=self._backend._bn_to_int(q[0]),
- g=self._backend._bn_to_int(g[0])
+ g=self._backend._bn_to_int(g[0]),
)
def generate_private_key(self):
@@ -144,11 +147,11 @@ class _DSAPrivateKey(object):
parameter_numbers=dsa.DSAParameterNumbers(
p=self._backend._bn_to_int(p[0]),
q=self._backend._bn_to_int(q[0]),
- g=self._backend._bn_to_int(g[0])
+ g=self._backend._bn_to_int(g[0]),
),
- y=self._backend._bn_to_int(pub_key[0])
+ y=self._backend._bn_to_int(pub_key[0]),
),
- x=self._backend._bn_to_int(priv_key[0])
+ x=self._backend._bn_to_int(priv_key[0]),
)
def public_key(self):
@@ -183,8 +186,9 @@ class _DSAPrivateKey(object):
encoding,
format,
encryption_algorithm,
+ self,
self._evp_pkey,
- self._dsa_cdata
+ self._dsa_cdata,
)
def sign(self, data, algorithm):
@@ -235,9 +239,9 @@ class _DSAPublicKey(object):
parameter_numbers=dsa.DSAParameterNumbers(
p=self._backend._bn_to_int(p[0]),
q=self._backend._bn_to_int(q[0]),
- g=self._backend._bn_to_int(g[0])
+ g=self._backend._bn_to_int(g[0]),
),
- y=self._backend._bn_to_int(pub_key[0])
+ y=self._backend._bn_to_int(pub_key[0]),
)
def parameters(self):
@@ -248,17 +252,8 @@ class _DSAPublicKey(object):
return _DSAParameters(self._backend, dsa_cdata)
def public_bytes(self, encoding, format):
- if format is serialization.PublicFormat.PKCS1:
- raise ValueError(
- "DSA public keys do not support PKCS1 serialization"
- )
-
return self._backend._public_key_bytes(
- encoding,
- format,
- self,
- self._evp_pkey,
- None
+ encoding, format, self, self._evp_pkey, None
)
def verify(self, signature, data, algorithm):
diff --git a/src/cryptography/hazmat/backends/openssl/ec.py b/src/cryptography/hazmat/backends/openssl/ec.py
index a8d69bdf9..05d32baba 100644
--- a/src/cryptography/hazmat/backends/openssl/ec.py
+++ b/src/cryptography/hazmat/backends/openssl/ec.py
@@ -6,15 +6,20 @@ from __future__ import absolute_import, division, print_function
from cryptography import utils
from cryptography.exceptions import (
- InvalidSignature, UnsupportedAlgorithm, _Reasons
+ InvalidSignature,
+ UnsupportedAlgorithm,
+ _Reasons,
)
from cryptography.hazmat.backends.openssl.utils import (
- _calculate_digest_and_algorithm, _check_not_prehashed,
- _warn_sign_verify_deprecated
+ _calculate_digest_and_algorithm,
+ _check_not_prehashed,
+ _warn_sign_verify_deprecated,
)
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import (
- AsymmetricSignatureContext, AsymmetricVerificationContext, ec
+ AsymmetricSignatureContext,
+ AsymmetricVerificationContext,
+ ec,
)
@@ -22,7 +27,8 @@ def _check_signature_algorithm(signature_algorithm):
if not isinstance(signature_algorithm, ec.ECDSA):
raise UnsupportedAlgorithm(
"Unsupported elliptic curve signature algorithm.",
- _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
+ _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM,
+ )
def _ec_key_curve_sn(backend, ec_key):
@@ -34,14 +40,24 @@ def _ec_key_curve_sn(backend, ec_key):
# an error for now.
if nid == backend._lib.NID_undef:
raise NotImplementedError(
- "ECDSA certificates with unnamed curves are unsupported "
- "at this time"
+ "ECDSA keys with unnamed curves are unsupported at this time"
+ )
+
+ # This is like the above check, but it also catches the case where you
+ # explicitly encoded a curve with the same parameters as a named curve.
+ # Don't do that.
+ if (
+ not backend._lib.CRYPTOGRAPHY_IS_LIBRESSL
+ and backend._lib.EC_GROUP_get_asn1_flag(group) == 0
+ ):
+ raise NotImplementedError(
+ "ECDSA keys with unnamed curves are unsupported at this time"
)
curve_name = backend._lib.OBJ_nid2sn(nid)
backend.openssl_assert(curve_name != backend._ffi.NULL)
- sn = backend._ffi.string(curve_name).decode('ascii')
+ sn = backend._ffi.string(curve_name).decode("ascii")
return sn
@@ -62,8 +78,8 @@ def _sn_to_elliptic_curve(backend, sn):
return ec._CURVE_TYPES[sn]()
except KeyError:
raise UnsupportedAlgorithm(
- "{0} is not a supported elliptic curve".format(sn),
- _Reasons.UNSUPPORTED_ELLIPTIC_CURVE
+ "{} is not a supported elliptic curve".format(sn),
+ _Reasons.UNSUPPORTED_ELLIPTIC_CURVE,
)
@@ -77,7 +93,7 @@ def _ecdsa_sig_sign(backend, private_key, data):
0, data, len(data), sigbuf, siglen_ptr, private_key._ec_key
)
backend.openssl_assert(res == 1)
- return backend._ffi.buffer(sigbuf)[:siglen_ptr[0]]
+ return backend._ffi.buffer(sigbuf)[: siglen_ptr[0]]
def _ecdsa_sig_verify(backend, public_key, signature, data):
@@ -127,12 +143,12 @@ class _ECDSAVerificationContext(object):
class _EllipticCurvePrivateKey(object):
def __init__(self, backend, ec_key_cdata, evp_pkey):
self._backend = backend
- _mark_asn1_named_ec_curve(backend, ec_key_cdata)
self._ec_key = ec_key_cdata
self._evp_pkey = evp_pkey
sn = _ec_key_curve_sn(backend, ec_key_cdata)
self._curve = _sn_to_elliptic_curve(backend, sn)
+ _mark_asn1_named_ec_curve(backend, ec_key_cdata)
curve = utils.read_only_property("_curve")
@@ -156,7 +172,7 @@ class _EllipticCurvePrivateKey(object):
):
raise UnsupportedAlgorithm(
"This backend does not support the ECDH algorithm.",
- _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM
+ _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM,
)
if peer_public_key.curve.name != self.curve.name:
@@ -183,12 +199,7 @@ class _EllipticCurvePrivateKey(object):
self._backend.openssl_assert(group != self._backend._ffi.NULL)
curve_nid = self._backend._lib.EC_GROUP_get_curve_name(group)
-
- public_ec_key = self._backend._lib.EC_KEY_new_by_curve_name(curve_nid)
- self._backend.openssl_assert(public_ec_key != self._backend._ffi.NULL)
- public_ec_key = self._backend._ffi.gc(
- public_ec_key, self._backend._lib.EC_KEY_free
- )
+ public_ec_key = self._backend._ec_key_new_by_curve_nid(curve_nid)
point = self._backend._lib.EC_KEY_get0_public_key(self._ec_key)
self._backend.openssl_assert(point != self._backend._ffi.NULL)
@@ -205,7 +216,7 @@ class _EllipticCurvePrivateKey(object):
private_value = self._backend._bn_to_int(bn)
return ec.EllipticCurvePrivateNumbers(
private_value=private_value,
- public_numbers=self.public_key().public_numbers()
+ public_numbers=self.public_key().public_numbers(),
)
def private_bytes(self, encoding, format, encryption_algorithm):
@@ -213,8 +224,9 @@ class _EllipticCurvePrivateKey(object):
encoding,
format,
encryption_algorithm,
+ self,
self._evp_pkey,
- self._ec_key
+ self._ec_key,
)
def sign(self, data, signature_algorithm):
@@ -229,12 +241,12 @@ class _EllipticCurvePrivateKey(object):
class _EllipticCurvePublicKey(object):
def __init__(self, backend, ec_key_cdata, evp_pkey):
self._backend = backend
- _mark_asn1_named_ec_curve(backend, ec_key_cdata)
self._ec_key = ec_key_cdata
self._evp_pkey = evp_pkey
sn = _ec_key_curve_sn(backend, ec_key_cdata)
self._curve = _sn_to_elliptic_curve(backend, sn)
+ _mark_asn1_named_ec_curve(backend, ec_key_cdata)
curve = utils.read_only_property("_curve")
@@ -253,8 +265,8 @@ class _EllipticCurvePublicKey(object):
)
def public_numbers(self):
- get_func, group = (
- self._backend._ec_key_determine_group_get_func(self._ec_key)
+ get_func, group = self._backend._ec_key_determine_group_get_func(
+ self._ec_key
)
point = self._backend._lib.EC_KEY_get0_public_key(self._ec_key)
self._backend.openssl_assert(point != self._backend._ffi.NULL)
@@ -269,11 +281,7 @@ class _EllipticCurvePublicKey(object):
x = self._backend._bn_to_int(bn_x)
y = self._backend._bn_to_int(bn_y)
- return ec.EllipticCurvePublicNumbers(
- x=x,
- y=y,
- curve=self._curve
- )
+ return ec.EllipticCurvePublicNumbers(x=x, y=y, curve=self._curve)
def _encode_point(self, format):
if format is serialization.PublicFormat.CompressedPoint:
@@ -300,22 +308,15 @@ class _EllipticCurvePublicKey(object):
return self._backend._ffi.buffer(buf)[:]
def public_bytes(self, encoding, format):
- if format is serialization.PublicFormat.PKCS1:
- raise ValueError(
- "EC public keys do not support PKCS1 serialization"
- )
if (
- encoding is serialization.Encoding.X962 or
- format is serialization.PublicFormat.CompressedPoint or
- format is serialization.PublicFormat.UncompressedPoint
+ encoding is serialization.Encoding.X962
+ or format is serialization.PublicFormat.CompressedPoint
+ or format is serialization.PublicFormat.UncompressedPoint
):
- if (
- encoding is not serialization.Encoding.X962 or
- format not in (
- serialization.PublicFormat.CompressedPoint,
- serialization.PublicFormat.UncompressedPoint
- )
+ if encoding is not serialization.Encoding.X962 or format not in (
+ serialization.PublicFormat.CompressedPoint,
+ serialization.PublicFormat.UncompressedPoint,
):
raise ValueError(
"X962 encoding must be used with CompressedPoint or "
@@ -325,11 +326,7 @@ class _EllipticCurvePublicKey(object):
return self._encode_point(format)
else:
return self._backend._public_key_bytes(
- encoding,
- format,
- self,
- self._evp_pkey,
- None
+ encoding, format, self, self._evp_pkey, None
)
def verify(self, signature, data, signature_algorithm):
diff --git a/src/cryptography/hazmat/backends/openssl/ed25519.py b/src/cryptography/hazmat/backends/openssl/ed25519.py
new file mode 100644
index 000000000..13bec3af1
--- /dev/null
+++ b/src/cryptography/hazmat/backends/openssl/ed25519.py
@@ -0,0 +1,145 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import absolute_import, division, print_function
+
+from cryptography import exceptions, utils
+from cryptography.hazmat.primitives import serialization
+from cryptography.hazmat.primitives.asymmetric.ed25519 import (
+ Ed25519PrivateKey,
+ Ed25519PublicKey,
+ _ED25519_KEY_SIZE,
+ _ED25519_SIG_SIZE,
+)
+
+
+@utils.register_interface(Ed25519PublicKey)
+class _Ed25519PublicKey(object):
+ def __init__(self, backend, evp_pkey):
+ self._backend = backend
+ self._evp_pkey = evp_pkey
+
+ def public_bytes(self, encoding, format):
+ if (
+ encoding is serialization.Encoding.Raw
+ or format is serialization.PublicFormat.Raw
+ ):
+ if (
+ encoding is not serialization.Encoding.Raw
+ or format is not serialization.PublicFormat.Raw
+ ):
+ raise ValueError(
+ "When using Raw both encoding and format must be Raw"
+ )
+
+ return self._raw_public_bytes()
+
+ return self._backend._public_key_bytes(
+ encoding, format, self, self._evp_pkey, None
+ )
+
+ def _raw_public_bytes(self):
+ buf = self._backend._ffi.new("unsigned char []", _ED25519_KEY_SIZE)
+ buflen = self._backend._ffi.new("size_t *", _ED25519_KEY_SIZE)
+ res = self._backend._lib.EVP_PKEY_get_raw_public_key(
+ self._evp_pkey, buf, buflen
+ )
+ self._backend.openssl_assert(res == 1)
+ self._backend.openssl_assert(buflen[0] == _ED25519_KEY_SIZE)
+ return self._backend._ffi.buffer(buf, _ED25519_KEY_SIZE)[:]
+
+ def verify(self, signature, data):
+ evp_md_ctx = self._backend._lib.EVP_MD_CTX_new()
+ self._backend.openssl_assert(evp_md_ctx != self._backend._ffi.NULL)
+ evp_md_ctx = self._backend._ffi.gc(
+ evp_md_ctx, self._backend._lib.EVP_MD_CTX_free
+ )
+ res = self._backend._lib.EVP_DigestVerifyInit(
+ evp_md_ctx,
+ self._backend._ffi.NULL,
+ self._backend._ffi.NULL,
+ self._backend._ffi.NULL,
+ self._evp_pkey,
+ )
+ self._backend.openssl_assert(res == 1)
+ res = self._backend._lib.EVP_DigestVerify(
+ evp_md_ctx, signature, len(signature), data, len(data)
+ )
+ if res != 1:
+ self._backend._consume_errors()
+ raise exceptions.InvalidSignature
+
+
+@utils.register_interface(Ed25519PrivateKey)
+class _Ed25519PrivateKey(object):
+ def __init__(self, backend, evp_pkey):
+ self._backend = backend
+ self._evp_pkey = evp_pkey
+
+ def public_key(self):
+ buf = self._backend._ffi.new("unsigned char []", _ED25519_KEY_SIZE)
+ buflen = self._backend._ffi.new("size_t *", _ED25519_KEY_SIZE)
+ res = self._backend._lib.EVP_PKEY_get_raw_public_key(
+ self._evp_pkey, buf, buflen
+ )
+ self._backend.openssl_assert(res == 1)
+ self._backend.openssl_assert(buflen[0] == _ED25519_KEY_SIZE)
+ public_bytes = self._backend._ffi.buffer(buf)[:]
+ return self._backend.ed25519_load_public_bytes(public_bytes)
+
+ def sign(self, data):
+ evp_md_ctx = self._backend._lib.EVP_MD_CTX_new()
+ self._backend.openssl_assert(evp_md_ctx != self._backend._ffi.NULL)
+ evp_md_ctx = self._backend._ffi.gc(
+ evp_md_ctx, self._backend._lib.EVP_MD_CTX_free
+ )
+ res = self._backend._lib.EVP_DigestSignInit(
+ evp_md_ctx,
+ self._backend._ffi.NULL,
+ self._backend._ffi.NULL,
+ self._backend._ffi.NULL,
+ self._evp_pkey,
+ )
+ self._backend.openssl_assert(res == 1)
+ buf = self._backend._ffi.new("unsigned char[]", _ED25519_SIG_SIZE)
+ buflen = self._backend._ffi.new("size_t *", len(buf))
+ res = self._backend._lib.EVP_DigestSign(
+ evp_md_ctx, buf, buflen, data, len(data)
+ )
+ self._backend.openssl_assert(res == 1)
+ self._backend.openssl_assert(buflen[0] == _ED25519_SIG_SIZE)
+ return self._backend._ffi.buffer(buf, buflen[0])[:]
+
+ def private_bytes(self, encoding, format, encryption_algorithm):
+ if (
+ encoding is serialization.Encoding.Raw
+ or format is serialization.PublicFormat.Raw
+ ):
+ if (
+ format is not serialization.PrivateFormat.Raw
+ or encoding is not serialization.Encoding.Raw
+ or not isinstance(
+ encryption_algorithm, serialization.NoEncryption
+ )
+ ):
+ raise ValueError(
+ "When using Raw both encoding and format must be Raw "
+ "and encryption_algorithm must be NoEncryption()"
+ )
+
+ return self._raw_private_bytes()
+
+ return self._backend._private_key_bytes(
+ encoding, format, encryption_algorithm, self, self._evp_pkey, None
+ )
+
+ def _raw_private_bytes(self):
+ buf = self._backend._ffi.new("unsigned char []", _ED25519_KEY_SIZE)
+ buflen = self._backend._ffi.new("size_t *", _ED25519_KEY_SIZE)
+ res = self._backend._lib.EVP_PKEY_get_raw_private_key(
+ self._evp_pkey, buf, buflen
+ )
+ self._backend.openssl_assert(res == 1)
+ self._backend.openssl_assert(buflen[0] == _ED25519_KEY_SIZE)
+ return self._backend._ffi.buffer(buf, _ED25519_KEY_SIZE)[:]
diff --git a/src/cryptography/hazmat/backends/openssl/ed448.py b/src/cryptography/hazmat/backends/openssl/ed448.py
new file mode 100644
index 000000000..6512770e5
--- /dev/null
+++ b/src/cryptography/hazmat/backends/openssl/ed448.py
@@ -0,0 +1,146 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import absolute_import, division, print_function
+
+from cryptography import exceptions, utils
+from cryptography.hazmat.primitives import serialization
+from cryptography.hazmat.primitives.asymmetric.ed448 import (
+ Ed448PrivateKey,
+ Ed448PublicKey,
+)
+
+_ED448_KEY_SIZE = 57
+_ED448_SIG_SIZE = 114
+
+
+@utils.register_interface(Ed448PublicKey)
+class _Ed448PublicKey(object):
+ def __init__(self, backend, evp_pkey):
+ self._backend = backend
+ self._evp_pkey = evp_pkey
+
+ def public_bytes(self, encoding, format):
+ if (
+ encoding is serialization.Encoding.Raw
+ or format is serialization.PublicFormat.Raw
+ ):
+ if (
+ encoding is not serialization.Encoding.Raw
+ or format is not serialization.PublicFormat.Raw
+ ):
+ raise ValueError(
+ "When using Raw both encoding and format must be Raw"
+ )
+
+ return self._raw_public_bytes()
+
+ return self._backend._public_key_bytes(
+ encoding, format, self, self._evp_pkey, None
+ )
+
+ def _raw_public_bytes(self):
+ buf = self._backend._ffi.new("unsigned char []", _ED448_KEY_SIZE)
+ buflen = self._backend._ffi.new("size_t *", _ED448_KEY_SIZE)
+ res = self._backend._lib.EVP_PKEY_get_raw_public_key(
+ self._evp_pkey, buf, buflen
+ )
+ self._backend.openssl_assert(res == 1)
+ self._backend.openssl_assert(buflen[0] == _ED448_KEY_SIZE)
+ return self._backend._ffi.buffer(buf, _ED448_KEY_SIZE)[:]
+
+ def verify(self, signature, data):
+ evp_md_ctx = self._backend._lib.EVP_MD_CTX_new()
+ self._backend.openssl_assert(evp_md_ctx != self._backend._ffi.NULL)
+ evp_md_ctx = self._backend._ffi.gc(
+ evp_md_ctx, self._backend._lib.EVP_MD_CTX_free
+ )
+ res = self._backend._lib.EVP_DigestVerifyInit(
+ evp_md_ctx,
+ self._backend._ffi.NULL,
+ self._backend._ffi.NULL,
+ self._backend._ffi.NULL,
+ self._evp_pkey,
+ )
+ self._backend.openssl_assert(res == 1)
+ res = self._backend._lib.EVP_DigestVerify(
+ evp_md_ctx, signature, len(signature), data, len(data)
+ )
+ if res != 1:
+ self._backend._consume_errors()
+ raise exceptions.InvalidSignature
+
+
+@utils.register_interface(Ed448PrivateKey)
+class _Ed448PrivateKey(object):
+ def __init__(self, backend, evp_pkey):
+ self._backend = backend
+ self._evp_pkey = evp_pkey
+
+ def public_key(self):
+ buf = self._backend._ffi.new("unsigned char []", _ED448_KEY_SIZE)
+ buflen = self._backend._ffi.new("size_t *", _ED448_KEY_SIZE)
+ res = self._backend._lib.EVP_PKEY_get_raw_public_key(
+ self._evp_pkey, buf, buflen
+ )
+ self._backend.openssl_assert(res == 1)
+ self._backend.openssl_assert(buflen[0] == _ED448_KEY_SIZE)
+ public_bytes = self._backend._ffi.buffer(buf)[:]
+ return self._backend.ed448_load_public_bytes(public_bytes)
+
+ def sign(self, data):
+ evp_md_ctx = self._backend._lib.EVP_MD_CTX_new()
+ self._backend.openssl_assert(evp_md_ctx != self._backend._ffi.NULL)
+ evp_md_ctx = self._backend._ffi.gc(
+ evp_md_ctx, self._backend._lib.EVP_MD_CTX_free
+ )
+ res = self._backend._lib.EVP_DigestSignInit(
+ evp_md_ctx,
+ self._backend._ffi.NULL,
+ self._backend._ffi.NULL,
+ self._backend._ffi.NULL,
+ self._evp_pkey,
+ )
+ self._backend.openssl_assert(res == 1)
+ buf = self._backend._ffi.new("unsigned char[]", _ED448_SIG_SIZE)
+ buflen = self._backend._ffi.new("size_t *", len(buf))
+ res = self._backend._lib.EVP_DigestSign(
+ evp_md_ctx, buf, buflen, data, len(data)
+ )
+ self._backend.openssl_assert(res == 1)
+ self._backend.openssl_assert(buflen[0] == _ED448_SIG_SIZE)
+ return self._backend._ffi.buffer(buf, buflen[0])[:]
+
+ def private_bytes(self, encoding, format, encryption_algorithm):
+ if (
+ encoding is serialization.Encoding.Raw
+ or format is serialization.PublicFormat.Raw
+ ):
+ if (
+ format is not serialization.PrivateFormat.Raw
+ or encoding is not serialization.Encoding.Raw
+ or not isinstance(
+ encryption_algorithm, serialization.NoEncryption
+ )
+ ):
+ raise ValueError(
+ "When using Raw both encoding and format must be Raw "
+ "and encryption_algorithm must be NoEncryption()"
+ )
+
+ return self._raw_private_bytes()
+
+ return self._backend._private_key_bytes(
+ encoding, format, encryption_algorithm, self, self._evp_pkey, None
+ )
+
+ def _raw_private_bytes(self):
+ buf = self._backend._ffi.new("unsigned char []", _ED448_KEY_SIZE)
+ buflen = self._backend._ffi.new("size_t *", _ED448_KEY_SIZE)
+ res = self._backend._lib.EVP_PKEY_get_raw_private_key(
+ self._evp_pkey, buf, buflen
+ )
+ self._backend.openssl_assert(res == 1)
+ self._backend.openssl_assert(buflen[0] == _ED448_KEY_SIZE)
+ return self._backend._ffi.buffer(buf, _ED448_KEY_SIZE)[:]
diff --git a/src/cryptography/hazmat/backends/openssl/encode_asn1.py b/src/cryptography/hazmat/backends/openssl/encode_asn1.py
index 355757624..0a33200bb 100644
--- a/src/cryptography/hazmat/backends/openssl/encode_asn1.py
+++ b/src/cryptography/hazmat/backends/openssl/encode_asn1.py
@@ -11,12 +11,15 @@ import six
from cryptography import utils, x509
from cryptography.hazmat.backends.openssl.decode_asn1 import (
- _CRL_ENTRY_REASON_ENUM_TO_CODE, _DISTPOINT_TYPE_FULLNAME,
- _DISTPOINT_TYPE_RELATIVENAME
+ _CRL_ENTRY_REASON_ENUM_TO_CODE,
+ _DISTPOINT_TYPE_FULLNAME,
+ _DISTPOINT_TYPE_RELATIVENAME,
)
from cryptography.x509.name import _ASN1Type
from cryptography.x509.oid import (
- CRLEntryExtensionOID, ExtensionOID, OCSPExtensionOID,
+ CRLEntryExtensionOID,
+ ExtensionOID,
+ OCSPExtensionOID,
)
@@ -94,7 +97,8 @@ def _encode_name(backend, name):
name_entry, backend._lib.X509_NAME_ENTRY_free
)
res = backend._lib.X509_NAME_add_entry(
- subject, name_entry, -1, set_flag)
+ subject, name_entry, -1, set_flag
+ )
backend.openssl_assert(res == 1)
set_flag = -1
return subject
@@ -120,9 +124,11 @@ def _encode_sk_name_entry(backend, attributes):
def _encode_name_entry(backend, attribute):
if attribute._type is _ASN1Type.BMPString:
- value = attribute.value.encode('utf_16_be')
+ value = attribute.value.encode("utf_16_be")
+ elif attribute._type is _ASN1Type.UniversalString:
+ value = attribute.value.encode("utf_32_be")
else:
- value = attribute.value.encode('utf8')
+ value = attribute.value.encode("utf8")
obj = _txt2obj_gc(backend, attribute.oid.dotted_string)
@@ -172,9 +178,8 @@ def _encode_crl_reason(backend, crl_reason):
def _encode_invalidity_date(backend, invalidity_date):
time = backend._lib.ASN1_GENERALIZEDTIME_set(
- backend._ffi.NULL, calendar.timegm(
- invalidity_date.invalidity_date.timetuple()
- )
+ backend._ffi.NULL,
+ calendar.timegm(invalidity_date.invalidity_date.timetuple()),
)
backend.openssl_assert(time != backend._ffi.NULL)
time = backend._ffi.gc(time, backend._lib.ASN1_GENERALIZEDTIME_free)
@@ -255,7 +260,7 @@ def _txt2obj(backend, name):
Converts a Python string with an ASN.1 object ID in dotted form to a
ASN1_OBJECT.
"""
- name = name.encode('ascii')
+ name = name.encode("ascii")
obj = backend._lib.OBJ_txt2obj(name, 1)
backend.openssl_assert(obj != backend._ffi.NULL)
return obj
@@ -341,20 +346,27 @@ def _encode_basic_constraints(backend, basic_constraints):
return constraints
-def _encode_authority_information_access(backend, authority_info_access):
+def _encode_information_access(backend, info_access):
aia = backend._lib.sk_ACCESS_DESCRIPTION_new_null()
backend.openssl_assert(aia != backend._ffi.NULL)
aia = backend._ffi.gc(
- aia, backend._lib.sk_ACCESS_DESCRIPTION_free
+ aia,
+ lambda x: backend._lib.sk_ACCESS_DESCRIPTION_pop_free(
+ x,
+ backend._ffi.addressof(
+ backend._lib._original_lib, "ACCESS_DESCRIPTION_free"
+ ),
+ ),
)
- for access_description in authority_info_access:
+ for access_description in info_access:
ad = backend._lib.ACCESS_DESCRIPTION_new()
method = _txt2obj(
backend, access_description.access_method.dotted_string
)
- gn = _encode_general_name(backend, access_description.access_location)
+ _encode_general_name_preallocated(
+ backend, access_description.access_location, ad.location
+ )
ad.method = method
- ad.location = gn
res = backend._lib.sk_ACCESS_DESCRIPTION_push(aia, ad)
backend.openssl_assert(res >= 1)
@@ -385,8 +397,13 @@ def _encode_subject_key_identifier(backend, ski):
def _encode_general_name(backend, name):
+ gn = backend._lib.GENERAL_NAME_new()
+ _encode_general_name_preallocated(backend, name, gn)
+ return gn
+
+
+def _encode_general_name_preallocated(backend, name, gn):
if isinstance(name, x509.DNSName):
- gn = backend._lib.GENERAL_NAME_new()
backend.openssl_assert(gn != backend._ffi.NULL)
gn.type = backend._lib.GEN_DNS
@@ -400,32 +417,27 @@ def _encode_general_name(backend, name):
backend.openssl_assert(res == 1)
gn.d.dNSName = ia5
elif isinstance(name, x509.RegisteredID):
- gn = backend._lib.GENERAL_NAME_new()
backend.openssl_assert(gn != backend._ffi.NULL)
gn.type = backend._lib.GEN_RID
obj = backend._lib.OBJ_txt2obj(
- name.value.dotted_string.encode('ascii'), 1
+ name.value.dotted_string.encode("ascii"), 1
)
backend.openssl_assert(obj != backend._ffi.NULL)
gn.d.registeredID = obj
elif isinstance(name, x509.DirectoryName):
- gn = backend._lib.GENERAL_NAME_new()
backend.openssl_assert(gn != backend._ffi.NULL)
dir_name = _encode_name(backend, name.value)
gn.type = backend._lib.GEN_DIRNAME
gn.d.directoryName = dir_name
elif isinstance(name, x509.IPAddress):
- gn = backend._lib.GENERAL_NAME_new()
backend.openssl_assert(gn != backend._ffi.NULL)
if isinstance(name.value, ipaddress.IPv4Network):
- packed = (
- name.value.network_address.packed +
- utils.int_to_bytes(((1 << 32) - name.value.num_addresses), 4)
+ packed = name.value.network_address.packed + utils.int_to_bytes(
+ ((1 << 32) - name.value.num_addresses), 4
)
elif isinstance(name.value, ipaddress.IPv6Network):
- packed = (
- name.value.network_address.packed +
- utils.int_to_bytes((1 << 128) - name.value.num_addresses, 16)
+ packed = name.value.network_address.packed + utils.int_to_bytes(
+ (1 << 128) - name.value.num_addresses, 16
)
else:
packed = name.value.packed
@@ -433,13 +445,12 @@ def _encode_general_name(backend, name):
gn.type = backend._lib.GEN_IPADD
gn.d.iPAddress = ipaddr
elif isinstance(name, x509.OtherName):
- gn = backend._lib.GENERAL_NAME_new()
backend.openssl_assert(gn != backend._ffi.NULL)
other_name = backend._lib.OTHERNAME_new()
backend.openssl_assert(other_name != backend._ffi.NULL)
type_id = backend._lib.OBJ_txt2obj(
- name.type_id.dotted_string.encode('ascii'), 1
+ name.type_id.dotted_string.encode("ascii"), 1
)
backend.openssl_assert(type_id != backend._ffi.NULL)
data = backend._ffi.new("unsigned char[]", name.value)
@@ -456,7 +467,6 @@ def _encode_general_name(backend, name):
gn.type = backend._lib.GEN_OTHERNAME
gn.d.otherName = other_name
elif isinstance(name, x509.RFC822Name):
- gn = backend._lib.GENERAL_NAME_new()
backend.openssl_assert(gn != backend._ffi.NULL)
# ia5strings are supposed to be ITU T.50 but to allow round-tripping
# of broken certs that encode utf8 we'll encode utf8 here too.
@@ -465,7 +475,6 @@ def _encode_general_name(backend, name):
gn.type = backend._lib.GEN_EMAIL
gn.d.rfc822Name = asn1_str
elif isinstance(name, x509.UniformResourceIdentifier):
- gn = backend._lib.GENERAL_NAME_new()
backend.openssl_assert(gn != backend._ffi.NULL)
# ia5strings are supposed to be ITU T.50 but to allow round-tripping
# of broken certs that encode utf8 we'll encode utf8 here too.
@@ -474,11 +483,7 @@ def _encode_general_name(backend, name):
gn.type = backend._lib.GEN_URI
gn.d.uniformResourceIdentifier = asn1_str
else:
- raise ValueError(
- "{0} is an unknown GeneralName type".format(name)
- )
-
- return gn
+ raise ValueError("{} is an unknown GeneralName type".format(name))
def _encode_extended_key_usage(backend, extended_key_usage):
@@ -599,11 +604,21 @@ def _encode_general_subtree(backend, subtrees):
gs = backend._lib.GENERAL_SUBTREE_new()
gs.base = _encode_general_name(backend, name)
res = backend._lib.sk_GENERAL_SUBTREE_push(general_subtrees, gs)
- assert res >= 1
+ backend.openssl_assert(res >= 1)
return general_subtrees
+def _encode_precert_signed_certificate_timestamps(backend, scts):
+ sct_stack = backend._lib.sk_SCT_new_null()
+ backend.openssl_assert(sct_stack != backend._ffi.NULL)
+ sct_stack = backend._ffi.gc(sct_stack, backend._lib.sk_SCT_free)
+ for sct in scts:
+ res = backend._lib.sk_SCT_push(sct_stack, sct._sct)
+ backend.openssl_assert(res >= 1)
+ return sct_stack
+
+
def _encode_nonce(backend, nonce):
return _encode_asn1_str_gc(backend, nonce.nonce)
@@ -617,26 +632,27 @@ _EXTENSION_ENCODE_HANDLERS = {
ExtensionOID.EXTENDED_KEY_USAGE: _encode_extended_key_usage,
ExtensionOID.AUTHORITY_KEY_IDENTIFIER: _encode_authority_key_identifier,
ExtensionOID.CERTIFICATE_POLICIES: _encode_certificate_policies,
- ExtensionOID.AUTHORITY_INFORMATION_ACCESS: (
- _encode_authority_information_access
- ),
+ ExtensionOID.AUTHORITY_INFORMATION_ACCESS: _encode_information_access,
+ ExtensionOID.SUBJECT_INFORMATION_ACCESS: _encode_information_access,
ExtensionOID.CRL_DISTRIBUTION_POINTS: _encode_cdps_freshest_crl,
ExtensionOID.FRESHEST_CRL: _encode_cdps_freshest_crl,
ExtensionOID.INHIBIT_ANY_POLICY: _encode_inhibit_any_policy,
ExtensionOID.OCSP_NO_CHECK: _encode_ocsp_nocheck,
ExtensionOID.NAME_CONSTRAINTS: _encode_name_constraints,
ExtensionOID.POLICY_CONSTRAINTS: _encode_policy_constraints,
+ ExtensionOID.PRECERT_SIGNED_CERTIFICATE_TIMESTAMPS: (
+ _encode_precert_signed_certificate_timestamps
+ ),
}
_CRL_EXTENSION_ENCODE_HANDLERS = {
ExtensionOID.ISSUER_ALTERNATIVE_NAME: _encode_alt_name,
ExtensionOID.AUTHORITY_KEY_IDENTIFIER: _encode_authority_key_identifier,
- ExtensionOID.AUTHORITY_INFORMATION_ACCESS: (
- _encode_authority_information_access
- ),
+ ExtensionOID.AUTHORITY_INFORMATION_ACCESS: _encode_information_access,
ExtensionOID.CRL_NUMBER: _encode_crl_number_delta_crl_indicator,
ExtensionOID.DELTA_CRL_INDICATOR: _encode_crl_number_delta_crl_indicator,
ExtensionOID.ISSUING_DISTRIBUTION_POINT: _encode_issuing_dist_point,
+ ExtensionOID.FRESHEST_CRL: _encode_cdps_freshest_crl,
}
_CRL_ENTRY_EXTENSION_ENCODE_HANDLERS = {
diff --git a/src/cryptography/hazmat/backends/openssl/hashes.py b/src/cryptography/hazmat/backends/openssl/hashes.py
index 549fa2bf5..764dce0ed 100644
--- a/src/cryptography/hazmat/backends/openssl/hashes.py
+++ b/src/cryptography/hazmat/backends/openssl/hashes.py
@@ -18,19 +18,21 @@ class _HashContext(object):
self._backend = backend
if ctx is None:
- ctx = self._backend._lib.Cryptography_EVP_MD_CTX_new()
+ ctx = self._backend._lib.EVP_MD_CTX_new()
ctx = self._backend._ffi.gc(
- ctx, self._backend._lib.Cryptography_EVP_MD_CTX_free
+ ctx, self._backend._lib.EVP_MD_CTX_free
)
evp_md = self._backend._evp_md_from_algorithm(algorithm)
if evp_md == self._backend._ffi.NULL:
raise UnsupportedAlgorithm(
- "{0} is not a supported hash on this backend.".format(
- algorithm.name),
- _Reasons.UNSUPPORTED_HASH
+ "{} is not a supported hash on this backend.".format(
+ algorithm.name
+ ),
+ _Reasons.UNSUPPORTED_HASH,
)
- res = self._backend._lib.EVP_DigestInit_ex(ctx, evp_md,
- self._backend._ffi.NULL)
+ res = self._backend._lib.EVP_DigestInit_ex(
+ ctx, evp_md, self._backend._ffi.NULL
+ )
self._backend.openssl_assert(res != 0)
self._ctx = ctx
@@ -38,9 +40,9 @@ class _HashContext(object):
algorithm = utils.read_only_property("_algorithm")
def copy(self):
- copied_ctx = self._backend._lib.Cryptography_EVP_MD_CTX_new()
+ copied_ctx = self._backend._lib.EVP_MD_CTX_new()
copied_ctx = self._backend._ffi.gc(
- copied_ctx, self._backend._lib.Cryptography_EVP_MD_CTX_free
+ copied_ctx, self._backend._lib.EVP_MD_CTX_free
)
res = self._backend._lib.EVP_MD_CTX_copy_ex(copied_ctx, self._ctx)
self._backend.openssl_assert(res != 0)
@@ -58,21 +60,23 @@ class _HashContext(object):
# extendable output functions use a different finalize
return self._finalize_xof()
else:
- buf = self._backend._ffi.new("unsigned char[]",
- self._backend._lib.EVP_MAX_MD_SIZE)
+ buf = self._backend._ffi.new(
+ "unsigned char[]", self._backend._lib.EVP_MAX_MD_SIZE
+ )
outlen = self._backend._ffi.new("unsigned int *")
res = self._backend._lib.EVP_DigestFinal_ex(self._ctx, buf, outlen)
self._backend.openssl_assert(res != 0)
self._backend.openssl_assert(
outlen[0] == self.algorithm.digest_size
)
- return self._backend._ffi.buffer(buf)[:outlen[0]]
+ return self._backend._ffi.buffer(buf)[: outlen[0]]
def _finalize_xof(self):
- buf = self._backend._ffi.new("unsigned char[]",
- self.algorithm.digest_size)
+ buf = self._backend._ffi.new(
+ "unsigned char[]", self.algorithm.digest_size
+ )
res = self._backend._lib.EVP_DigestFinalXOF(
self._ctx, buf, self.algorithm.digest_size
)
self._backend.openssl_assert(res != 0)
- return self._backend._ffi.buffer(buf)[:self.algorithm.digest_size]
+ return self._backend._ffi.buffer(buf)[: self.algorithm.digest_size]
diff --git a/src/cryptography/hazmat/backends/openssl/hmac.py b/src/cryptography/hazmat/backends/openssl/hmac.py
index b23ac6498..1cc9d99fe 100644
--- a/src/cryptography/hazmat/backends/openssl/hmac.py
+++ b/src/cryptography/hazmat/backends/openssl/hmac.py
@@ -7,12 +7,13 @@ from __future__ import absolute_import, division, print_function
from cryptography import utils
from cryptography.exceptions import (
- InvalidSignature, UnsupportedAlgorithm, _Reasons
+ InvalidSignature,
+ UnsupportedAlgorithm,
+ _Reasons,
)
-from cryptography.hazmat.primitives import constant_time, hashes, mac
+from cryptography.hazmat.primitives import constant_time, hashes
-@utils.register_interface(mac.MACContext)
@utils.register_interface(hashes.HashContext)
class _HMACContext(object):
def __init__(self, backend, key, algorithm, ctx=None):
@@ -20,17 +21,16 @@ class _HMACContext(object):
self._backend = backend
if ctx is None:
- ctx = self._backend._lib.Cryptography_HMAC_CTX_new()
+ ctx = self._backend._lib.HMAC_CTX_new()
self._backend.openssl_assert(ctx != self._backend._ffi.NULL)
- ctx = self._backend._ffi.gc(
- ctx, self._backend._lib.Cryptography_HMAC_CTX_free
- )
+ ctx = self._backend._ffi.gc(ctx, self._backend._lib.HMAC_CTX_free)
evp_md = self._backend._evp_md_from_algorithm(algorithm)
if evp_md == self._backend._ffi.NULL:
raise UnsupportedAlgorithm(
- "{0} is not a supported hash on this backend".format(
- algorithm.name),
- _Reasons.UNSUPPORTED_HASH
+ "{} is not a supported hash on this backend".format(
+ algorithm.name
+ ),
+ _Reasons.UNSUPPORTED_HASH,
)
key_ptr = self._backend._ffi.from_buffer(key)
res = self._backend._lib.HMAC_Init_ex(
@@ -44,10 +44,10 @@ class _HMACContext(object):
algorithm = utils.read_only_property("_algorithm")
def copy(self):
- copied_ctx = self._backend._lib.Cryptography_HMAC_CTX_new()
+ copied_ctx = self._backend._lib.HMAC_CTX_new()
self._backend.openssl_assert(copied_ctx != self._backend._ffi.NULL)
copied_ctx = self._backend._ffi.gc(
- copied_ctx, self._backend._lib.Cryptography_HMAC_CTX_free
+ copied_ctx, self._backend._lib.HMAC_CTX_free
)
res = self._backend._lib.HMAC_CTX_copy(copied_ctx, self._ctx)
self._backend.openssl_assert(res != 0)
@@ -61,13 +61,14 @@ class _HMACContext(object):
self._backend.openssl_assert(res != 0)
def finalize(self):
- buf = self._backend._ffi.new("unsigned char[]",
- self._backend._lib.EVP_MAX_MD_SIZE)
+ buf = self._backend._ffi.new(
+ "unsigned char[]", self._backend._lib.EVP_MAX_MD_SIZE
+ )
outlen = self._backend._ffi.new("unsigned int *")
res = self._backend._lib.HMAC_Final(self._ctx, buf, outlen)
self._backend.openssl_assert(res != 0)
self._backend.openssl_assert(outlen[0] == self.algorithm.digest_size)
- return self._backend._ffi.buffer(buf)[:outlen[0]]
+ return self._backend._ffi.buffer(buf)[: outlen[0]]
def verify(self, signature):
digest = self.finalize()
diff --git a/src/cryptography/hazmat/backends/openssl/ocsp.py b/src/cryptography/hazmat/backends/openssl/ocsp.py
index 16dbbc2ae..50c02e7a8 100644
--- a/src/cryptography/hazmat/backends/openssl/ocsp.py
+++ b/src/cryptography/hazmat/backends/openssl/ocsp.py
@@ -9,16 +9,23 @@ import functools
from cryptography import utils, x509
from cryptography.exceptions import UnsupportedAlgorithm
from cryptography.hazmat.backends.openssl.decode_asn1 import (
- _CRL_ENTRY_REASON_CODE_TO_ENUM, _OCSP_BASICRESP_EXT_PARSER,
- _OCSP_REQ_EXT_PARSER, _asn1_integer_to_int,
- _asn1_string_to_bytes, _decode_x509_name, _obj2txt,
+ _CRL_ENTRY_REASON_CODE_TO_ENUM,
+ _asn1_integer_to_int,
+ _asn1_string_to_bytes,
+ _decode_x509_name,
+ _obj2txt,
_parse_asn1_generalized_time,
)
from cryptography.hazmat.backends.openssl.x509 import _Certificate
from cryptography.hazmat.primitives import serialization
from cryptography.x509.ocsp import (
- OCSPCertStatus, OCSPRequest, OCSPResponse, OCSPResponseStatus,
- _CERT_STATUS_TO_ENUM, _OIDS_TO_HASH, _RESPONSE_STATUS_TO_ENUM,
+ OCSPCertStatus,
+ OCSPRequest,
+ OCSPResponse,
+ OCSPResponseStatus,
+ _CERT_STATUS_TO_ENUM,
+ _OIDS_TO_HASH,
+ _RESPONSE_STATUS_TO_ENUM,
)
@@ -39,8 +46,11 @@ def _requires_successful_response(func):
def _issuer_key_hash(backend, cert_id):
key_hash = backend._ffi.new("ASN1_OCTET_STRING **")
res = backend._lib.OCSP_id_get0_info(
- backend._ffi.NULL, backend._ffi.NULL,
- key_hash, backend._ffi.NULL, cert_id
+ backend._ffi.NULL,
+ backend._ffi.NULL,
+ key_hash,
+ backend._ffi.NULL,
+ cert_id,
)
backend.openssl_assert(res == 1)
backend.openssl_assert(key_hash[0] != backend._ffi.NULL)
@@ -50,8 +60,11 @@ def _issuer_key_hash(backend, cert_id):
def _issuer_name_hash(backend, cert_id):
name_hash = backend._ffi.new("ASN1_OCTET_STRING **")
res = backend._lib.OCSP_id_get0_info(
- name_hash, backend._ffi.NULL,
- backend._ffi.NULL, backend._ffi.NULL, cert_id
+ name_hash,
+ backend._ffi.NULL,
+ backend._ffi.NULL,
+ backend._ffi.NULL,
+ cert_id,
)
backend.openssl_assert(res == 1)
backend.openssl_assert(name_hash[0] != backend._ffi.NULL)
@@ -61,8 +74,7 @@ def _issuer_name_hash(backend, cert_id):
def _serial_number(backend, cert_id):
num = backend._ffi.new("ASN1_INTEGER **")
res = backend._lib.OCSP_id_get0_info(
- backend._ffi.NULL, backend._ffi.NULL,
- backend._ffi.NULL, num, cert_id
+ backend._ffi.NULL, backend._ffi.NULL, backend._ffi.NULL, num, cert_id
)
backend.openssl_assert(res == 1)
backend.openssl_assert(num[0] != backend._ffi.NULL)
@@ -72,8 +84,11 @@ def _serial_number(backend, cert_id):
def _hash_algorithm(backend, cert_id):
asn1obj = backend._ffi.new("ASN1_OBJECT **")
res = backend._lib.OCSP_id_get0_info(
- backend._ffi.NULL, asn1obj,
- backend._ffi.NULL, backend._ffi.NULL, cert_id
+ backend._ffi.NULL,
+ asn1obj,
+ backend._ffi.NULL,
+ backend._ffi.NULL,
+ cert_id,
)
backend.openssl_assert(res == 1)
backend.openssl_assert(asn1obj[0] != backend._ffi.NULL)
@@ -82,7 +97,7 @@ def _hash_algorithm(backend, cert_id):
return _OIDS_TO_HASH[oid]
except KeyError:
raise UnsupportedAlgorithm(
- "Signature algorithm OID: {0} not recognized".format(oid)
+ "Signature algorithm OID: {} not recognized".format(oid)
)
@@ -102,9 +117,13 @@ class _OCSPResponse(object):
self._basic = self._backend._ffi.gc(
basic, self._backend._lib.OCSP_BASICRESP_free
)
- self._backend.openssl_assert(
- self._backend._lib.OCSP_resp_count(self._basic) == 1
- )
+ num_resp = self._backend._lib.OCSP_resp_count(self._basic)
+ if num_resp != 1:
+ raise ValueError(
+ "OCSP response contains more than one SINGLERESP structure"
+ ", which this library does not support. "
+ "{} found".format(num_resp)
+ )
self._single = self._backend._lib.OCSP_resp_get0(self._basic, 0)
self._backend.openssl_assert(
self._single != self._backend._ffi.NULL
@@ -134,7 +153,7 @@ class _OCSPResponse(object):
return x509._SIG_OIDS_TO_HASH[oid]
except KeyError:
raise UnsupportedAlgorithm(
- "Signature algorithm OID:{0} not recognized".format(oid)
+ "Signature algorithm OID:{} not recognized".format(oid)
)
@property
@@ -317,13 +336,16 @@ class _OCSPResponse(object):
@utils.cached_property
@_requires_successful_response
def extensions(self):
- return _OCSP_BASICRESP_EXT_PARSER.parse(self._backend, self._basic)
+ return self._backend._ocsp_basicresp_ext_parser.parse(self._basic)
+
+ @utils.cached_property
+ @_requires_successful_response
+ def single_extensions(self):
+ return self._backend._ocsp_singleresp_ext_parser.parse(self._single)
def public_bytes(self, encoding):
if encoding is not serialization.Encoding.DER:
- raise ValueError(
- "The only allowed encoding value is Encoding.DER"
- )
+ raise ValueError("The only allowed encoding value is Encoding.DER")
bio = self._backend._create_mem_bio_gc()
res = self._backend._lib.i2d_OCSP_RESPONSE_bio(
@@ -338,7 +360,7 @@ class _OCSPRequest(object):
def __init__(self, backend, ocsp_request):
if backend._lib.OCSP_request_onereq_count(ocsp_request) > 1:
raise NotImplementedError(
- 'OCSP request contains more than one request'
+ "OCSP request contains more than one request"
)
self._backend = backend
self._ocsp_request = ocsp_request
@@ -367,13 +389,11 @@ class _OCSPRequest(object):
@utils.cached_property
def extensions(self):
- return _OCSP_REQ_EXT_PARSER.parse(self._backend, self._ocsp_request)
+ return self._backend._ocsp_req_ext_parser.parse(self._ocsp_request)
def public_bytes(self, encoding):
if encoding is not serialization.Encoding.DER:
- raise ValueError(
- "The only allowed encoding value is Encoding.DER"
- )
+ raise ValueError("The only allowed encoding value is Encoding.DER")
bio = self._backend._create_mem_bio_gc()
res = self._backend._lib.i2d_OCSP_REQUEST_bio(bio, self._ocsp_request)
diff --git a/src/cryptography/hazmat/backends/openssl/poly1305.py b/src/cryptography/hazmat/backends/openssl/poly1305.py
new file mode 100644
index 000000000..5699918b1
--- /dev/null
+++ b/src/cryptography/hazmat/backends/openssl/poly1305.py
@@ -0,0 +1,65 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import absolute_import, division, print_function
+
+
+from cryptography.exceptions import InvalidSignature
+from cryptography.hazmat.primitives import constant_time
+
+
+_POLY1305_TAG_SIZE = 16
+_POLY1305_KEY_SIZE = 32
+
+
+class _Poly1305Context(object):
+ def __init__(self, backend, key):
+ self._backend = backend
+
+ key_ptr = self._backend._ffi.from_buffer(key)
+ # This function copies the key into OpenSSL-owned memory so we don't
+ # need to retain it ourselves
+ evp_pkey = self._backend._lib.EVP_PKEY_new_raw_private_key(
+ self._backend._lib.NID_poly1305,
+ self._backend._ffi.NULL,
+ key_ptr,
+ len(key),
+ )
+ self._backend.openssl_assert(evp_pkey != self._backend._ffi.NULL)
+ self._evp_pkey = self._backend._ffi.gc(
+ evp_pkey, self._backend._lib.EVP_PKEY_free
+ )
+ ctx = self._backend._lib.EVP_MD_CTX_new()
+ self._backend.openssl_assert(ctx != self._backend._ffi.NULL)
+ self._ctx = self._backend._ffi.gc(
+ ctx, self._backend._lib.EVP_MD_CTX_free
+ )
+ res = self._backend._lib.EVP_DigestSignInit(
+ self._ctx,
+ self._backend._ffi.NULL,
+ self._backend._ffi.NULL,
+ self._backend._ffi.NULL,
+ self._evp_pkey,
+ )
+ self._backend.openssl_assert(res == 1)
+
+ def update(self, data):
+ data_ptr = self._backend._ffi.from_buffer(data)
+ res = self._backend._lib.EVP_DigestSignUpdate(
+ self._ctx, data_ptr, len(data)
+ )
+ self._backend.openssl_assert(res != 0)
+
+ def finalize(self):
+ buf = self._backend._ffi.new("unsigned char[]", _POLY1305_TAG_SIZE)
+ outlen = self._backend._ffi.new("size_t *")
+ res = self._backend._lib.EVP_DigestSignFinal(self._ctx, buf, outlen)
+ self._backend.openssl_assert(res != 0)
+ self._backend.openssl_assert(outlen[0] == _POLY1305_TAG_SIZE)
+ return self._backend._ffi.buffer(buf)[: outlen[0]]
+
+ def verify(self, tag):
+ mac = self.finalize()
+ if not constant_time.bytes_eq(mac, tag):
+ raise InvalidSignature("Value did not match computed tag.")
diff --git a/src/cryptography/hazmat/backends/openssl/rsa.py b/src/cryptography/hazmat/backends/openssl/rsa.py
index 30d59bd80..82cd49c96 100644
--- a/src/cryptography/hazmat/backends/openssl/rsa.py
+++ b/src/cryptography/hazmat/backends/openssl/rsa.py
@@ -4,25 +4,34 @@
from __future__ import absolute_import, division, print_function
-import math
-
from cryptography import utils
from cryptography.exceptions import (
- InvalidSignature, UnsupportedAlgorithm, _Reasons
+ InvalidSignature,
+ UnsupportedAlgorithm,
+ _Reasons,
)
from cryptography.hazmat.backends.openssl.utils import (
- _calculate_digest_and_algorithm, _check_not_prehashed,
- _warn_sign_verify_deprecated
+ _calculate_digest_and_algorithm,
+ _check_not_prehashed,
+ _warn_sign_verify_deprecated,
)
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import (
- AsymmetricSignatureContext, AsymmetricVerificationContext, rsa
+ AsymmetricSignatureContext,
+ AsymmetricVerificationContext,
+ rsa,
)
from cryptography.hazmat.primitives.asymmetric.padding import (
- AsymmetricPadding, MGF1, OAEP, PKCS1v15, PSS, calculate_max_pss_salt_length
+ AsymmetricPadding,
+ MGF1,
+ OAEP,
+ PKCS1v15,
+ PSS,
+ calculate_max_pss_salt_length,
)
from cryptography.hazmat.primitives.asymmetric.rsa import (
- RSAPrivateKeyWithSerialization, RSAPublicKeyWithSerialization
+ RSAPrivateKeyWithSerialization,
+ RSAPublicKeyWithSerialization,
)
@@ -47,22 +56,20 @@ def _enc_dec_rsa(backend, key, data, padding):
if not isinstance(padding._mgf, MGF1):
raise UnsupportedAlgorithm(
"Only MGF1 is supported by this backend.",
- _Reasons.UNSUPPORTED_MGF
+ _Reasons.UNSUPPORTED_MGF,
)
if not backend.rsa_padding_supported(padding):
raise UnsupportedAlgorithm(
"This combination of padding and hash algorithm is not "
"supported by this backend.",
- _Reasons.UNSUPPORTED_PADDING
+ _Reasons.UNSUPPORTED_PADDING,
)
else:
raise UnsupportedAlgorithm(
- "{0} is not supported by this backend.".format(
- padding.name
- ),
- _Reasons.UNSUPPORTED_PADDING
+ "{} is not supported by this backend.".format(padding.name),
+ _Reasons.UNSUPPORTED_PADDING,
)
return _enc_dec_rsa_pkey_ctx(backend, key, data, padding_enum, padding)
@@ -76,24 +83,19 @@ def _enc_dec_rsa_pkey_ctx(backend, key, data, padding_enum, padding):
init = backend._lib.EVP_PKEY_decrypt_init
crypt = backend._lib.EVP_PKEY_decrypt
- pkey_ctx = backend._lib.EVP_PKEY_CTX_new(
- key._evp_pkey, backend._ffi.NULL
- )
+ pkey_ctx = backend._lib.EVP_PKEY_CTX_new(key._evp_pkey, backend._ffi.NULL)
backend.openssl_assert(pkey_ctx != backend._ffi.NULL)
pkey_ctx = backend._ffi.gc(pkey_ctx, backend._lib.EVP_PKEY_CTX_free)
res = init(pkey_ctx)
backend.openssl_assert(res == 1)
- res = backend._lib.EVP_PKEY_CTX_set_rsa_padding(
- pkey_ctx, padding_enum)
+ res = backend._lib.EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, padding_enum)
backend.openssl_assert(res > 0)
buf_size = backend._lib.EVP_PKEY_size(key._evp_pkey)
backend.openssl_assert(buf_size > 0)
- if (
- isinstance(padding, OAEP) and
- backend._lib.Cryptography_HAS_RSA_OAEP_MD
- ):
+ if isinstance(padding, OAEP) and backend._lib.Cryptography_HAS_RSA_OAEP_MD:
mgf1_md = backend._evp_md_non_null_from_algorithm(
- padding._mgf._algorithm)
+ padding._mgf._algorithm
+ )
res = backend._lib.EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, mgf1_md)
backend.openssl_assert(res > 0)
oaep_md = backend._evp_md_non_null_from_algorithm(padding._algorithm)
@@ -101,9 +103,9 @@ def _enc_dec_rsa_pkey_ctx(backend, key, data, padding_enum, padding):
backend.openssl_assert(res > 0)
if (
- isinstance(padding, OAEP) and
- padding._label is not None and
- len(padding._label) > 0
+ isinstance(padding, OAEP)
+ and padding._label is not None
+ and len(padding._label) > 0
):
# set0_rsa_oaep_label takes ownership of the char * so we need to
# copy it into some new memory
@@ -117,40 +119,19 @@ def _enc_dec_rsa_pkey_ctx(backend, key, data, padding_enum, padding):
outlen = backend._ffi.new("size_t *", buf_size)
buf = backend._ffi.new("unsigned char[]", buf_size)
+ # Everything from this line onwards is written with the goal of being as
+ # constant-time as is practical given the constraints of Python and our
+ # API. See Bleichenbacher's '98 attack on RSA, and its many many variants.
+ # As such, you should not attempt to change this (particularly to "clean it
+ # up") without understanding why it was written this way (see
+ # Chesterton's Fence), and without measuring to verify you have not
+ # introduced observable time differences.
res = crypt(pkey_ctx, buf, outlen, data, len(data))
+ resbuf = backend._ffi.buffer(buf)[: outlen[0]]
+ backend._lib.ERR_clear_error()
if res <= 0:
- _handle_rsa_enc_dec_error(backend, key)
-
- return backend._ffi.buffer(buf)[:outlen[0]]
-
-
-def _handle_rsa_enc_dec_error(backend, key):
- errors = backend._consume_errors()
- backend.openssl_assert(errors)
- backend.openssl_assert(errors[0].lib == backend._lib.ERR_LIB_RSA)
- if isinstance(key, _RSAPublicKey):
- backend.openssl_assert(
- errors[0].reason == backend._lib.RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE
- )
- raise ValueError(
- "Data too long for key size. Encrypt less data or use a "
- "larger key size."
- )
- else:
- decoding_errors = [
- backend._lib.RSA_R_BLOCK_TYPE_IS_NOT_01,
- backend._lib.RSA_R_BLOCK_TYPE_IS_NOT_02,
- backend._lib.RSA_R_OAEP_DECODING_ERROR,
- # Though this error looks similar to the
- # RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE, this occurs on decrypts,
- # rather than on encrypts
- backend._lib.RSA_R_DATA_TOO_LARGE_FOR_MODULUS,
- ]
- if backend._lib.Cryptography_HAS_RSA_R_PKCS_DECODING_ERROR:
- decoding_errors.append(backend._lib.RSA_R_PKCS_DECODING_ERROR)
-
- backend.openssl_assert(errors[0].reason in decoding_errors)
- raise ValueError("Decryption failed.")
+ raise ValueError("Encryption/decryption failed.")
+ return resbuf
def _rsa_sig_determine_padding(backend, key, padding, algorithm):
@@ -161,49 +142,68 @@ def _rsa_sig_determine_padding(backend, key, padding, algorithm):
backend.openssl_assert(pkey_size > 0)
if isinstance(padding, PKCS1v15):
+ # Hash algorithm is ignored for PKCS1v15-padding, may be None.
padding_enum = backend._lib.RSA_PKCS1_PADDING
elif isinstance(padding, PSS):
if not isinstance(padding._mgf, MGF1):
raise UnsupportedAlgorithm(
"Only MGF1 is supported by this backend.",
- _Reasons.UNSUPPORTED_MGF
+ _Reasons.UNSUPPORTED_MGF,
)
+ # PSS padding requires a hash algorithm
+ if not isinstance(algorithm, hashes.HashAlgorithm):
+ raise TypeError("Expected instance of hashes.HashAlgorithm.")
+
# Size of key in bytes - 2 is the maximum
# PSS signature length (salt length is checked later)
if pkey_size - algorithm.digest_size - 2 < 0:
- raise ValueError("Digest too large for key size. Use a larger "
- "key or different digest.")
+ raise ValueError(
+ "Digest too large for key size. Use a larger "
+ "key or different digest."
+ )
padding_enum = backend._lib.RSA_PKCS1_PSS_PADDING
else:
raise UnsupportedAlgorithm(
- "{0} is not supported by this backend.".format(padding.name),
- _Reasons.UNSUPPORTED_PADDING
+ "{} is not supported by this backend.".format(padding.name),
+ _Reasons.UNSUPPORTED_PADDING,
)
return padding_enum
-def _rsa_sig_setup(backend, padding, algorithm, key, data, init_func):
+# Hash algorithm can be absent (None) to initialize the context without setting
+# any message digest algorithm. This is currently only valid for the PKCS1v15
+# padding type, where it means that the signature data is encoded/decoded
+# as provided, without being wrapped in a DigestInfo structure.
+def _rsa_sig_setup(backend, padding, algorithm, key, init_func):
padding_enum = _rsa_sig_determine_padding(backend, key, padding, algorithm)
- evp_md = backend._evp_md_non_null_from_algorithm(algorithm)
pkey_ctx = backend._lib.EVP_PKEY_CTX_new(key._evp_pkey, backend._ffi.NULL)
backend.openssl_assert(pkey_ctx != backend._ffi.NULL)
pkey_ctx = backend._ffi.gc(pkey_ctx, backend._lib.EVP_PKEY_CTX_free)
res = init_func(pkey_ctx)
backend.openssl_assert(res == 1)
- res = backend._lib.EVP_PKEY_CTX_set_signature_md(pkey_ctx, evp_md)
- if res == 0:
+ if algorithm is not None:
+ evp_md = backend._evp_md_non_null_from_algorithm(algorithm)
+ res = backend._lib.EVP_PKEY_CTX_set_signature_md(pkey_ctx, evp_md)
+ if res == 0:
+ backend._consume_errors()
+ raise UnsupportedAlgorithm(
+ "{} is not supported by this backend for RSA signing.".format(
+ algorithm.name
+ ),
+ _Reasons.UNSUPPORTED_HASH,
+ )
+ res = backend._lib.EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, padding_enum)
+ if res <= 0:
backend._consume_errors()
raise UnsupportedAlgorithm(
- "{0} is not supported by this backend for RSA signing.".format(
- algorithm.name
+ "{} is not supported for the RSA signature operation.".format(
+ padding.name
),
- _Reasons.UNSUPPORTED_HASH
+ _Reasons.UNSUPPORTED_PADDING,
)
- res = backend._lib.EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, padding_enum)
- backend.openssl_assert(res > 0)
if isinstance(padding, PSS):
res = backend._lib.EVP_PKEY_CTX_set_rsa_pss_saltlen(
pkey_ctx, _get_rsa_pss_salt_length(padding, key, algorithm)
@@ -211,7 +211,8 @@ def _rsa_sig_setup(backend, padding, algorithm, key, data, init_func):
backend.openssl_assert(res > 0)
mgf1_md = backend._evp_md_non_null_from_algorithm(
- padding._mgf._algorithm)
+ padding._mgf._algorithm
+ )
res = backend._lib.EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, mgf1_md)
backend.openssl_assert(res > 0)
@@ -220,45 +221,37 @@ def _rsa_sig_setup(backend, padding, algorithm, key, data, init_func):
def _rsa_sig_sign(backend, padding, algorithm, private_key, data):
pkey_ctx = _rsa_sig_setup(
- backend, padding, algorithm, private_key, data,
- backend._lib.EVP_PKEY_sign_init
+ backend,
+ padding,
+ algorithm,
+ private_key,
+ backend._lib.EVP_PKEY_sign_init,
)
buflen = backend._ffi.new("size_t *")
res = backend._lib.EVP_PKEY_sign(
- pkey_ctx,
- backend._ffi.NULL,
- buflen,
- data,
- len(data)
+ pkey_ctx, backend._ffi.NULL, buflen, data, len(data)
)
backend.openssl_assert(res == 1)
buf = backend._ffi.new("unsigned char[]", buflen[0])
- res = backend._lib.EVP_PKEY_sign(
- pkey_ctx, buf, buflen, data, len(data))
+ res = backend._lib.EVP_PKEY_sign(pkey_ctx, buf, buflen, data, len(data))
if res != 1:
- errors = backend._consume_errors()
- backend.openssl_assert(errors[0].lib == backend._lib.ERR_LIB_RSA)
- if (
- errors[0].reason ==
- backend._lib.RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE
- ):
- reason = ("Salt length too long for key size. Try using "
- "MAX_LENGTH instead.")
- else:
- backend.openssl_assert(
- errors[0].reason ==
- backend._lib.RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY
- )
- reason = "Digest too large for key size. Use a larger key."
- raise ValueError(reason)
+ errors = backend._consume_errors_with_text()
+ raise ValueError(
+ "Digest or salt length too long for key size. Use a larger key "
+ "or shorter salt length if you are specifying a PSS salt",
+ errors,
+ )
return backend._ffi.buffer(buf)[:]
def _rsa_sig_verify(backend, padding, algorithm, public_key, signature, data):
pkey_ctx = _rsa_sig_setup(
- backend, padding, algorithm, public_key, data,
- backend._lib.EVP_PKEY_verify_init
+ backend,
+ padding,
+ algorithm,
+ public_key,
+ backend._lib.EVP_PKEY_verify_init,
)
res = backend._lib.EVP_PKEY_verify(
pkey_ctx, signature, len(signature), data, len(data)
@@ -272,6 +265,36 @@ def _rsa_sig_verify(backend, padding, algorithm, public_key, signature, data):
raise InvalidSignature
+def _rsa_sig_recover(backend, padding, algorithm, public_key, signature):
+ pkey_ctx = _rsa_sig_setup(
+ backend,
+ padding,
+ algorithm,
+ public_key,
+ backend._lib.EVP_PKEY_verify_recover_init,
+ )
+
+ # Attempt to keep the rest of the code in this function as constant/time
+ # as possible. See the comment in _enc_dec_rsa_pkey_ctx. Note that the
+ # outlen parameter is used even though its value may be undefined in the
+ # error case. Due to the tolerant nature of Python slicing this does not
+ # trigger any exceptions.
+ maxlen = backend._lib.EVP_PKEY_size(public_key._evp_pkey)
+ backend.openssl_assert(maxlen > 0)
+ buf = backend._ffi.new("unsigned char[]", maxlen)
+ buflen = backend._ffi.new("size_t *", maxlen)
+ res = backend._lib.EVP_PKEY_verify_recover(
+ pkey_ctx, buf, buflen, signature, len(signature)
+ )
+ resbuf = backend._ffi.buffer(buf)[: buflen[0]]
+ backend._lib.ERR_clear_error()
+ # Assume that all parameter errors are handled during the setup phase and
+ # any error here is due to invalid signature.
+ if res != 1:
+ raise InvalidSignature
+ return resbuf
+
+
@utils.register_interface(AsymmetricSignatureContext)
class _RSASignatureContext(object):
def __init__(self, backend, private_key, padding, algorithm):
@@ -295,7 +318,7 @@ class _RSASignatureContext(object):
self._padding,
self._algorithm,
self._private_key,
- self._hash_ctx.finalize()
+ self._hash_ctx.finalize(),
)
@@ -325,21 +348,33 @@ class _RSAVerificationContext(object):
self._algorithm,
self._public_key,
self._signature,
- self._hash_ctx.finalize()
+ self._hash_ctx.finalize(),
)
@utils.register_interface(RSAPrivateKeyWithSerialization)
class _RSAPrivateKey(object):
def __init__(self, backend, rsa_cdata, evp_pkey):
+ res = backend._lib.RSA_check_key(rsa_cdata)
+ if res != 1:
+ errors = backend._consume_errors_with_text()
+ raise ValueError("Invalid private key", errors)
+
+ # Blinding is on by default in many versions of OpenSSL, but let's
+ # just be conservative here.
+ res = backend._lib.RSA_blinding_on(rsa_cdata, backend._ffi.NULL)
+ backend.openssl_assert(res == 1)
+
self._backend = backend
self._rsa_cdata = rsa_cdata
self._evp_pkey = evp_pkey
n = self._backend._ffi.new("BIGNUM **")
self._backend._lib.RSA_get0_key(
- self._rsa_cdata, n, self._backend._ffi.NULL,
- self._backend._ffi.NULL
+ self._rsa_cdata,
+ n,
+ self._backend._ffi.NULL,
+ self._backend._ffi.NULL,
)
self._backend.openssl_assert(n[0] != self._backend._ffi.NULL)
self._key_size = self._backend._lib.BN_num_bits(n[0])
@@ -352,7 +387,7 @@ class _RSAPrivateKey(object):
return _RSASignatureContext(self._backend, self, padding, algorithm)
def decrypt(self, ciphertext, padding):
- key_size_bytes = int(math.ceil(self.key_size / 8.0))
+ key_size_bytes = (self.key_size + 7) // 8
if key_size_bytes != len(ciphertext):
raise ValueError("Ciphertext length must be equal to key size.")
@@ -362,8 +397,6 @@ class _RSAPrivateKey(object):
ctx = self._backend._lib.RSAPublicKey_dup(self._rsa_cdata)
self._backend.openssl_assert(ctx != self._backend._ffi.NULL)
ctx = self._backend._ffi.gc(ctx, self._backend._lib.RSA_free)
- res = self._backend._lib.RSA_blinding_on(ctx, self._backend._ffi.NULL)
- self._backend.openssl_assert(res == 1)
evp_pkey = self._backend._rsa_cdata_to_evp_pkey(ctx)
return _RSAPublicKey(self._backend, ctx, evp_pkey)
@@ -399,7 +432,7 @@ class _RSAPrivateKey(object):
public_numbers=rsa.RSAPublicNumbers(
e=self._backend._bn_to_int(e[0]),
n=self._backend._bn_to_int(n[0]),
- )
+ ),
)
def private_bytes(self, encoding, format, encryption_algorithm):
@@ -407,8 +440,9 @@ class _RSAPrivateKey(object):
encoding,
format,
encryption_algorithm,
+ self,
self._evp_pkey,
- self._rsa_cdata
+ self._rsa_cdata,
)
def sign(self, data, padding, algorithm):
@@ -427,8 +461,10 @@ class _RSAPublicKey(object):
n = self._backend._ffi.new("BIGNUM **")
self._backend._lib.RSA_get0_key(
- self._rsa_cdata, n, self._backend._ffi.NULL,
- self._backend._ffi.NULL
+ self._rsa_cdata,
+ n,
+ self._backend._ffi.NULL,
+ self._backend._ffi.NULL,
)
self._backend.openssl_assert(n[0] != self._backend._ffi.NULL)
self._key_size = self._backend._lib.BN_num_bits(n[0])
@@ -462,11 +498,7 @@ class _RSAPublicKey(object):
def public_bytes(self, encoding, format):
return self._backend._public_key_bytes(
- encoding,
- format,
- self,
- self._evp_pkey,
- self._rsa_cdata
+ encoding, format, self, self._evp_pkey, self._rsa_cdata
)
def verify(self, signature, data, padding, algorithm):
@@ -476,3 +508,9 @@ class _RSAPublicKey(object):
return _rsa_sig_verify(
self._backend, padding, algorithm, self, signature, data
)
+
+ def recover_data_from_signature(self, signature, padding, algorithm):
+ _check_not_prehashed(algorithm)
+ return _rsa_sig_recover(
+ self._backend, padding, algorithm, self, signature
+ )
diff --git a/src/cryptography/hazmat/backends/openssl/utils.py b/src/cryptography/hazmat/backends/openssl/utils.py
index 363f3d2c2..3d697d1fb 100644
--- a/src/cryptography/hazmat/backends/openssl/utils.py
+++ b/src/cryptography/hazmat/backends/openssl/utils.py
@@ -17,9 +17,7 @@ def _evp_pkey_derive(backend, evp_pkey, peer_public_key):
ctx = backend._ffi.gc(ctx, backend._lib.EVP_PKEY_CTX_free)
res = backend._lib.EVP_PKEY_derive_init(ctx)
backend.openssl_assert(res == 1)
- res = backend._lib.EVP_PKEY_derive_set_peer(
- ctx, peer_public_key._evp_pkey
- )
+ res = backend._lib.EVP_PKEY_derive_set_peer(ctx, peer_public_key._evp_pkey)
backend.openssl_assert(res == 1)
keylen = backend._ffi.new("size_t *")
res = backend._lib.EVP_PKEY_derive(ctx, backend._ffi.NULL, keylen)
@@ -28,9 +26,7 @@ def _evp_pkey_derive(backend, evp_pkey, peer_public_key):
buf = backend._ffi.new("unsigned char[]", keylen[0])
res = backend._lib.EVP_PKEY_derive(ctx, buf, keylen)
if res != 1:
- raise ValueError(
- "Null shared key derived from public/private pair."
- )
+ raise ValueError("Null shared key derived from public/private pair.")
return backend._ffi.buffer(buf, keylen[0])[:]
@@ -56,7 +52,8 @@ def _check_not_prehashed(signature_algorithm):
if isinstance(signature_algorithm, Prehashed):
raise TypeError(
"Prehashed is only supported in the sign and verify methods. "
- "It cannot be used with signer or verifier."
+ "It cannot be used with signer, verifier or "
+ "recover_data_from_signature."
)
@@ -64,6 +61,6 @@ def _warn_sign_verify_deprecated():
warnings.warn(
"signer and verifier have been deprecated. Please use sign "
"and verify instead.",
- utils.PersistentlyDeprecated,
- stacklevel=3
+ utils.PersistentlyDeprecated2017,
+ stacklevel=3,
)
diff --git a/src/cryptography/hazmat/backends/openssl/x25519.py b/src/cryptography/hazmat/backends/openssl/x25519.py
index 914f59413..4971c5481 100644
--- a/src/cryptography/hazmat/backends/openssl/x25519.py
+++ b/src/cryptography/hazmat/backends/openssl/x25519.py
@@ -4,13 +4,12 @@
from __future__ import absolute_import, division, print_function
-import warnings
-
from cryptography import utils
from cryptography.hazmat.backends.openssl.utils import _evp_pkey_derive
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric.x25519 import (
- X25519PrivateKey, X25519PublicKey
+ X25519PrivateKey,
+ X25519PublicKey,
)
@@ -23,26 +22,14 @@ class _X25519PublicKey(object):
self._backend = backend
self._evp_pkey = evp_pkey
- def public_bytes(self, encoding=None, format=None):
- if encoding is None or format is None:
- if encoding is not None or format is not None:
- raise ValueError("Both encoding and format are required")
- else:
- warnings.warn(
- "public_bytes now requires encoding and format arguments. "
- "Support for calling without arguments will be removed in "
- "cryptography 2.7",
- utils.DeprecatedIn25,
- )
- encoding = serialization.Encoding.Raw
- format = serialization.PublicFormat.Raw
+ def public_bytes(self, encoding, format):
if (
- encoding is serialization.Encoding.Raw or
- format is serialization.PublicFormat.Raw
+ encoding is serialization.Encoding.Raw
+ or format is serialization.PublicFormat.Raw
):
if (
- encoding is not serialization.Encoding.Raw or
- format is not serialization.PublicFormat.Raw
+ encoding is not serialization.Encoding.Raw
+ or format is not serialization.PublicFormat.Raw
):
raise ValueError(
"When using Raw both encoding and format must be Raw"
@@ -50,15 +37,6 @@ class _X25519PublicKey(object):
return self._raw_public_bytes()
- if (
- encoding in serialization._PEM_DER and
- format is not serialization.PublicFormat.SubjectPublicKeyInfo
- ):
- raise ValueError(
- "format must be SubjectPublicKeyInfo when encoding is PEM or "
- "DER"
- )
-
return self._backend._public_key_bytes(
encoding, format, self, self._evp_pkey, None
)
@@ -99,37 +77,29 @@ class _X25519PrivateKey(object):
if not isinstance(peer_public_key, X25519PublicKey):
raise TypeError("peer_public_key must be X25519PublicKey.")
- return _evp_pkey_derive(
- self._backend, self._evp_pkey, peer_public_key
- )
+ return _evp_pkey_derive(self._backend, self._evp_pkey, peer_public_key)
def private_bytes(self, encoding, format, encryption_algorithm):
if (
- encoding is serialization.Encoding.Raw or
- format is serialization.PublicFormat.Raw
+ encoding is serialization.Encoding.Raw
+ or format is serialization.PublicFormat.Raw
):
if (
- format is not serialization.PrivateFormat.Raw or
- encoding is not serialization.Encoding.Raw or not
- isinstance(encryption_algorithm, serialization.NoEncryption)
+ format is not serialization.PrivateFormat.Raw
+ or encoding is not serialization.Encoding.Raw
+ or not isinstance(
+ encryption_algorithm, serialization.NoEncryption
+ )
):
raise ValueError(
"When using Raw both encoding and format must be Raw "
- "and encryption_algorithm must be NoEncryption"
+ "and encryption_algorithm must be NoEncryption()"
)
return self._raw_private_bytes()
- if (
- encoding in serialization._PEM_DER and
- format is not serialization.PrivateFormat.PKCS8
- ):
- raise ValueError(
- "format must be PKCS8 when encoding is PEM or DER"
- )
-
return self._backend._private_key_bytes(
- encoding, format, encryption_algorithm, self._evp_pkey, None
+ encoding, format, encryption_algorithm, self, self._evp_pkey, None
)
def _raw_private_bytes(self):
@@ -139,9 +109,13 @@ class _X25519PrivateKey(object):
# using the last 32 bytes, which is the key itself.
bio = self._backend._create_mem_bio_gc()
res = self._backend._lib.i2d_PKCS8PrivateKey_bio(
- bio, self._evp_pkey,
- self._backend._ffi.NULL, self._backend._ffi.NULL,
- 0, self._backend._ffi.NULL, self._backend._ffi.NULL
+ bio,
+ self._evp_pkey,
+ self._backend._ffi.NULL,
+ self._backend._ffi.NULL,
+ 0,
+ self._backend._ffi.NULL,
+ self._backend._ffi.NULL,
)
self._backend.openssl_assert(res == 1)
pkcs8 = self._backend._read_mem_bio(bio)
diff --git a/src/cryptography/hazmat/backends/openssl/x448.py b/src/cryptography/hazmat/backends/openssl/x448.py
index 13e4ce15e..7ebcdf84b 100644
--- a/src/cryptography/hazmat/backends/openssl/x448.py
+++ b/src/cryptography/hazmat/backends/openssl/x448.py
@@ -8,7 +8,8 @@ from cryptography import utils
from cryptography.hazmat.backends.openssl.utils import _evp_pkey_derive
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric.x448 import (
- X448PrivateKey, X448PublicKey
+ X448PrivateKey,
+ X448PublicKey,
)
_X448_KEY_SIZE = 56
@@ -22,12 +23,12 @@ class _X448PublicKey(object):
def public_bytes(self, encoding, format):
if (
- encoding is serialization.Encoding.Raw or
- format is serialization.PublicFormat.Raw
+ encoding is serialization.Encoding.Raw
+ or format is serialization.PublicFormat.Raw
):
if (
- encoding is not serialization.Encoding.Raw or
- format is not serialization.PublicFormat.Raw
+ encoding is not serialization.Encoding.Raw
+ or format is not serialization.PublicFormat.Raw
):
raise ValueError(
"When using Raw both encoding and format must be Raw"
@@ -35,15 +36,6 @@ class _X448PublicKey(object):
return self._raw_public_bytes()
- if (
- encoding in serialization._PEM_DER and
- format is not serialization.PublicFormat.SubjectPublicKeyInfo
- ):
- raise ValueError(
- "format must be SubjectPublicKeyInfo when encoding is PEM or "
- "DER"
- )
-
return self._backend._public_key_bytes(
encoding, format, self, self._evp_pkey, None
)
@@ -79,37 +71,29 @@ class _X448PrivateKey(object):
if not isinstance(peer_public_key, X448PublicKey):
raise TypeError("peer_public_key must be X448PublicKey.")
- return _evp_pkey_derive(
- self._backend, self._evp_pkey, peer_public_key
- )
+ return _evp_pkey_derive(self._backend, self._evp_pkey, peer_public_key)
def private_bytes(self, encoding, format, encryption_algorithm):
if (
- encoding is serialization.Encoding.Raw or
- format is serialization.PublicFormat.Raw
+ encoding is serialization.Encoding.Raw
+ or format is serialization.PublicFormat.Raw
):
if (
- format is not serialization.PrivateFormat.Raw or
- encoding is not serialization.Encoding.Raw or not
- isinstance(encryption_algorithm, serialization.NoEncryption)
+ format is not serialization.PrivateFormat.Raw
+ or encoding is not serialization.Encoding.Raw
+ or not isinstance(
+ encryption_algorithm, serialization.NoEncryption
+ )
):
raise ValueError(
"When using Raw both encoding and format must be Raw "
- "and encryption_algorithm must be NoEncryption"
+ "and encryption_algorithm must be NoEncryption()"
)
return self._raw_private_bytes()
- if (
- encoding in serialization._PEM_DER and
- format is not serialization.PrivateFormat.PKCS8
- ):
- raise ValueError(
- "format must be PKCS8 when encoding is PEM or DER"
- )
-
return self._backend._private_key_bytes(
- encoding, format, encryption_algorithm, self._evp_pkey, None
+ encoding, format, encryption_algorithm, self, self._evp_pkey, None
)
def _raw_private_bytes(self):
diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py
index ac1838c6d..4d0dac764 100644
--- a/src/cryptography/hazmat/backends/openssl/x509.py
+++ b/src/cryptography/hazmat/backends/openssl/x509.py
@@ -6,31 +6,43 @@ from __future__ import absolute_import, division, print_function
import datetime
import operator
-import warnings
from cryptography import utils, x509
from cryptography.exceptions import UnsupportedAlgorithm
from cryptography.hazmat.backends.openssl.decode_asn1 import (
- _CERTIFICATE_EXTENSION_PARSER, _CERTIFICATE_EXTENSION_PARSER_NO_SCT,
- _CRL_EXTENSION_PARSER, _CSR_EXTENSION_PARSER,
- _REVOKED_CERTIFICATE_EXTENSION_PARSER, _asn1_integer_to_int,
- _asn1_string_to_bytes, _decode_x509_name, _obj2txt, _parse_asn1_time
+ _asn1_integer_to_int,
+ _asn1_string_to_bytes,
+ _decode_x509_name,
+ _obj2txt,
+ _parse_asn1_time,
)
from cryptography.hazmat.backends.openssl.encode_asn1 import (
- _encode_asn1_int_gc
+ _encode_asn1_int_gc,
+ _txt2obj_gc,
)
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import dsa, ec, rsa
+from cryptography.x509.name import _ASN1Type
@utils.register_interface(x509.Certificate)
class _Certificate(object):
- def __init__(self, backend, x509):
+ def __init__(self, backend, x509_cert):
self._backend = backend
- self._x509 = x509
+ self._x509 = x509_cert
+
+ version = self._backend._lib.X509_get_version(self._x509)
+ if version == 0:
+ self._version = x509.Version.v1
+ elif version == 2:
+ self._version = x509.Version.v3
+ else:
+ raise x509.InvalidVersion(
+ "{} is not a valid X509 version".format(version), version
+ )
def __repr__(self):
- return "<Certificate(subject={0}, ...)>".format(self.subject)
+ return "<Certificate(subject={}, ...)>".format(self.subject)
def __eq__(self, other):
if not isinstance(other, x509.Certificate):
@@ -45,31 +57,15 @@ class _Certificate(object):
def __hash__(self):
return hash(self.public_bytes(serialization.Encoding.DER))
+ def __deepcopy__(self, memo):
+ return self
+
def fingerprint(self, algorithm):
h = hashes.Hash(algorithm, self._backend)
h.update(self.public_bytes(serialization.Encoding.DER))
return h.finalize()
- @property
- def version(self):
- version = self._backend._lib.X509_get_version(self._x509)
- if version == 0:
- return x509.Version.v1
- elif version == 2:
- return x509.Version.v3
- else:
- raise x509.InvalidVersion(
- "{0} is not a valid X509 version".format(version), version
- )
-
- @property
- def serial(self):
- warnings.warn(
- "Certificate serial is deprecated, use serial_number instead.",
- utils.PersistentlyDeprecated,
- stacklevel=2
- )
- return self.serial_number
+ version = utils.read_only_property("_version")
@property
def serial_number(self):
@@ -90,12 +86,12 @@ class _Certificate(object):
@property
def not_valid_before(self):
- asn1_time = self._backend._lib.X509_get_notBefore(self._x509)
+ asn1_time = self._backend._lib.X509_getm_notBefore(self._x509)
return _parse_asn1_time(self._backend, asn1_time)
@property
def not_valid_after(self):
- asn1_time = self._backend._lib.X509_get_notAfter(self._x509)
+ asn1_time = self._backend._lib.X509_getm_notAfter(self._x509)
return _parse_asn1_time(self._backend, asn1_time)
@property
@@ -117,7 +113,7 @@ class _Certificate(object):
return x509._SIG_OIDS_TO_HASH[oid]
except KeyError:
raise UnsupportedAlgorithm(
- "Signature algorithm OID:{0} not recognized".format(oid)
+ "Signature algorithm OID:{} not recognized".format(oid)
)
@property
@@ -132,14 +128,7 @@ class _Certificate(object):
@utils.cached_property
def extensions(self):
- if self._backend._lib.CRYPTOGRAPHY_OPENSSL_110_OR_GREATER:
- return _CERTIFICATE_EXTENSION_PARSER.parse(
- self._backend, self._x509
- )
- else:
- return _CERTIFICATE_EXTENSION_PARSER_NO_SCT.parse(
- self._backend, self._x509
- )
+ return self._backend._certificate_extension_parser.parse(self._x509)
@property
def signature(self):
@@ -201,13 +190,13 @@ class _RevokedCertificate(object):
self._backend,
self._backend._lib.X509_REVOKED_get0_revocationDate(
self._x509_revoked
- )
+ ),
)
@utils.cached_property
def extensions(self):
- return _REVOKED_CERTIFICATE_EXTENSION_PARSER.parse(
- self._backend, self._x509_revoked
+ return self._backend._revoked_cert_extension_parser.parse(
+ self._x509_revoked
)
@@ -230,9 +219,7 @@ class _CertificateRevocationList(object):
def fingerprint(self, algorithm):
h = hashes.Hash(algorithm, self._backend)
bio = self._backend._create_mem_bio_gc()
- res = self._backend._lib.i2d_X509_CRL_bio(
- bio, self._x509_crl
- )
+ res = self._backend._lib.i2d_X509_CRL_bio(bio, self._x509_crl)
self._backend.openssl_assert(res == 1)
der = self._backend._read_mem_bio(bio)
h.update(der)
@@ -257,9 +244,7 @@ class _CertificateRevocationList(object):
if res == 0:
return None
else:
- self._backend.openssl_assert(
- revoked[0] != self._backend._ffi.NULL
- )
+ self._backend.openssl_assert(revoked[0] != self._backend._ffi.NULL)
return _RevokedCertificate(
self._backend, self._sorted_crl, revoked[0]
)
@@ -271,7 +256,7 @@ class _CertificateRevocationList(object):
return x509._SIG_OIDS_TO_HASH[oid]
except KeyError:
raise UnsupportedAlgorithm(
- "Signature algorithm OID:{0} not recognized".format(oid)
+ "Signature algorithm OID:{} not recognized".format(oid)
)
@property
@@ -366,13 +351,17 @@ class _CertificateRevocationList(object):
@utils.cached_property
def extensions(self):
- return _CRL_EXTENSION_PARSER.parse(self._backend, self._x509_crl)
+ return self._backend._crl_extension_parser.parse(self._x509_crl)
def is_signature_valid(self, public_key):
- if not isinstance(public_key, (dsa.DSAPublicKey, rsa.RSAPublicKey,
- ec.EllipticCurvePublicKey)):
- raise TypeError('Expecting one of DSAPublicKey, RSAPublicKey,'
- ' or EllipticCurvePublicKey.')
+ if not isinstance(
+ public_key,
+ (dsa.DSAPublicKey, rsa.RSAPublicKey, ec.EllipticCurvePublicKey),
+ ):
+ raise TypeError(
+ "Expecting one of DSAPublicKey, RSAPublicKey,"
+ " or EllipticCurvePublicKey."
+ )
res = self._backend._lib.X509_CRL_verify(
self._x509_crl, public_key._evp_pkey
)
@@ -423,7 +412,7 @@ class _CertificateSigningRequest(object):
return x509._SIG_OIDS_TO_HASH[oid]
except KeyError:
raise UnsupportedAlgorithm(
- "Signature algorithm OID:{0} not recognized".format(oid)
+ "Signature algorithm OID:{} not recognized".format(oid)
)
@property
@@ -442,12 +431,13 @@ class _CertificateSigningRequest(object):
x509_exts = self._backend._ffi.gc(
x509_exts,
lambda x: self._backend._lib.sk_X509_EXTENSION_pop_free(
- x, self._backend._ffi.addressof(
+ x,
+ self._backend._ffi.addressof(
self._backend._lib._original_lib, "X509_EXTENSION_free"
- )
- )
+ ),
+ ),
)
- return _CSR_EXTENSION_PARSER.parse(self._backend, x509_exts)
+ return self._backend._csr_extension_parser.parse(x509_exts)
def public_bytes(self, encoding):
bio = self._backend._create_mem_bio_gc()
@@ -495,6 +485,47 @@ class _CertificateSigningRequest(object):
return True
+ def get_attribute_for_oid(self, oid):
+ obj = _txt2obj_gc(self._backend, oid.dotted_string)
+ pos = self._backend._lib.X509_REQ_get_attr_by_OBJ(
+ self._x509_req, obj, -1
+ )
+ if pos == -1:
+ raise x509.AttributeNotFound(
+ "No {} attribute was found".format(oid), oid
+ )
+
+ attr = self._backend._lib.X509_REQ_get_attr(self._x509_req, pos)
+ self._backend.openssl_assert(attr != self._backend._ffi.NULL)
+ # We don't support multiple valued attributes for now.
+ self._backend.openssl_assert(
+ self._backend._lib.X509_ATTRIBUTE_count(attr) == 1
+ )
+ asn1_type = self._backend._lib.X509_ATTRIBUTE_get0_type(attr, 0)
+ self._backend.openssl_assert(asn1_type != self._backend._ffi.NULL)
+ # We need this to ensure that our C type cast is safe.
+ # Also this should always be a sane string type, but we'll see if
+ # that is true in the real world...
+ if asn1_type.type not in (
+ _ASN1Type.UTF8String.value,
+ _ASN1Type.PrintableString.value,
+ _ASN1Type.IA5String.value,
+ ):
+ raise ValueError(
+ "OID {} has a disallowed ASN.1 type: {}".format(
+ oid, asn1_type.type
+ )
+ )
+
+ data = self._backend._lib.X509_ATTRIBUTE_get0_data(
+ attr, 0, asn1_type.type, self._backend._ffi.NULL
+ )
+ self._backend.openssl_assert(data != self._backend._ffi.NULL)
+ # This cast is safe iff we assert on the type above to ensure
+ # that it is always a type of ASN1_STRING
+ data = self._backend._ffi.cast("ASN1_STRING *", data)
+ return _asn1_string_to_bytes(self._backend, data)
+
@utils.register_interface(
x509.certificate_transparency.SignedCertificateTimestamp
@@ -523,9 +554,9 @@ class _SignedCertificateTimestamp(object):
def timestamp(self):
timestamp = self._backend._lib.SCT_get_timestamp(self._sct)
milliseconds = timestamp % 1000
- return datetime.datetime.utcfromtimestamp(
- timestamp // 1000
- ).replace(microsecond=milliseconds * 1000)
+ return datetime.datetime.utcfromtimestamp(timestamp // 1000).replace(
+ microsecond=milliseconds * 1000
+ )
@property
def entry_type(self):
diff --git a/src/cryptography/hazmat/bindings/_constant_time.so b/src/cryptography/hazmat/bindings/_constant_time.so
deleted file mode 100644
index 6a3435955..000000000
--- a/src/cryptography/hazmat/bindings/_constant_time.so
+++ /dev/null
Binary files differ
diff --git a/src/cryptography/hazmat/bindings/_openssl.so b/src/cryptography/hazmat/bindings/_openssl.so
index d896a793b..6551c6a15 100644..100755
--- a/src/cryptography/hazmat/bindings/_openssl.so
+++ b/src/cryptography/hazmat/bindings/_openssl.so
Binary files differ
diff --git a/src/cryptography/hazmat/bindings/_padding.so b/src/cryptography/hazmat/bindings/_padding.so
index a27d09e58..5cdc438f0 100644..100755
--- a/src/cryptography/hazmat/bindings/_padding.so
+++ b/src/cryptography/hazmat/bindings/_padding.so
Binary files differ
diff --git a/src/cryptography/hazmat/bindings/openssl/_conditional.py b/src/cryptography/hazmat/bindings/openssl/_conditional.py
index c0238dcc2..ca50fed13 100644
--- a/src/cryptography/hazmat/bindings/openssl/_conditional.py
+++ b/src/cryptography/hazmat/bindings/openssl/_conditional.py
@@ -13,24 +13,6 @@ def cryptography_has_ec2m():
]
-def cryptography_has_ec_1_0_2():
- return [
- "EC_curve_nid2nist",
- ]
-
-
-def cryptography_has_set_ecdh_auto():
- return [
- "SSL_CTX_set_ecdh_auto",
- ]
-
-
-def cryptography_has_rsa_r_pkcs_decoding_error():
- return [
- "RSA_R_PKCS_DECODING_ERROR"
- ]
-
-
def cryptography_has_rsa_oaep_md():
return [
"EVP_PKEY_CTX_set_rsa_oaep_md",
@@ -51,78 +33,22 @@ def cryptography_has_ssl3_method():
]
-def cryptography_has_alpn():
- return [
- "SSL_CTX_set_alpn_protos",
- "SSL_set_alpn_protos",
- "SSL_CTX_set_alpn_select_cb",
- "SSL_get0_alpn_selected",
- ]
-
-
-def cryptography_has_compression():
- return [
- "SSL_get_current_compression",
- "SSL_get_current_expansion",
- "SSL_COMP_get_name",
- ]
-
-
-def cryptography_has_get_server_tmp_key():
- return [
- "SSL_get_server_tmp_key",
- ]
-
-
-def cryptography_has_102_verification_error_codes():
- return [
- 'X509_V_ERR_SUITE_B_INVALID_VERSION',
- 'X509_V_ERR_SUITE_B_INVALID_ALGORITHM',
- 'X509_V_ERR_SUITE_B_INVALID_CURVE',
- 'X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM',
- 'X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED',
- 'X509_V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256',
- 'X509_V_ERR_HOSTNAME_MISMATCH',
- 'X509_V_ERR_EMAIL_MISMATCH',
- 'X509_V_ERR_IP_ADDRESS_MISMATCH'
- ]
-
-
-def cryptography_has_102_verification_params():
+def cryptography_has_102_verification():
return [
+ "X509_V_ERR_SUITE_B_INVALID_VERSION",
+ "X509_V_ERR_SUITE_B_INVALID_ALGORITHM",
+ "X509_V_ERR_SUITE_B_INVALID_CURVE",
+ "X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM",
+ "X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED",
+ "X509_V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256",
"X509_V_FLAG_SUITEB_128_LOS_ONLY",
"X509_V_FLAG_SUITEB_192_LOS",
"X509_V_FLAG_SUITEB_128_LOS",
- "X509_VERIFY_PARAM_set1_host",
- "X509_VERIFY_PARAM_set1_email",
- "X509_VERIFY_PARAM_set1_ip",
- "X509_VERIFY_PARAM_set1_ip_asc",
- "X509_VERIFY_PARAM_set_hostflags",
- "SSL_get0_param",
- "X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT",
- "X509_CHECK_FLAG_NO_WILDCARDS",
- "X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS",
- "X509_CHECK_FLAG_MULTI_LABEL_WILDCARDS",
- "X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS"
]
def cryptography_has_110_verification_params():
- return [
- "X509_CHECK_FLAG_NEVER_CHECK_SUBJECT"
- ]
-
-
-def cryptography_has_x509_v_flag_trusted_first():
- return [
- "X509_V_FLAG_TRUSTED_FIRST",
- ]
-
-
-def cryptography_has_x509_v_flag_partial_chain():
- return [
- "X509_V_FLAG_PARTIAL_CHAIN",
- ]
+ return ["X509_CHECK_FLAG_NEVER_CHECK_SUBJECT"]
def cryptography_has_set_cert_cb():
@@ -148,35 +74,12 @@ def cryptography_has_tls_st():
]
-def cryptography_has_locking_callbacks():
- return [
- "CRYPTO_LOCK",
- "CRYPTO_UNLOCK",
- "CRYPTO_READ",
- "CRYPTO_LOCK_SSL",
- "CRYPTO_lock",
- "Cryptography_setup_ssl_threads",
- ]
-
-
def cryptography_has_scrypt():
return [
"EVP_PBE_scrypt",
]
-def cryptography_has_generic_dtls_method():
- return [
- "DTLS_method",
- "DTLS_server_method",
- "DTLS_client_method",
- "SSL_OP_NO_DTLSv1",
- "SSL_OP_NO_DTLSv1_2",
- "DTLS_set_link_mtu",
- "DTLS_get_link_min_mtu",
- ]
-
-
def cryptography_has_evp_pkey_dhx():
return [
"EVP_PKEY_DHX",
@@ -197,11 +100,12 @@ def cryptography_has_sct():
"SCT_get0_signature",
"SCT_get_timestamp",
"SCT_set_source",
+ "sk_SCT_new_null",
+ "sk_SCT_free",
"sk_SCT_num",
"sk_SCT_value",
- "SCT_LIST_free",
"sk_SCT_push",
- "sk_SCT_new_null",
+ "SCT_LIST_free",
"SCT_new",
"SCT_set1_log_id",
"SCT_set_timestamp",
@@ -217,20 +121,6 @@ def cryptography_has_x509_store_ctx_get_issuer():
]
-def cryptography_has_x25519():
- return [
- "EVP_PKEY_X25519",
- "NID_X25519",
- ]
-
-
-def cryptography_has_x448():
- return [
- "EVP_PKEY_X448",
- "NID_X448",
- ]
-
-
def cryptography_has_ed448():
return [
"EVP_PKEY_ED448",
@@ -245,6 +135,13 @@ def cryptography_has_ed25519():
]
+def cryptography_has_poly1305():
+ return [
+ "NID_poly1305",
+ "EVP_PKEY_POLY1305",
+ ]
+
+
def cryptography_has_oneshot_evp_digest_sign_verify():
return [
"EVP_DigestSign",
@@ -267,7 +164,7 @@ def cryptography_has_evp_pkey_get_set_tls_encodedpoint():
def cryptography_has_fips():
return [
- "FIPS_set_mode",
+ "FIPS_mode_set",
"FIPS_mode",
]
@@ -301,16 +198,6 @@ def cryptography_has_openssl_cleanup():
]
-def cryptography_has_cipher_details():
- return [
- "SSL_CIPHER_is_aead",
- "SSL_CIPHER_get_cipher_nid",
- "SSL_CIPHER_get_digest_nid",
- "SSL_CIPHER_get_kx_nid",
- "SSL_CIPHER_get_auth_nid",
- ]
-
-
def cryptography_has_tlsv13():
return [
"SSL_OP_NO_TLSv1_3",
@@ -326,6 +213,13 @@ def cryptography_has_tlsv13():
]
+def cryptography_has_keylog():
+ return [
+ "SSL_CTX_set_keylog_callback",
+ "SSL_CTX_get_keylog_callback",
+ ]
+
+
def cryptography_has_raw_key():
return [
"EVP_PKEY_new_raw_private_key",
@@ -335,9 +229,45 @@ def cryptography_has_raw_key():
]
-def cryptography_has_evp_r_memory_limit_exceeded():
+def cryptography_has_engine():
+ return [
+ "ENGINE_by_id",
+ "ENGINE_init",
+ "ENGINE_finish",
+ "ENGINE_get_default_RAND",
+ "ENGINE_set_default_RAND",
+ "ENGINE_unregister_RAND",
+ "ENGINE_ctrl_cmd",
+ "ENGINE_free",
+ "ENGINE_get_name",
+ "Cryptography_add_osrandom_engine",
+ "ENGINE_ctrl_cmd_string",
+ "ENGINE_load_builtin_engines",
+ "ENGINE_load_private_key",
+ "ENGINE_load_public_key",
+ ]
+
+
+def cryptography_has_verified_chain():
+ return [
+ "SSL_get0_verified_chain",
+ ]
+
+
+def cryptography_has_srtp():
return [
- "EVP_R_MEMORY_LIMIT_EXCEEDED",
+ "SSL_CTX_set_tlsext_use_srtp",
+ "SSL_set_tlsext_use_srtp",
+ "SSL_get_selected_srtp_profile",
+ ]
+
+
+def cryptography_has_get_proto_version():
+ return [
+ "SSL_CTX_get_min_proto_version",
+ "SSL_CTX_get_max_proto_version",
+ "SSL_get_min_proto_version",
+ "SSL_get_max_proto_version",
]
@@ -348,50 +278,26 @@ def cryptography_has_evp_r_memory_limit_exceeded():
# lists so we can use coverage to measure which are used.
CONDITIONAL_NAMES = {
"Cryptography_HAS_EC2M": cryptography_has_ec2m,
- "Cryptography_HAS_EC_1_0_2": cryptography_has_ec_1_0_2,
- "Cryptography_HAS_SET_ECDH_AUTO": cryptography_has_set_ecdh_auto,
- "Cryptography_HAS_RSA_R_PKCS_DECODING_ERROR": (
- cryptography_has_rsa_r_pkcs_decoding_error
- ),
"Cryptography_HAS_RSA_OAEP_MD": cryptography_has_rsa_oaep_md,
"Cryptography_HAS_RSA_OAEP_LABEL": cryptography_has_rsa_oaep_label,
"Cryptography_HAS_SSL3_METHOD": cryptography_has_ssl3_method,
- "Cryptography_HAS_ALPN": cryptography_has_alpn,
- "Cryptography_HAS_COMPRESSION": cryptography_has_compression,
- "Cryptography_HAS_GET_SERVER_TMP_KEY": cryptography_has_get_server_tmp_key,
- "Cryptography_HAS_102_VERIFICATION_ERROR_CODES": (
- cryptography_has_102_verification_error_codes
- ),
- "Cryptography_HAS_102_VERIFICATION_PARAMS": (
- cryptography_has_102_verification_params
- ),
+ "Cryptography_HAS_102_VERIFICATION": cryptography_has_102_verification,
"Cryptography_HAS_110_VERIFICATION_PARAMS": (
cryptography_has_110_verification_params
),
- "Cryptography_HAS_X509_V_FLAG_TRUSTED_FIRST": (
- cryptography_has_x509_v_flag_trusted_first
- ),
- "Cryptography_HAS_X509_V_FLAG_PARTIAL_CHAIN": (
- cryptography_has_x509_v_flag_partial_chain
- ),
"Cryptography_HAS_SET_CERT_CB": cryptography_has_set_cert_cb,
"Cryptography_HAS_SSL_ST": cryptography_has_ssl_st,
"Cryptography_HAS_TLS_ST": cryptography_has_tls_st,
- "Cryptography_HAS_LOCKING_CALLBACKS": cryptography_has_locking_callbacks,
"Cryptography_HAS_SCRYPT": cryptography_has_scrypt,
- "Cryptography_HAS_GENERIC_DTLS_METHOD": (
- cryptography_has_generic_dtls_method
- ),
"Cryptography_HAS_EVP_PKEY_DHX": cryptography_has_evp_pkey_dhx,
"Cryptography_HAS_MEM_FUNCTIONS": cryptography_has_mem_functions,
"Cryptography_HAS_SCT": cryptography_has_sct,
"Cryptography_HAS_X509_STORE_CTX_GET_ISSUER": (
cryptography_has_x509_store_ctx_get_issuer
),
- "Cryptography_HAS_X25519": cryptography_has_x25519,
- "Cryptography_HAS_X448": cryptography_has_x448,
"Cryptography_HAS_ED448": cryptography_has_ed448,
"Cryptography_HAS_ED25519": cryptography_has_ed25519,
+ "Cryptography_HAS_POLY1305": cryptography_has_poly1305,
"Cryptography_HAS_ONESHOT_EVP_DIGEST_SIGN_VERIFY": (
cryptography_has_oneshot_evp_digest_sign_verify
),
@@ -403,13 +309,14 @@ CONDITIONAL_NAMES = {
"Cryptography_HAS_PSK": cryptography_has_psk,
"Cryptography_HAS_CUSTOM_EXT": cryptography_has_custom_ext,
"Cryptography_HAS_OPENSSL_CLEANUP": cryptography_has_openssl_cleanup,
- "Cryptography_HAS_CIPHER_DETAILS": cryptography_has_cipher_details,
"Cryptography_HAS_TLSv1_3": cryptography_has_tlsv13,
+ "Cryptography_HAS_KEYLOG": cryptography_has_keylog,
"Cryptography_HAS_RAW_KEY": cryptography_has_raw_key,
"Cryptography_HAS_EVP_DIGESTFINAL_XOF": (
cryptography_has_evp_digestfinal_xof
),
- "Cryptography_HAS_EVP_R_MEMORY_LIMIT_EXCEEDED": (
- cryptography_has_evp_r_memory_limit_exceeded
- ),
+ "Cryptography_HAS_ENGINE": cryptography_has_engine,
+ "Cryptography_HAS_VERIFIED_CHAIN": cryptography_has_verified_chain,
+ "Cryptography_HAS_SRTP": cryptography_has_srtp,
+ "Cryptography_HAS_GET_PROTO_VERSION": cryptography_has_get_proto_version,
}
diff --git a/src/cryptography/hazmat/bindings/openssl/binding.py b/src/cryptography/hazmat/bindings/openssl/binding.py
index 0824ea88c..7a84a340e 100644
--- a/src/cryptography/hazmat/bindings/openssl/binding.py
+++ b/src/cryptography/hazmat/bindings/openssl/binding.py
@@ -7,8 +7,8 @@ from __future__ import absolute_import, division, print_function
import collections
import threading
import types
-import warnings
+import cryptography
from cryptography import utils
from cryptography.exceptions import InternalError
from cryptography.hazmat.bindings._openssl import ffi, lib
@@ -51,20 +51,31 @@ def _consume_errors(lib):
return errors
-def _openssl_assert(lib, ok):
- if not ok:
- errors = _consume_errors(lib)
- errors_with_text = []
- for err in errors:
- buf = ffi.new("char[]", 256)
- lib.ERR_error_string_n(err.code, buf, len(buf))
- err_text_reason = ffi.string(buf)
-
- errors_with_text.append(
- _OpenSSLErrorWithText(
- err.code, err.lib, err.func, err.reason, err_text_reason
- )
+def _errors_with_text(errors):
+ errors_with_text = []
+ for err in errors:
+ buf = ffi.new("char[]", 256)
+ lib.ERR_error_string_n(err.code, buf, len(buf))
+ err_text_reason = ffi.string(buf)
+
+ errors_with_text.append(
+ _OpenSSLErrorWithText(
+ err.code, err.lib, err.func, err.reason, err_text_reason
)
+ )
+
+ return errors_with_text
+
+
+def _consume_errors_with_text(lib):
+ return _errors_with_text(_consume_errors(lib))
+
+
+def _openssl_assert(lib, ok, errors=None):
+ if not ok:
+ if errors is None:
+ errors = _consume_errors(lib)
+ errors_with_text = _errors_with_text(errors)
raise InternalError(
"Unknown OpenSSL error. This error is commonly encountered when "
@@ -74,7 +85,7 @@ def _openssl_assert(lib, ok):
"please file an issue at https://github.com/pyca/cryptography/"
"issues with information on how to reproduce "
"this. ({0!r})".format(errors_with_text),
- errors_with_text
+ errors_with_text,
)
@@ -97,11 +108,11 @@ class Binding(object):
"""
OpenSSL API wrapper.
"""
+
lib = None
ffi = ffi
_lib_loaded = False
_init_lock = threading.Lock()
- _lock_init_lock = threading.Lock()
def __init__(self):
self._ensure_ffi_initialized()
@@ -114,10 +125,9 @@ class Binding(object):
# reliably clear the error queue. Once we clear it here we will
# error on any subsequent unexpected item in the stack.
cls.lib.ERR_clear_error()
- cls._osrandom_engine_id = cls.lib.Cryptography_osrandom_engine_id
- cls._osrandom_engine_name = cls.lib.Cryptography_osrandom_engine_name
- result = cls.lib.Cryptography_add_osrandom_engine()
- _openssl_assert(cls.lib, result in (1, 2))
+ if cls.lib.CRYPTOGRAPHY_NEEDS_OSRANDOM_ENGINE:
+ result = cls.lib.Cryptography_add_osrandom_engine()
+ _openssl_assert(cls.lib, result in (1, 2))
@classmethod
def _ensure_ffi_initialized(cls):
@@ -129,46 +139,34 @@ class Binding(object):
cls.lib.SSL_library_init()
# adds all ciphers/digests for EVP
cls.lib.OpenSSL_add_all_algorithms()
- # loads error strings for libcrypto and libssl functions
- cls.lib.SSL_load_error_strings()
cls._register_osrandom_engine()
@classmethod
def init_static_locks(cls):
- with cls._lock_init_lock:
- cls._ensure_ffi_initialized()
- # Use Python's implementation if available, importing _ssl triggers
- # the setup for this.
- __import__("_ssl")
-
- if (not cls.lib.Cryptography_HAS_LOCKING_CALLBACKS or
- cls.lib.CRYPTO_get_locking_callback() != cls.ffi.NULL):
- return
-
- # If nothing else has setup a locking callback already, we set up
- # our own
- res = lib.Cryptography_setup_ssl_threads()
- _openssl_assert(cls.lib, res == 1)
-
-
-def _verify_openssl_version(lib):
- if (
- lib.CRYPTOGRAPHY_OPENSSL_LESS_THAN_102 and
- not lib.CRYPTOGRAPHY_IS_LIBRESSL
- ):
- warnings.warn(
- "OpenSSL version 1.0.1 is no longer supported by the OpenSSL "
- "project, please upgrade. A future version of cryptography will "
- "drop support for it.",
- utils.CryptographyDeprecationWarning
+ cls._ensure_ffi_initialized()
+
+
+def _verify_package_version(version):
+ # Occasionally we run into situations where the version of the Python
+ # package does not match the version of the shared object that is loaded.
+ # This may occur in environments where multiple versions of cryptography
+ # are installed and available in the python path. To avoid errors cropping
+ # up later this code checks that the currently imported package and the
+ # shared object that were loaded have the same version and raise an
+ # ImportError if they do not
+ so_package_version = ffi.string(lib.CRYPTOGRAPHY_PACKAGE_VERSION)
+ if version.encode("ascii") != so_package_version:
+ raise ImportError(
+ "The version of cryptography does not match the loaded "
+ "shared object. This can happen if you have multiple copies of "
+ "cryptography installed in your Python path. Please try creating "
+ "a new virtual environment to resolve this issue. "
+ "Loaded python version: {}, shared object version: {}".format(
+ version, so_package_version
+ )
)
-# OpenSSL is not thread safe until the locks are initialized. We call this
-# method in module scope so that it executes with the import lock. On
-# Pythons < 3.4 this import lock is a global lock, which can prevent a race
-# condition registering the OpenSSL locks. On Python 3.4+ the import lock
-# is per module so this approach will not work.
-Binding.init_static_locks()
+_verify_package_version(cryptography.__version__)
-_verify_openssl_version(Binding.lib)
+Binding.init_static_locks()
diff --git a/src/cryptography/hazmat/primitives/asymmetric/dh.py b/src/cryptography/hazmat/primitives/asymmetric/dh.py
index 4fc995245..74a311d50 100644
--- a/src/cryptography/hazmat/primitives/asymmetric/dh.py
+++ b/src/cryptography/hazmat/primitives/asymmetric/dh.py
@@ -9,9 +9,14 @@ import abc
import six
from cryptography import utils
+from cryptography.hazmat.backends import _get_backend
-def generate_parameters(generator, key_size, backend):
+_MIN_MODULUS_SIZE = 512
+
+
+def generate_parameters(generator, key_size, backend=None):
+ backend = _get_backend(backend)
return backend.generate_dh_parameters(generator, key_size)
@@ -21,8 +26,9 @@ class DHPrivateNumbers(object):
raise TypeError("x must be an integer.")
if not isinstance(public_numbers, DHPublicNumbers):
- raise TypeError("public_numbers must be an instance of "
- "DHPublicNumbers.")
+ raise TypeError(
+ "public_numbers must be an instance of " "DHPublicNumbers."
+ )
self._x = x
self._public_numbers = public_numbers
@@ -32,14 +38,15 @@ class DHPrivateNumbers(object):
return NotImplemented
return (
- self._x == other._x and
- self._public_numbers == other._public_numbers
+ self._x == other._x
+ and self._public_numbers == other._public_numbers
)
def __ne__(self, other):
return not self == other
- def private_key(self, backend):
+ def private_key(self, backend=None):
+ backend = _get_backend(backend)
return backend.load_dh_private_numbers(self)
public_numbers = utils.read_only_property("_public_numbers")
@@ -53,7 +60,8 @@ class DHPublicNumbers(object):
if not isinstance(parameter_numbers, DHParameterNumbers):
raise TypeError(
- "parameters must be an instance of DHParameterNumbers.")
+ "parameters must be an instance of DHParameterNumbers."
+ )
self._y = y
self._parameter_numbers = parameter_numbers
@@ -63,14 +71,15 @@ class DHPublicNumbers(object):
return NotImplemented
return (
- self._y == other._y and
- self._parameter_numbers == other._parameter_numbers
+ self._y == other._y
+ and self._parameter_numbers == other._parameter_numbers
)
def __ne__(self, other):
return not self == other
- def public_key(self, backend):
+ def public_key(self, backend=None):
+ backend = _get_backend(backend)
return backend.load_dh_public_numbers(self)
y = utils.read_only_property("_y")
@@ -79,9 +88,8 @@ class DHPublicNumbers(object):
class DHParameterNumbers(object):
def __init__(self, p, g, q=None):
- if (
- not isinstance(p, six.integer_types) or
- not isinstance(g, six.integer_types)
+ if not isinstance(p, six.integer_types) or not isinstance(
+ g, six.integer_types
):
raise TypeError("p and g must be integers")
if q is not None and not isinstance(q, six.integer_types):
@@ -90,6 +98,11 @@ class DHParameterNumbers(object):
if g < 2:
raise ValueError("DH generator must be 2 or greater")
+ if p.bit_length() < _MIN_MODULUS_SIZE:
+ raise ValueError(
+ "p (modulus) must be at least {}-bit".format(_MIN_MODULUS_SIZE)
+ )
+
self._p = p
self._g = g
self._q = q
@@ -99,15 +112,14 @@ class DHParameterNumbers(object):
return NotImplemented
return (
- self._p == other._p and
- self._g == other._g and
- self._q == other._q
+ self._p == other._p and self._g == other._g and self._q == other._q
)
def __ne__(self, other):
return not self == other
- def parameters(self, backend):
+ def parameters(self, backend=None):
+ backend = _get_backend(backend)
return backend.load_dh_parameter_numbers(self)
p = utils.read_only_property("_p")
diff --git a/src/cryptography/hazmat/primitives/asymmetric/dsa.py b/src/cryptography/hazmat/primitives/asymmetric/dsa.py
index e380a441f..8ccc66665 100644
--- a/src/cryptography/hazmat/primitives/asymmetric/dsa.py
+++ b/src/cryptography/hazmat/primitives/asymmetric/dsa.py
@@ -9,6 +9,7 @@ import abc
import six
from cryptography import utils
+from cryptography.hazmat.backends import _get_backend
@six.add_metaclass(abc.ABCMeta)
@@ -119,17 +120,21 @@ class DSAPublicKey(object):
DSAPublicKeyWithSerialization = DSAPublicKey
-def generate_parameters(key_size, backend):
+def generate_parameters(key_size, backend=None):
+ backend = _get_backend(backend)
return backend.generate_dsa_parameters(key_size)
-def generate_private_key(key_size, backend):
+def generate_private_key(key_size, backend=None):
+ backend = _get_backend(backend)
return backend.generate_dsa_private_key_and_parameters(key_size)
def _check_dsa_parameters(parameters):
- if parameters.p.bit_length() not in [1024, 2048, 3072]:
- raise ValueError("p must be exactly 1024, 2048, or 3072 bits long")
+ if parameters.p.bit_length() not in [1024, 2048, 3072, 4096]:
+ raise ValueError(
+ "p must be exactly 1024, 2048, 3072, or 4096 bits long"
+ )
if parameters.q.bit_length() not in [160, 224, 256]:
raise ValueError("q must be exactly 160, 224, or 256 bits long")
@@ -150,9 +155,9 @@ def _check_dsa_private_numbers(numbers):
class DSAParameterNumbers(object):
def __init__(self, p, q, g):
if (
- not isinstance(p, six.integer_types) or
- not isinstance(q, six.integer_types) or
- not isinstance(g, six.integer_types)
+ not isinstance(p, six.integer_types)
+ or not isinstance(q, six.integer_types)
+ or not isinstance(g, six.integer_types)
):
raise TypeError(
"DSAParameterNumbers p, q, and g arguments must be integers."
@@ -166,7 +171,8 @@ class DSAParameterNumbers(object):
q = utils.read_only_property("_q")
g = utils.read_only_property("_g")
- def parameters(self, backend):
+ def parameters(self, backend=None):
+ backend = _get_backend(backend)
return backend.load_dsa_parameter_numbers(self)
def __eq__(self, other):
@@ -180,9 +186,8 @@ class DSAParameterNumbers(object):
def __repr__(self):
return (
- "<DSAParameterNumbers(p={self.p}, q={self.q}, g={self.g})>".format(
- self=self
- )
+ "<DSAParameterNumbers(p={self.p}, q={self.q}, "
+ "g={self.g})>".format(self=self)
)
@@ -202,7 +207,8 @@ class DSAPublicNumbers(object):
y = utils.read_only_property("_y")
parameter_numbers = utils.read_only_property("_parameter_numbers")
- def public_key(self, backend):
+ def public_key(self, backend=None):
+ backend = _get_backend(backend)
return backend.load_dsa_public_numbers(self)
def __eq__(self, other):
@@ -210,8 +216,8 @@ class DSAPublicNumbers(object):
return NotImplemented
return (
- self.y == other.y and
- self.parameter_numbers == other.parameter_numbers
+ self.y == other.y
+ and self.parameter_numbers == other.parameter_numbers
)
def __ne__(self, other):
@@ -239,7 +245,8 @@ class DSAPrivateNumbers(object):
x = utils.read_only_property("_x")
public_numbers = utils.read_only_property("_public_numbers")
- def private_key(self, backend):
+ def private_key(self, backend=None):
+ backend = _get_backend(backend)
return backend.load_dsa_private_numbers(self)
def __eq__(self, other):
diff --git a/src/cryptography/hazmat/primitives/asymmetric/ec.py b/src/cryptography/hazmat/primitives/asymmetric/ec.py
index 1de0976a6..c7e694fc5 100644
--- a/src/cryptography/hazmat/primitives/asymmetric/ec.py
+++ b/src/cryptography/hazmat/primitives/asymmetric/ec.py
@@ -11,6 +11,7 @@ import six
from cryptography import utils
from cryptography.hazmat._oid import ObjectIdentifier
+from cryptography.hazmat.backends import _get_backend
class EllipticCurveOID(object):
@@ -166,6 +167,7 @@ class EllipticCurvePublicKey(object):
raise ValueError("Unsupported elliptic curve point type")
from cryptography.hazmat.backends.openssl.backend import backend
+
return backend.load_elliptic_curve_public_bytes(curve, data)
@@ -289,26 +291,22 @@ class BrainpoolP512R1(object):
_CURVE_TYPES = {
"prime192v1": SECP192R1,
"prime256v1": SECP256R1,
-
"secp192r1": SECP192R1,
"secp224r1": SECP224R1,
"secp256r1": SECP256R1,
"secp384r1": SECP384R1,
"secp521r1": SECP521R1,
"secp256k1": SECP256K1,
-
"sect163k1": SECT163K1,
"sect233k1": SECT233K1,
"sect283k1": SECT283K1,
"sect409k1": SECT409K1,
"sect571k1": SECT571K1,
-
"sect163r2": SECT163R2,
"sect233r1": SECT233R1,
"sect283r1": SECT283R1,
"sect409r1": SECT409R1,
"sect571r1": SECT571R1,
-
"brainpoolP256r1": BrainpoolP256R1,
"brainpoolP384r1": BrainpoolP384R1,
"brainpoolP512r1": BrainpoolP512R1,
@@ -323,11 +321,13 @@ class ECDSA(object):
algorithm = utils.read_only_property("_algorithm")
-def generate_private_key(curve, backend):
+def generate_private_key(curve, backend=None):
+ backend = _get_backend(backend)
return backend.generate_elliptic_curve_private_key(curve)
-def derive_private_key(private_value, curve, backend):
+def derive_private_key(private_value, curve, backend=None):
+ backend = _get_backend(backend)
if not isinstance(private_value, six.integer_types):
raise TypeError("private_value must be an integer type.")
@@ -342,9 +342,8 @@ def derive_private_key(private_value, curve, backend):
class EllipticCurvePublicNumbers(object):
def __init__(self, x, y, curve):
- if (
- not isinstance(x, six.integer_types) or
- not isinstance(y, six.integer_types)
+ if not isinstance(x, six.integer_types) or not isinstance(
+ y, six.integer_types
):
raise TypeError("x and y must be integers.")
@@ -355,7 +354,8 @@ class EllipticCurvePublicNumbers(object):
self._x = x
self._curve = curve
- def public_key(self, backend):
+ def public_key(self, backend=None):
+ backend = _get_backend(backend)
return backend.load_elliptic_curve_public_numbers(self)
def encode_point(self):
@@ -364,14 +364,15 @@ class EllipticCurvePublicNumbers(object):
" and will be removed in a future version. Please use "
"EllipticCurvePublicKey.public_bytes to obtain both "
"compressed and uncompressed point encoding.",
- utils.DeprecatedIn25,
+ utils.PersistentlyDeprecated2019,
stacklevel=2,
)
# key_size is in bits. Convert to bytes and round up
byte_length = (self.curve.key_size + 7) // 8
return (
- b'\x04' + utils.int_to_bytes(self.x, byte_length) +
- utils.int_to_bytes(self.y, byte_length)
+ b"\x04"
+ + utils.int_to_bytes(self.x, byte_length)
+ + utils.int_to_bytes(self.y, byte_length)
)
@classmethod
@@ -383,21 +384,21 @@ class EllipticCurvePublicNumbers(object):
"Support for unsafe construction of public numbers from "
"encoded data will be removed in a future version. "
"Please use EllipticCurvePublicKey.from_encoded_point",
- utils.DeprecatedIn25,
+ utils.PersistentlyDeprecated2019,
stacklevel=2,
)
- if data.startswith(b'\x04'):
+ if data.startswith(b"\x04"):
# key_size is in bits. Convert to bytes and round up
byte_length = (curve.key_size + 7) // 8
if len(data) == 2 * byte_length + 1:
- x = utils.int_from_bytes(data[1:byte_length + 1], 'big')
- y = utils.int_from_bytes(data[byte_length + 1:], 'big')
+ x = utils.int_from_bytes(data[1 : byte_length + 1], "big")
+ y = utils.int_from_bytes(data[byte_length + 1 :], "big")
return cls(x, y, curve)
else:
- raise ValueError('Invalid elliptic curve point data length')
+ raise ValueError("Invalid elliptic curve point data length")
else:
- raise ValueError('Unsupported elliptic curve point type')
+ raise ValueError("Unsupported elliptic curve point type")
curve = utils.read_only_property("_curve")
x = utils.read_only_property("_x")
@@ -408,10 +409,10 @@ class EllipticCurvePublicNumbers(object):
return NotImplemented
return (
- self.x == other.x and
- self.y == other.y and
- self.curve.name == other.curve.name and
- self.curve.key_size == other.curve.key_size
+ self.x == other.x
+ and self.y == other.y
+ and self.curve.name == other.curve.name
+ and self.curve.key_size == other.curve.key_size
)
def __ne__(self, other):
@@ -441,7 +442,8 @@ class EllipticCurvePrivateNumbers(object):
self._private_value = private_value
self._public_numbers = public_numbers
- def private_key(self, backend):
+ def private_key(self, backend=None):
+ backend = _get_backend(backend)
return backend.load_elliptic_curve_private_numbers(self)
private_value = utils.read_only_property("_private_value")
@@ -452,8 +454,8 @@ class EllipticCurvePrivateNumbers(object):
return NotImplemented
return (
- self.private_value == other.private_value and
- self.public_numbers == other.public_numbers
+ self.private_value == other.private_value
+ and self.public_numbers == other.public_numbers
)
def __ne__(self, other):
@@ -465,3 +467,36 @@ class EllipticCurvePrivateNumbers(object):
class ECDH(object):
pass
+
+
+_OID_TO_CURVE = {
+ EllipticCurveOID.SECP192R1: SECP192R1,
+ EllipticCurveOID.SECP224R1: SECP224R1,
+ EllipticCurveOID.SECP256K1: SECP256K1,
+ EllipticCurveOID.SECP256R1: SECP256R1,
+ EllipticCurveOID.SECP384R1: SECP384R1,
+ EllipticCurveOID.SECP521R1: SECP521R1,
+ EllipticCurveOID.BRAINPOOLP256R1: BrainpoolP256R1,
+ EllipticCurveOID.BRAINPOOLP384R1: BrainpoolP384R1,
+ EllipticCurveOID.BRAINPOOLP512R1: BrainpoolP512R1,
+ EllipticCurveOID.SECT163K1: SECT163K1,
+ EllipticCurveOID.SECT163R2: SECT163R2,
+ EllipticCurveOID.SECT233K1: SECT233K1,
+ EllipticCurveOID.SECT233R1: SECT233R1,
+ EllipticCurveOID.SECT283K1: SECT283K1,
+ EllipticCurveOID.SECT283R1: SECT283R1,
+ EllipticCurveOID.SECT409K1: SECT409K1,
+ EllipticCurveOID.SECT409R1: SECT409R1,
+ EllipticCurveOID.SECT571K1: SECT571K1,
+ EllipticCurveOID.SECT571R1: SECT571R1,
+}
+
+
+def get_curve_for_oid(oid):
+ try:
+ return _OID_TO_CURVE[oid]
+ except KeyError:
+ raise LookupError(
+ "The provided object identifier has no matching elliptic "
+ "curve class"
+ )
diff --git a/src/cryptography/hazmat/primitives/asymmetric/ed25519.py b/src/cryptography/hazmat/primitives/asymmetric/ed25519.py
new file mode 100644
index 000000000..2d07a029b
--- /dev/null
+++ b/src/cryptography/hazmat/primitives/asymmetric/ed25519.py
@@ -0,0 +1,87 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import absolute_import, division, print_function
+
+import abc
+
+import six
+
+from cryptography.exceptions import UnsupportedAlgorithm, _Reasons
+
+
+_ED25519_KEY_SIZE = 32
+_ED25519_SIG_SIZE = 64
+
+
+@six.add_metaclass(abc.ABCMeta)
+class Ed25519PublicKey(object):
+ @classmethod
+ def from_public_bytes(cls, data):
+ from cryptography.hazmat.backends.openssl.backend import backend
+
+ if not backend.ed25519_supported():
+ raise UnsupportedAlgorithm(
+ "ed25519 is not supported by this version of OpenSSL.",
+ _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM,
+ )
+
+ return backend.ed25519_load_public_bytes(data)
+
+ @abc.abstractmethod
+ def public_bytes(self, encoding, format):
+ """
+ The serialized bytes of the public key.
+ """
+
+ @abc.abstractmethod
+ def verify(self, signature, data):
+ """
+ Verify the signature.
+ """
+
+
+@six.add_metaclass(abc.ABCMeta)
+class Ed25519PrivateKey(object):
+ @classmethod
+ def generate(cls):
+ from cryptography.hazmat.backends.openssl.backend import backend
+
+ if not backend.ed25519_supported():
+ raise UnsupportedAlgorithm(
+ "ed25519 is not supported by this version of OpenSSL.",
+ _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM,
+ )
+
+ return backend.ed25519_generate_key()
+
+ @classmethod
+ def from_private_bytes(cls, data):
+ from cryptography.hazmat.backends.openssl.backend import backend
+
+ if not backend.ed25519_supported():
+ raise UnsupportedAlgorithm(
+ "ed25519 is not supported by this version of OpenSSL.",
+ _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM,
+ )
+
+ return backend.ed25519_load_private_bytes(data)
+
+ @abc.abstractmethod
+ def public_key(self):
+ """
+ The Ed25519PublicKey derived from the private key.
+ """
+
+ @abc.abstractmethod
+ def private_bytes(self, encoding, format, encryption_algorithm):
+ """
+ The serialized bytes of the private key.
+ """
+
+ @abc.abstractmethod
+ def sign(self, data):
+ """
+ Signs the data.
+ """
diff --git a/src/cryptography/hazmat/primitives/asymmetric/ed448.py b/src/cryptography/hazmat/primitives/asymmetric/ed448.py
new file mode 100644
index 000000000..520ffcbcb
--- /dev/null
+++ b/src/cryptography/hazmat/primitives/asymmetric/ed448.py
@@ -0,0 +1,82 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import absolute_import, division, print_function
+
+import abc
+
+import six
+
+from cryptography.exceptions import UnsupportedAlgorithm, _Reasons
+
+
+@six.add_metaclass(abc.ABCMeta)
+class Ed448PublicKey(object):
+ @classmethod
+ def from_public_bytes(cls, data):
+ from cryptography.hazmat.backends.openssl.backend import backend
+
+ if not backend.ed448_supported():
+ raise UnsupportedAlgorithm(
+ "ed448 is not supported by this version of OpenSSL.",
+ _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM,
+ )
+
+ return backend.ed448_load_public_bytes(data)
+
+ @abc.abstractmethod
+ def public_bytes(self, encoding, format):
+ """
+ The serialized bytes of the public key.
+ """
+
+ @abc.abstractmethod
+ def verify(self, signature, data):
+ """
+ Verify the signature.
+ """
+
+
+@six.add_metaclass(abc.ABCMeta)
+class Ed448PrivateKey(object):
+ @classmethod
+ def generate(cls):
+ from cryptography.hazmat.backends.openssl.backend import backend
+
+ if not backend.ed448_supported():
+ raise UnsupportedAlgorithm(
+ "ed448 is not supported by this version of OpenSSL.",
+ _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM,
+ )
+ return backend.ed448_generate_key()
+
+ @classmethod
+ def from_private_bytes(cls, data):
+ from cryptography.hazmat.backends.openssl.backend import backend
+
+ if not backend.ed448_supported():
+ raise UnsupportedAlgorithm(
+ "ed448 is not supported by this version of OpenSSL.",
+ _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM,
+ )
+
+ return backend.ed448_load_private_bytes(data)
+
+ @abc.abstractmethod
+ def public_key(self):
+ """
+ The Ed448PublicKey derived from the private key.
+ """
+
+ @abc.abstractmethod
+ def sign(self, data):
+ """
+ Signs the data.
+ """
+
+ @abc.abstractmethod
+ def private_bytes(self, encoding, format, encryption_algorithm):
+ """
+ The serialized bytes of the private key.
+ """
diff --git a/src/cryptography/hazmat/primitives/asymmetric/padding.py b/src/cryptography/hazmat/primitives/asymmetric/padding.py
index a37c3f90c..fc8f6e26a 100644
--- a/src/cryptography/hazmat/primitives/asymmetric/padding.py
+++ b/src/cryptography/hazmat/primitives/asymmetric/padding.py
@@ -5,7 +5,6 @@
from __future__ import absolute_import, division, print_function
import abc
-import math
import six
@@ -36,8 +35,10 @@ class PSS(object):
def __init__(self, mgf, salt_length):
self._mgf = mgf
- if (not isinstance(salt_length, six.integer_types) and
- salt_length is not self.MAX_LENGTH):
+ if (
+ not isinstance(salt_length, six.integer_types)
+ and salt_length is not self.MAX_LENGTH
+ ):
raise TypeError("salt_length must be an integer.")
if salt_length is not self.MAX_LENGTH and salt_length < 0:
@@ -73,7 +74,7 @@ def calculate_max_pss_salt_length(key, hash_algorithm):
if not isinstance(key, (rsa.RSAPrivateKey, rsa.RSAPublicKey)):
raise TypeError("key must be an RSA public or private key")
# bit length - 1 per RFC 3447
- emlen = int(math.ceil((key.key_size - 1) / 8.0))
+ emlen = (key.key_size + 6) // 8
salt_length = emlen - hash_algorithm.digest_size - 2
assert salt_length >= 0
return salt_length
diff --git a/src/cryptography/hazmat/primitives/asymmetric/rsa.py b/src/cryptography/hazmat/primitives/asymmetric/rsa.py
index 27db671af..ea16bbf66 100644
--- a/src/cryptography/hazmat/primitives/asymmetric/rsa.py
+++ b/src/cryptography/hazmat/primitives/asymmetric/rsa.py
@@ -5,6 +5,7 @@
from __future__ import absolute_import, division, print_function
import abc
+
try:
# Only available in math in 3.5+
from math import gcd
@@ -15,6 +16,7 @@ import six
from cryptography import utils
from cryptography.exceptions import UnsupportedAlgorithm, _Reasons
+from cryptography.hazmat.backends import _get_backend
from cryptography.hazmat.backends.interfaces import RSABackend
@@ -104,15 +106,22 @@ class RSAPublicKey(object):
Verifies the signature of the data.
"""
+ @abc.abstractmethod
+ def recover_data_from_signature(self, signature, padding, algorithm):
+ """
+ Recovers the original data from the signature.
+ """
+
RSAPublicKeyWithSerialization = RSAPublicKey
-def generate_private_key(public_exponent, key_size, backend):
+def generate_private_key(public_exponent, key_size, backend=None):
+ backend = _get_backend(backend)
if not isinstance(backend, RSABackend):
raise UnsupportedAlgorithm(
"Backend object does not implement RSABackend.",
- _Reasons.BACKEND_MISSING_INTERFACE
+ _Reasons.BACKEND_MISSING_INTERFACE,
)
_verify_rsa_parameters(public_exponent, key_size)
@@ -120,18 +129,19 @@ def generate_private_key(public_exponent, key_size, backend):
def _verify_rsa_parameters(public_exponent, key_size):
- if public_exponent < 3:
- raise ValueError("public_exponent must be >= 3.")
-
- if public_exponent & 1 == 0:
- raise ValueError("public_exponent must be odd.")
+ if public_exponent not in (3, 65537):
+ raise ValueError(
+ "public_exponent must be either 3 (for legacy compatibility) or "
+ "65537. Almost everyone should choose 65537 here!"
+ )
if key_size < 512:
raise ValueError("key_size must be at least 512-bits.")
-def _check_private_key_components(p, q, private_exponent, dmp1, dmq1, iqmp,
- public_exponent, modulus):
+def _check_private_key_components(
+ p, q, private_exponent, dmp1, dmq1, iqmp, public_exponent, modulus
+):
if modulus < 3:
raise ValueError("modulus must be >= 3.")
@@ -184,12 +194,12 @@ def _modinv(e, m):
"""
Modular Multiplicative Inverse. Returns x such that: (x*e) mod m == 1
"""
- x1, y1, x2, y2 = 1, 0, 0, 1
+ x1, x2 = 1, 0
a, b = e, m
while b > 0:
q, r = divmod(a, b)
- xn, yn = x1 - q * x2, y1 - q * y2
- a, b, x1, y1, x2, y2 = b, r, x2, y2, xn, yn
+ xn = x1 - q * x2
+ a, b, x1, x2 = b, r, x2, xn
return x1 % m
@@ -266,15 +276,14 @@ def rsa_recover_prime_factors(n, e, d):
class RSAPrivateNumbers(object):
- def __init__(self, p, q, d, dmp1, dmq1, iqmp,
- public_numbers):
+ def __init__(self, p, q, d, dmp1, dmq1, iqmp, public_numbers):
if (
- not isinstance(p, six.integer_types) or
- not isinstance(q, six.integer_types) or
- not isinstance(d, six.integer_types) or
- not isinstance(dmp1, six.integer_types) or
- not isinstance(dmq1, six.integer_types) or
- not isinstance(iqmp, six.integer_types)
+ not isinstance(p, six.integer_types)
+ or not isinstance(q, six.integer_types)
+ or not isinstance(d, six.integer_types)
+ or not isinstance(dmp1, six.integer_types)
+ or not isinstance(dmq1, six.integer_types)
+ or not isinstance(iqmp, six.integer_types)
):
raise TypeError(
"RSAPrivateNumbers p, q, d, dmp1, dmq1, iqmp arguments must"
@@ -303,7 +312,8 @@ class RSAPrivateNumbers(object):
iqmp = utils.read_only_property("_iqmp")
public_numbers = utils.read_only_property("_public_numbers")
- def private_key(self, backend):
+ def private_key(self, backend=None):
+ backend = _get_backend(backend)
return backend.load_rsa_private_numbers(self)
def __eq__(self, other):
@@ -311,35 +321,36 @@ class RSAPrivateNumbers(object):
return NotImplemented
return (
- self.p == other.p and
- self.q == other.q and
- self.d == other.d and
- self.dmp1 == other.dmp1 and
- self.dmq1 == other.dmq1 and
- self.iqmp == other.iqmp and
- self.public_numbers == other.public_numbers
+ self.p == other.p
+ and self.q == other.q
+ and self.d == other.d
+ and self.dmp1 == other.dmp1
+ and self.dmq1 == other.dmq1
+ and self.iqmp == other.iqmp
+ and self.public_numbers == other.public_numbers
)
def __ne__(self, other):
return not self == other
def __hash__(self):
- return hash((
- self.p,
- self.q,
- self.d,
- self.dmp1,
- self.dmq1,
- self.iqmp,
- self.public_numbers,
- ))
+ return hash(
+ (
+ self.p,
+ self.q,
+ self.d,
+ self.dmp1,
+ self.dmq1,
+ self.iqmp,
+ self.public_numbers,
+ )
+ )
class RSAPublicNumbers(object):
def __init__(self, e, n):
- if (
- not isinstance(e, six.integer_types) or
- not isinstance(n, six.integer_types)
+ if not isinstance(e, six.integer_types) or not isinstance(
+ n, six.integer_types
):
raise TypeError("RSAPublicNumbers arguments must be integers.")
@@ -349,7 +360,8 @@ class RSAPublicNumbers(object):
e = utils.read_only_property("_e")
n = utils.read_only_property("_n")
- def public_key(self, backend):
+ def public_key(self, backend=None):
+ backend = _get_backend(backend)
return backend.load_rsa_public_numbers(self)
def __repr__(self):
diff --git a/src/cryptography/hazmat/primitives/asymmetric/utils.py b/src/cryptography/hazmat/primitives/asymmetric/utils.py
index ef1e7eb92..5f9b67786 100644
--- a/src/cryptography/hazmat/primitives/asymmetric/utils.py
+++ b/src/cryptography/hazmat/primitives/asymmetric/utils.py
@@ -4,49 +4,30 @@
from __future__ import absolute_import, division, print_function
-import warnings
-
-from asn1crypto.algos import DSASignature
-
-import six
-
from cryptography import utils
+from cryptography.hazmat._der import (
+ DERReader,
+ INTEGER,
+ SEQUENCE,
+ encode_der,
+ encode_der_integer,
+)
from cryptography.hazmat.primitives import hashes
-def decode_rfc6979_signature(signature):
- warnings.warn(
- "decode_rfc6979_signature is deprecated and will "
- "be removed in a future version, use decode_dss_signature instead.",
- utils.PersistentlyDeprecated,
- stacklevel=2
- )
- return decode_dss_signature(signature)
-
-
def decode_dss_signature(signature):
- data = DSASignature.load(signature, strict=True).native
- return data['r'], data['s']
-
-
-def encode_rfc6979_signature(r, s):
- warnings.warn(
- "encode_rfc6979_signature is deprecated and will "
- "be removed in a future version, use encode_dss_signature instead.",
- utils.PersistentlyDeprecated,
- stacklevel=2
- )
- return encode_dss_signature(r, s)
+ with DERReader(signature).read_single_element(SEQUENCE) as seq:
+ r = seq.read_element(INTEGER).as_integer()
+ s = seq.read_element(INTEGER).as_integer()
+ return r, s
def encode_dss_signature(r, s):
- if (
- not isinstance(r, six.integer_types) or
- not isinstance(s, six.integer_types)
- ):
- raise ValueError("Both r and s must be integers")
-
- return DSASignature({'r': r, 's': s}).dump()
+ return encode_der(
+ SEQUENCE,
+ encode_der(INTEGER, encode_der_integer(r)),
+ encode_der(INTEGER, encode_der_integer(s)),
+ )
class Prehashed(object):
diff --git a/src/cryptography/hazmat/primitives/asymmetric/x25519.py b/src/cryptography/hazmat/primitives/asymmetric/x25519.py
index 4e8badf43..fc6369153 100644
--- a/src/cryptography/hazmat/primitives/asymmetric/x25519.py
+++ b/src/cryptography/hazmat/primitives/asymmetric/x25519.py
@@ -16,16 +16,17 @@ class X25519PublicKey(object):
@classmethod
def from_public_bytes(cls, data):
from cryptography.hazmat.backends.openssl.backend import backend
+
if not backend.x25519_supported():
raise UnsupportedAlgorithm(
"X25519 is not supported by this version of OpenSSL.",
- _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM
+ _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM,
)
return backend.x25519_load_public_bytes(data)
@abc.abstractmethod
- def public_bytes(self, encoding=None, format=None):
+ def public_bytes(self, encoding, format):
"""
The serialized bytes of the public key.
"""
@@ -36,20 +37,22 @@ class X25519PrivateKey(object):
@classmethod
def generate(cls):
from cryptography.hazmat.backends.openssl.backend import backend
+
if not backend.x25519_supported():
raise UnsupportedAlgorithm(
"X25519 is not supported by this version of OpenSSL.",
- _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM
+ _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM,
)
return backend.x25519_generate_key()
@classmethod
def from_private_bytes(cls, data):
from cryptography.hazmat.backends.openssl.backend import backend
+
if not backend.x25519_supported():
raise UnsupportedAlgorithm(
"X25519 is not supported by this version of OpenSSL.",
- _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM
+ _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM,
)
return backend.x25519_load_private_bytes(data)
diff --git a/src/cryptography/hazmat/primitives/asymmetric/x448.py b/src/cryptography/hazmat/primitives/asymmetric/x448.py
index 475e678ff..3ac067bfd 100644
--- a/src/cryptography/hazmat/primitives/asymmetric/x448.py
+++ b/src/cryptography/hazmat/primitives/asymmetric/x448.py
@@ -16,10 +16,11 @@ class X448PublicKey(object):
@classmethod
def from_public_bytes(cls, data):
from cryptography.hazmat.backends.openssl.backend import backend
+
if not backend.x448_supported():
raise UnsupportedAlgorithm(
"X448 is not supported by this version of OpenSSL.",
- _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM
+ _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM,
)
return backend.x448_load_public_bytes(data)
@@ -36,20 +37,22 @@ class X448PrivateKey(object):
@classmethod
def generate(cls):
from cryptography.hazmat.backends.openssl.backend import backend
+
if not backend.x448_supported():
raise UnsupportedAlgorithm(
"X448 is not supported by this version of OpenSSL.",
- _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM
+ _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM,
)
return backend.x448_generate_key()
@classmethod
def from_private_bytes(cls, data):
from cryptography.hazmat.backends.openssl.backend import backend
+
if not backend.x448_supported():
raise UnsupportedAlgorithm(
"X448 is not supported by this version of OpenSSL.",
- _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM
+ _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM,
)
return backend.x448_load_private_bytes(data)
diff --git a/src/cryptography/hazmat/primitives/ciphers/__init__.py b/src/cryptography/hazmat/primitives/ciphers/__init__.py
index 171b1c693..4380f72b2 100644
--- a/src/cryptography/hazmat/primitives/ciphers/__init__.py
+++ b/src/cryptography/hazmat/primitives/ciphers/__init__.py
@@ -5,8 +5,13 @@
from __future__ import absolute_import, division, print_function
from cryptography.hazmat.primitives.ciphers.base import (
- AEADCipherContext, AEADDecryptionContext, AEADEncryptionContext,
- BlockCipherAlgorithm, Cipher, CipherAlgorithm, CipherContext
+ AEADCipherContext,
+ AEADDecryptionContext,
+ AEADEncryptionContext,
+ BlockCipherAlgorithm,
+ Cipher,
+ CipherAlgorithm,
+ CipherContext,
)
diff --git a/src/cryptography/hazmat/primitives/ciphers/aead.py b/src/cryptography/hazmat/primitives/ciphers/aead.py
index 42e19adb1..c8c93955c 100644
--- a/src/cryptography/hazmat/primitives/ciphers/aead.py
+++ b/src/cryptography/hazmat/primitives/ciphers/aead.py
@@ -18,7 +18,7 @@ class ChaCha20Poly1305(object):
if not backend.aead_cipher_supported(self):
raise exceptions.UnsupportedAlgorithm(
"ChaCha20Poly1305 is not supported by this version of OpenSSL",
- exceptions._Reasons.UNSUPPORTED_CIPHER
+ exceptions._Reasons.UNSUPPORTED_CIPHER,
)
utils._check_byteslike("key", key)
@@ -42,18 +42,14 @@ class ChaCha20Poly1305(object):
)
self._check_params(nonce, data, associated_data)
- return aead._encrypt(
- backend, self, nonce, data, associated_data, 16
- )
+ return aead._encrypt(backend, self, nonce, data, associated_data, 16)
def decrypt(self, nonce, data, associated_data):
if associated_data is None:
associated_data = b""
self._check_params(nonce, data, associated_data)
- return aead._decrypt(
- backend, self, nonce, data, associated_data, 16
- )
+ return aead._decrypt(backend, self, nonce, data, associated_data, 16)
def _check_params(self, nonce, data, associated_data):
utils._check_byteslike("nonce", nonce)
@@ -80,12 +76,6 @@ class AESCCM(object):
self._tag_length = tag_length
- if not backend.aead_cipher_supported(self):
- raise exceptions.UnsupportedAlgorithm(
- "AESCCM is not supported by this version of OpenSSL",
- exceptions._Reasons.UNSUPPORTED_CIPHER
- )
-
@classmethod
def generate_key(cls, bit_length):
if not isinstance(bit_length, int):
@@ -126,7 +116,7 @@ class AESCCM(object):
# https://tools.ietf.org/html/rfc3610#section-2.1
l_val = 15 - len(nonce)
if 2 ** (8 * l_val) < data_len:
- raise ValueError("Nonce too long for data")
+ raise ValueError("Data too long for nonce")
def _check_params(self, nonce, data, associated_data):
utils._check_byteslike("nonce", nonce)
@@ -167,22 +157,18 @@ class AESGCM(object):
)
self._check_params(nonce, data, associated_data)
- return aead._encrypt(
- backend, self, nonce, data, associated_data, 16
- )
+ return aead._encrypt(backend, self, nonce, data, associated_data, 16)
def decrypt(self, nonce, data, associated_data):
if associated_data is None:
associated_data = b""
self._check_params(nonce, data, associated_data)
- return aead._decrypt(
- backend, self, nonce, data, associated_data, 16
- )
+ return aead._decrypt(backend, self, nonce, data, associated_data, 16)
def _check_params(self, nonce, data, associated_data):
utils._check_byteslike("nonce", nonce)
utils._check_bytes("data", data)
utils._check_bytes("associated_data", associated_data)
- if len(nonce) == 0:
- raise ValueError("Nonce must be at least 1 byte")
+ if len(nonce) < 8 or len(nonce) > 128:
+ raise ValueError("Nonce must be between 8 and 128 bytes")
diff --git a/src/cryptography/hazmat/primitives/ciphers/algorithms.py b/src/cryptography/hazmat/primitives/ciphers/algorithms.py
index 1f49fd9de..8072cedd1 100644
--- a/src/cryptography/hazmat/primitives/ciphers/algorithms.py
+++ b/src/cryptography/hazmat/primitives/ciphers/algorithms.py
@@ -6,7 +6,8 @@ from __future__ import absolute_import, division, print_function
from cryptography import utils
from cryptography.hazmat.primitives.ciphers import (
- BlockCipherAlgorithm, CipherAlgorithm
+ BlockCipherAlgorithm,
+ CipherAlgorithm,
)
from cryptography.hazmat.primitives.ciphers.modes import ModeWithNonce
@@ -17,9 +18,11 @@ def _verify_key_size(algorithm, key):
# Verify that the key size matches the expected key size
if len(key) * 8 not in algorithm.key_sizes:
- raise ValueError("Invalid key size ({0}) for {1}.".format(
- len(key) * 8, algorithm.name
- ))
+ raise ValueError(
+ "Invalid key size ({}) for {}.".format(
+ len(key) * 8, algorithm.name
+ )
+ )
return key
diff --git a/src/cryptography/hazmat/primitives/ciphers/base.py b/src/cryptography/hazmat/primitives/ciphers/base.py
index f85704142..dae425a29 100644
--- a/src/cryptography/hazmat/primitives/ciphers/base.py
+++ b/src/cryptography/hazmat/primitives/ciphers/base.py
@@ -10,9 +10,13 @@ import six
from cryptography import utils
from cryptography.exceptions import (
- AlreadyFinalized, AlreadyUpdated, NotYetFinalized, UnsupportedAlgorithm,
- _Reasons
+ AlreadyFinalized,
+ AlreadyUpdated,
+ NotYetFinalized,
+ UnsupportedAlgorithm,
+ _Reasons,
)
+from cryptography.hazmat.backends import _get_backend
from cryptography.hazmat.backends.interfaces import CipherBackend
from cryptography.hazmat.primitives.ciphers import modes
@@ -94,11 +98,12 @@ class AEADEncryptionContext(object):
class Cipher(object):
- def __init__(self, algorithm, mode, backend):
+ def __init__(self, algorithm, mode, backend=None):
+ backend = _get_backend(backend)
if not isinstance(backend, CipherBackend):
raise UnsupportedAlgorithm(
"Backend object does not implement CipherBackend.",
- _Reasons.BACKEND_MISSING_INTERFACE
+ _Reasons.BACKEND_MISSING_INTERFACE,
)
if not isinstance(algorithm, CipherAlgorithm):
@@ -179,7 +184,7 @@ class _AEADCipherContext(object):
self._bytes_processed += data_size
if self._bytes_processed > self._ctx._mode._MAX_ENCRYPTED_BYTES:
raise ValueError(
- "{0} has a maximum encrypted byte limit of {1}".format(
+ "{} has a maximum encrypted byte limit of {}".format(
self._ctx._mode.name, self._ctx._mode._MAX_ENCRYPTED_BYTES
)
)
@@ -217,7 +222,7 @@ class _AEADCipherContext(object):
self._aad_bytes_processed += len(data)
if self._aad_bytes_processed > self._ctx._mode._MAX_AAD_BYTES:
raise ValueError(
- "{0} has a maximum AAD byte limit of {1}".format(
+ "{} has a maximum AAD byte limit of {}".format(
self._ctx._mode.name, self._ctx._mode._MAX_AAD_BYTES
)
)
@@ -230,6 +235,7 @@ class _AEADEncryptionContext(_AEADCipherContext):
@property
def tag(self):
if self._ctx is not None:
- raise NotYetFinalized("You must finalize encryption before "
- "getting the tag.")
+ raise NotYetFinalized(
+ "You must finalize encryption before " "getting the tag."
+ )
return self._tag
diff --git a/src/cryptography/hazmat/primitives/ciphers/modes.py b/src/cryptography/hazmat/primitives/ciphers/modes.py
index ad91a6e14..0ba0f2b5a 100644
--- a/src/cryptography/hazmat/primitives/ciphers/modes.py
+++ b/src/cryptography/hazmat/primitives/ciphers/modes.py
@@ -72,9 +72,11 @@ def _check_aes_key_length(self, algorithm):
def _check_iv_length(self, algorithm):
if len(self.initialization_vector) * 8 != algorithm.block_size:
- raise ValueError("Invalid IV size ({0}) for {1}.".format(
- len(self.initialization_vector), self.name
- ))
+ raise ValueError(
+ "Invalid IV size ({}) for {}.".format(
+ len(self.initialization_vector), self.name
+ )
+ )
def _check_iv_and_key_length(self, algorithm):
@@ -178,9 +180,11 @@ class CTR(object):
def validate_for_algorithm(self, algorithm):
_check_aes_key_length(self, algorithm)
if len(self.nonce) * 8 != algorithm.block_size:
- raise ValueError("Invalid nonce size ({0}) for {1}.".format(
- len(self.nonce), self.name
- ))
+ raise ValueError(
+ "Invalid nonce size ({}) for {}.".format(
+ len(self.nonce), self.name
+ )
+ )
@utils.register_interface(Mode)
@@ -192,12 +196,14 @@ class GCM(object):
_MAX_AAD_BYTES = (2 ** 64) // 8
def __init__(self, initialization_vector, tag=None, min_tag_length=16):
- # len(initialization_vector) must in [1, 2 ** 64), but it's impossible
- # to actually construct a bytes object that large, so we don't check
- # for it
+ # OpenSSL 3.0.0 constrains GCM IVs to [64, 1024] bits inclusive
+ # This is a sane limit anyway so we'll enforce it here.
utils._check_byteslike("initialization_vector", initialization_vector)
- if len(initialization_vector) == 0:
- raise ValueError("initialization_vector must be at least 1 byte")
+ if len(initialization_vector) < 8 or len(initialization_vector) > 128:
+ raise ValueError(
+ "initialization_vector must be between 8 and 128 bytes (64 "
+ "and 1024 bits)."
+ )
self._initialization_vector = initialization_vector
if tag is not None:
utils._check_bytes("tag", tag)
@@ -205,8 +211,9 @@ class GCM(object):
raise ValueError("min_tag_length must be >= 4")
if len(tag) < min_tag_length:
raise ValueError(
- "Authentication tag must be {0} bytes or longer.".format(
- min_tag_length)
+ "Authentication tag must be {} bytes or longer.".format(
+ min_tag_length
+ )
)
self._tag = tag
self._min_tag_length = min_tag_length
diff --git a/src/cryptography/hazmat/primitives/cmac.py b/src/cryptography/hazmat/primitives/cmac.py
index 1404eac3d..bf962c906 100644
--- a/src/cryptography/hazmat/primitives/cmac.py
+++ b/src/cryptography/hazmat/primitives/cmac.py
@@ -6,25 +6,26 @@ from __future__ import absolute_import, division, print_function
from cryptography import utils
from cryptography.exceptions import (
- AlreadyFinalized, UnsupportedAlgorithm, _Reasons
+ AlreadyFinalized,
+ UnsupportedAlgorithm,
+ _Reasons,
)
+from cryptography.hazmat.backends import _get_backend
from cryptography.hazmat.backends.interfaces import CMACBackend
-from cryptography.hazmat.primitives import ciphers, mac
+from cryptography.hazmat.primitives import ciphers
-@utils.register_interface(mac.MACContext)
class CMAC(object):
- def __init__(self, algorithm, backend, ctx=None):
+ def __init__(self, algorithm, backend=None, ctx=None):
+ backend = _get_backend(backend)
if not isinstance(backend, CMACBackend):
raise UnsupportedAlgorithm(
"Backend object does not implement CMACBackend.",
- _Reasons.BACKEND_MISSING_INTERFACE
+ _Reasons.BACKEND_MISSING_INTERFACE,
)
if not isinstance(algorithm, ciphers.BlockCipherAlgorithm):
- raise TypeError(
- "Expected instance of BlockCipherAlgorithm."
- )
+ raise TypeError("Expected instance of BlockCipherAlgorithm.")
self._algorithm = algorithm
self._backend = backend
@@ -59,7 +60,5 @@ class CMAC(object):
if self._ctx is None:
raise AlreadyFinalized("Context was already finalized.")
return CMAC(
- self._algorithm,
- backend=self._backend,
- ctx=self._ctx.copy()
+ self._algorithm, backend=self._backend, ctx=self._ctx.copy()
)
diff --git a/src/cryptography/hazmat/primitives/constant_time.py b/src/cryptography/hazmat/primitives/constant_time.py
index 0e987ea75..7f41b9efa 100644
--- a/src/cryptography/hazmat/primitives/constant_time.py
+++ b/src/cryptography/hazmat/primitives/constant_time.py
@@ -5,31 +5,10 @@
from __future__ import absolute_import, division, print_function
import hmac
-import warnings
-from cryptography import utils
-from cryptography.hazmat.bindings._constant_time import lib
+def bytes_eq(a, b):
+ if not isinstance(a, bytes) or not isinstance(b, bytes):
+ raise TypeError("a and b must be bytes.")
-if hasattr(hmac, "compare_digest"):
- def bytes_eq(a, b):
- if not isinstance(a, bytes) or not isinstance(b, bytes):
- raise TypeError("a and b must be bytes.")
-
- return hmac.compare_digest(a, b)
-
-else:
- warnings.warn(
- "Support for your Python version is deprecated. The next version of "
- "cryptography will remove support. Please upgrade to a 2.7.x "
- "release that supports hmac.compare_digest as soon as possible.",
- utils.DeprecatedIn23,
- )
-
- def bytes_eq(a, b):
- if not isinstance(a, bytes) or not isinstance(b, bytes):
- raise TypeError("a and b must be bytes.")
-
- return lib.Cryptography_constant_time_bytes_eq(
- a, len(a), b, len(b)
- ) == 1
+ return hmac.compare_digest(a, b)
diff --git a/src/cryptography/hazmat/primitives/hashes.py b/src/cryptography/hazmat/primitives/hashes.py
index 9be2b6009..18e2bab36 100644
--- a/src/cryptography/hazmat/primitives/hashes.py
+++ b/src/cryptography/hazmat/primitives/hashes.py
@@ -10,8 +10,11 @@ import six
from cryptography import utils
from cryptography.exceptions import (
- AlreadyFinalized, UnsupportedAlgorithm, _Reasons
+ AlreadyFinalized,
+ UnsupportedAlgorithm,
+ _Reasons,
)
+from cryptography.hazmat.backends import _get_backend
from cryptography.hazmat.backends.interfaces import HashBackend
@@ -66,11 +69,12 @@ class ExtendableOutputFunction(object):
@utils.register_interface(HashContext)
class Hash(object):
- def __init__(self, algorithm, backend, ctx=None):
+ def __init__(self, algorithm, backend=None, ctx=None):
+ backend = _get_backend(backend)
if not isinstance(backend, HashBackend):
raise UnsupportedAlgorithm(
"Backend object does not implement HashBackend.",
- _Reasons.BACKEND_MISSING_INTERFACE
+ _Reasons.BACKEND_MISSING_INTERFACE,
)
if not isinstance(algorithm, HashAlgorithm):
diff --git a/src/cryptography/hazmat/primitives/hmac.py b/src/cryptography/hazmat/primitives/hmac.py
index f7f401d2b..8c421dc68 100644
--- a/src/cryptography/hazmat/primitives/hmac.py
+++ b/src/cryptography/hazmat/primitives/hmac.py
@@ -6,20 +6,23 @@ from __future__ import absolute_import, division, print_function
from cryptography import utils
from cryptography.exceptions import (
- AlreadyFinalized, UnsupportedAlgorithm, _Reasons
+ AlreadyFinalized,
+ UnsupportedAlgorithm,
+ _Reasons,
)
+from cryptography.hazmat.backends import _get_backend
from cryptography.hazmat.backends.interfaces import HMACBackend
-from cryptography.hazmat.primitives import hashes, mac
+from cryptography.hazmat.primitives import hashes
-@utils.register_interface(mac.MACContext)
@utils.register_interface(hashes.HashContext)
class HMAC(object):
- def __init__(self, key, algorithm, backend, ctx=None):
+ def __init__(self, key, algorithm, backend=None, ctx=None):
+ backend = _get_backend(backend)
if not isinstance(backend, HMACBackend):
raise UnsupportedAlgorithm(
"Backend object does not implement HMACBackend.",
- _Reasons.BACKEND_MISSING_INTERFACE
+ _Reasons.BACKEND_MISSING_INTERFACE,
)
if not isinstance(algorithm, hashes.HashAlgorithm):
@@ -48,7 +51,7 @@ class HMAC(object):
self._key,
self.algorithm,
backend=self._backend,
- ctx=self._ctx.copy()
+ ctx=self._ctx.copy(),
)
def finalize(self):
diff --git a/src/cryptography/hazmat/primitives/kdf/concatkdf.py b/src/cryptography/hazmat/primitives/kdf/concatkdf.py
index 65b25cdc2..7cc0324fc 100644
--- a/src/cryptography/hazmat/primitives/kdf/concatkdf.py
+++ b/src/cryptography/hazmat/primitives/kdf/concatkdf.py
@@ -8,8 +8,12 @@ import struct
from cryptography import utils
from cryptography.exceptions import (
- AlreadyFinalized, InvalidKey, UnsupportedAlgorithm, _Reasons
+ AlreadyFinalized,
+ InvalidKey,
+ UnsupportedAlgorithm,
+ _Reasons,
)
+from cryptography.hazmat.backends import _get_backend
from cryptography.hazmat.backends.interfaces import HMACBackend
from cryptography.hazmat.backends.interfaces import HashBackend
from cryptography.hazmat.primitives import constant_time, hashes, hmac
@@ -17,16 +21,15 @@ from cryptography.hazmat.primitives.kdf import KeyDerivationFunction
def _int_to_u32be(n):
- return struct.pack('>I', n)
+ return struct.pack(">I", n)
def _common_args_checks(algorithm, length, otherinfo):
max_length = algorithm.digest_size * (2 ** 32 - 1)
if length > max_length:
raise ValueError(
- "Can not derive keys larger than {0} bits.".format(
- max_length
- ))
+ "Can not derive keys larger than {} bits.".format(max_length)
+ )
if otherinfo is not None:
utils._check_bytes("otherinfo", otherinfo)
@@ -37,7 +40,7 @@ def _concatkdf_derive(key_material, length, auxfn, otherinfo):
outlen = 0
counter = 1
- while (length > outlen):
+ while length > outlen:
h = auxfn()
h.update(_int_to_u32be(counter))
h.update(key_material)
@@ -51,7 +54,8 @@ def _concatkdf_derive(key_material, length, auxfn, otherinfo):
@utils.register_interface(KeyDerivationFunction)
class ConcatKDFHash(object):
- def __init__(self, algorithm, length, otherinfo, backend):
+ def __init__(self, algorithm, length, otherinfo, backend=None):
+ backend = _get_backend(backend)
_common_args_checks(algorithm, length, otherinfo)
self._algorithm = algorithm
@@ -63,7 +67,7 @@ class ConcatKDFHash(object):
if not isinstance(backend, HashBackend):
raise UnsupportedAlgorithm(
"Backend object does not implement HashBackend.",
- _Reasons.BACKEND_MISSING_INTERFACE
+ _Reasons.BACKEND_MISSING_INTERFACE,
)
self._backend = backend
self._used = False
@@ -75,8 +79,9 @@ class ConcatKDFHash(object):
if self._used:
raise AlreadyFinalized
self._used = True
- return _concatkdf_derive(key_material, self._length,
- self._hash, self._otherinfo)
+ return _concatkdf_derive(
+ key_material, self._length, self._hash, self._otherinfo
+ )
def verify(self, key_material, expected_key):
if not constant_time.bytes_eq(self.derive(key_material), expected_key):
@@ -85,7 +90,8 @@ class ConcatKDFHash(object):
@utils.register_interface(KeyDerivationFunction)
class ConcatKDFHMAC(object):
- def __init__(self, algorithm, length, salt, otherinfo, backend):
+ def __init__(self, algorithm, length, salt, otherinfo, backend=None):
+ backend = _get_backend(backend)
_common_args_checks(algorithm, length, otherinfo)
self._algorithm = algorithm
@@ -104,7 +110,7 @@ class ConcatKDFHMAC(object):
if not isinstance(backend, HMACBackend):
raise UnsupportedAlgorithm(
"Backend object does not implement HMACBackend.",
- _Reasons.BACKEND_MISSING_INTERFACE
+ _Reasons.BACKEND_MISSING_INTERFACE,
)
self._backend = backend
self._used = False
@@ -116,8 +122,9 @@ class ConcatKDFHMAC(object):
if self._used:
raise AlreadyFinalized
self._used = True
- return _concatkdf_derive(key_material, self._length,
- self._hmac, self._otherinfo)
+ return _concatkdf_derive(
+ key_material, self._length, self._hmac, self._otherinfo
+ )
def verify(self, key_material, expected_key):
if not constant_time.bytes_eq(self.derive(key_material), expected_key):
diff --git a/src/cryptography/hazmat/primitives/kdf/hkdf.py b/src/cryptography/hazmat/primitives/kdf/hkdf.py
index 307f91cca..9bb6bc213 100644
--- a/src/cryptography/hazmat/primitives/kdf/hkdf.py
+++ b/src/cryptography/hazmat/primitives/kdf/hkdf.py
@@ -8,8 +8,12 @@ import six
from cryptography import utils
from cryptography.exceptions import (
- AlreadyFinalized, InvalidKey, UnsupportedAlgorithm, _Reasons
+ AlreadyFinalized,
+ InvalidKey,
+ UnsupportedAlgorithm,
+ _Reasons,
)
+from cryptography.hazmat.backends import _get_backend
from cryptography.hazmat.backends.interfaces import HMACBackend
from cryptography.hazmat.primitives import constant_time, hmac
from cryptography.hazmat.primitives.kdf import KeyDerivationFunction
@@ -17,11 +21,12 @@ from cryptography.hazmat.primitives.kdf import KeyDerivationFunction
@utils.register_interface(KeyDerivationFunction)
class HKDF(object):
- def __init__(self, algorithm, length, salt, info, backend):
+ def __init__(self, algorithm, length, salt, info, backend=None):
+ backend = _get_backend(backend)
if not isinstance(backend, HMACBackend):
raise UnsupportedAlgorithm(
"Backend object does not implement HMACBackend.",
- _Reasons.BACKEND_MISSING_INTERFACE
+ _Reasons.BACKEND_MISSING_INTERFACE,
)
self._algorithm = algorithm
@@ -53,11 +58,12 @@ class HKDF(object):
@utils.register_interface(KeyDerivationFunction)
class HKDFExpand(object):
- def __init__(self, algorithm, length, info, backend):
+ def __init__(self, algorithm, length, info, backend=None):
+ backend = _get_backend(backend)
if not isinstance(backend, HMACBackend):
raise UnsupportedAlgorithm(
"Backend object does not implement HMACBackend.",
- _Reasons.BACKEND_MISSING_INTERFACE
+ _Reasons.BACKEND_MISSING_INTERFACE,
)
self._algorithm = algorithm
@@ -68,9 +74,8 @@ class HKDFExpand(object):
if length > max_length:
raise ValueError(
- "Can not derive keys larger than {0} octets.".format(
- max_length
- ))
+ "Can not derive keys larger than {} octets.".format(max_length)
+ )
self._length = length
@@ -95,7 +100,7 @@ class HKDFExpand(object):
output.append(h.finalize())
counter += 1
- return b"".join(output)[:self._length]
+ return b"".join(output)[: self._length]
def derive(self, key_material):
utils._check_byteslike("key_material", key_material)
diff --git a/src/cryptography/hazmat/primitives/kdf/kbkdf.py b/src/cryptography/hazmat/primitives/kdf/kbkdf.py
index 56783a85c..864337001 100644
--- a/src/cryptography/hazmat/primitives/kdf/kbkdf.py
+++ b/src/cryptography/hazmat/primitives/kdf/kbkdf.py
@@ -10,8 +10,12 @@ from six.moves import range
from cryptography import utils
from cryptography.exceptions import (
- AlreadyFinalized, InvalidKey, UnsupportedAlgorithm, _Reasons
+ AlreadyFinalized,
+ InvalidKey,
+ UnsupportedAlgorithm,
+ _Reasons,
)
+from cryptography.hazmat.backends import _get_backend
from cryptography.hazmat.backends.interfaces import HMACBackend
from cryptography.hazmat.primitives import constant_time, hashes, hmac
from cryptography.hazmat.primitives.kdf import KeyDerivationFunction
@@ -28,24 +32,36 @@ class CounterLocation(Enum):
@utils.register_interface(KeyDerivationFunction)
class KBKDFHMAC(object):
- def __init__(self, algorithm, mode, length, rlen, llen,
- location, label, context, fixed, backend):
+ def __init__(
+ self,
+ algorithm,
+ mode,
+ length,
+ rlen,
+ llen,
+ location,
+ label,
+ context,
+ fixed,
+ backend=None,
+ ):
+ backend = _get_backend(backend)
if not isinstance(backend, HMACBackend):
raise UnsupportedAlgorithm(
"Backend object does not implement HMACBackend.",
- _Reasons.BACKEND_MISSING_INTERFACE
+ _Reasons.BACKEND_MISSING_INTERFACE,
)
if not isinstance(algorithm, hashes.HashAlgorithm):
raise UnsupportedAlgorithm(
"Algorithm supplied is not a supported hash algorithm.",
- _Reasons.UNSUPPORTED_HASH
+ _Reasons.UNSUPPORTED_HASH,
)
if not backend.hmac_supported(algorithm):
raise UnsupportedAlgorithm(
"Algorithm supplied is not a supported hmac algorithm.",
- _Reasons.UNSUPPORTED_HASH
+ _Reasons.UNSUPPORTED_HASH,
)
if not isinstance(mode, Mode):
@@ -55,8 +71,9 @@ class KBKDFHMAC(object):
raise TypeError("location must be of type CounterLocation")
if (label or context) and fixed:
- raise ValueError("When supplying fixed data, "
- "label and context are ignored.")
+ raise ValueError(
+ "When supplying fixed data, " "label and context are ignored."
+ )
if rlen is None or not self._valid_byte_length(rlen):
raise ValueError("rlen must be between 1 and 4")
@@ -68,10 +85,10 @@ class KBKDFHMAC(object):
raise TypeError("llen must be an integer")
if label is None:
- label = b''
+ label = b""
if context is None:
- context = b''
+ context = b""
utils._check_bytes("label", label)
utils._check_bytes("context", context)
@@ -89,7 +106,7 @@ class KBKDFHMAC(object):
def _valid_byte_length(self, value):
if not isinstance(value, int):
- raise TypeError('value must be of type int')
+ raise TypeError("value must be of type int")
value_bin = utils.int_to_bytes(1, value)
if not 1 <= len(value_bin) <= 4:
@@ -106,7 +123,7 @@ class KBKDFHMAC(object):
# inverse floor division (equivalent to ceiling)
rounds = -(-self._length // self._algorithm.digest_size)
- output = [b'']
+ output = [b""]
# For counter mode, the number of iterations shall not be
# larger than 2^r-1, where r <= 32 is the binary length of the counter
@@ -114,7 +131,7 @@ class KBKDFHMAC(object):
# PRF will not repeat during a particular call to the KDF function.
r_bin = utils.int_to_bytes(1, self._rlen)
if rounds > pow(2, len(r_bin) * 8) - 1:
- raise ValueError('There are too many iterations.')
+ raise ValueError("There are too many iterations.")
for i in range(1, rounds + 1):
h = hmac.HMAC(key_material, self._algorithm, backend=self._backend)
@@ -130,7 +147,7 @@ class KBKDFHMAC(object):
output.append(h.finalize())
- return b''.join(output)[:self._length]
+ return b"".join(output)[: self._length]
def _generate_fixed_input(self):
if self._fixed_data and isinstance(self._fixed_data, bytes):
diff --git a/src/cryptography/hazmat/primitives/kdf/pbkdf2.py b/src/cryptography/hazmat/primitives/kdf/pbkdf2.py
index a47b7bbcb..5b67d48bb 100644
--- a/src/cryptography/hazmat/primitives/kdf/pbkdf2.py
+++ b/src/cryptography/hazmat/primitives/kdf/pbkdf2.py
@@ -6,8 +6,12 @@ from __future__ import absolute_import, division, print_function
from cryptography import utils
from cryptography.exceptions import (
- AlreadyFinalized, InvalidKey, UnsupportedAlgorithm, _Reasons
+ AlreadyFinalized,
+ InvalidKey,
+ UnsupportedAlgorithm,
+ _Reasons,
)
+from cryptography.hazmat.backends import _get_backend
from cryptography.hazmat.backends.interfaces import PBKDF2HMACBackend
from cryptography.hazmat.primitives import constant_time
from cryptography.hazmat.primitives.kdf import KeyDerivationFunction
@@ -15,18 +19,20 @@ from cryptography.hazmat.primitives.kdf import KeyDerivationFunction
@utils.register_interface(KeyDerivationFunction)
class PBKDF2HMAC(object):
- def __init__(self, algorithm, length, salt, iterations, backend):
+ def __init__(self, algorithm, length, salt, iterations, backend=None):
+ backend = _get_backend(backend)
if not isinstance(backend, PBKDF2HMACBackend):
raise UnsupportedAlgorithm(
"Backend object does not implement PBKDF2HMACBackend.",
- _Reasons.BACKEND_MISSING_INTERFACE
+ _Reasons.BACKEND_MISSING_INTERFACE,
)
if not backend.pbkdf2_hmac_supported(algorithm):
raise UnsupportedAlgorithm(
- "{0} is not supported for PBKDF2 by this backend.".format(
- algorithm.name),
- _Reasons.UNSUPPORTED_HASH
+ "{} is not supported for PBKDF2 by this backend.".format(
+ algorithm.name
+ ),
+ _Reasons.UNSUPPORTED_HASH,
)
self._used = False
self._algorithm = algorithm
@@ -47,7 +53,7 @@ class PBKDF2HMAC(object):
self._length,
self._salt,
self._iterations,
- key_material
+ key_material,
)
def verify(self, key_material, expected_key):
diff --git a/src/cryptography/hazmat/primitives/kdf/scrypt.py b/src/cryptography/hazmat/primitives/kdf/scrypt.py
index df9745e68..f028646aa 100644
--- a/src/cryptography/hazmat/primitives/kdf/scrypt.py
+++ b/src/cryptography/hazmat/primitives/kdf/scrypt.py
@@ -8,8 +8,12 @@ import sys
from cryptography import utils
from cryptography.exceptions import (
- AlreadyFinalized, InvalidKey, UnsupportedAlgorithm, _Reasons
+ AlreadyFinalized,
+ InvalidKey,
+ UnsupportedAlgorithm,
+ _Reasons,
)
+from cryptography.hazmat.backends import _get_backend
from cryptography.hazmat.backends.interfaces import ScryptBackend
from cryptography.hazmat.primitives import constant_time
from cryptography.hazmat.primitives.kdf import KeyDerivationFunction
@@ -22,11 +26,12 @@ _MEM_LIMIT = sys.maxsize // 2
@utils.register_interface(KeyDerivationFunction)
class Scrypt(object):
- def __init__(self, salt, length, n, r, p, backend):
+ def __init__(self, salt, length, n, r, p, backend=None):
+ backend = _get_backend(backend)
if not isinstance(backend, ScryptBackend):
raise UnsupportedAlgorithm(
"Backend object does not implement ScryptBackend.",
- _Reasons.BACKEND_MISSING_INTERFACE
+ _Reasons.BACKEND_MISSING_INTERFACE,
)
self._length = length
diff --git a/src/cryptography/hazmat/primitives/kdf/x963kdf.py b/src/cryptography/hazmat/primitives/kdf/x963kdf.py
index fd9d125e7..1898d526a 100644
--- a/src/cryptography/hazmat/primitives/kdf/x963kdf.py
+++ b/src/cryptography/hazmat/primitives/kdf/x963kdf.py
@@ -8,25 +8,31 @@ import struct
from cryptography import utils
from cryptography.exceptions import (
- AlreadyFinalized, InvalidKey, UnsupportedAlgorithm, _Reasons
+ AlreadyFinalized,
+ InvalidKey,
+ UnsupportedAlgorithm,
+ _Reasons,
)
+from cryptography.hazmat.backends import _get_backend
from cryptography.hazmat.backends.interfaces import HashBackend
from cryptography.hazmat.primitives import constant_time, hashes
from cryptography.hazmat.primitives.kdf import KeyDerivationFunction
def _int_to_u32be(n):
- return struct.pack('>I', n)
+ return struct.pack(">I", n)
@utils.register_interface(KeyDerivationFunction)
class X963KDF(object):
- def __init__(self, algorithm, length, sharedinfo, backend):
+ def __init__(self, algorithm, length, sharedinfo, backend=None):
+ backend = _get_backend(backend)
max_len = algorithm.digest_size * (2 ** 32 - 1)
if length > max_len:
raise ValueError(
- "Can not derive keys larger than {0} bits.".format(max_len))
+ "Can not derive keys larger than {} bits.".format(max_len)
+ )
if sharedinfo is not None:
utils._check_bytes("sharedinfo", sharedinfo)
@@ -37,7 +43,7 @@ class X963KDF(object):
if not isinstance(backend, HashBackend):
raise UnsupportedAlgorithm(
"Backend object does not implement HashBackend.",
- _Reasons.BACKEND_MISSING_INTERFACE
+ _Reasons.BACKEND_MISSING_INTERFACE,
)
self._backend = backend
self._used = False
@@ -61,7 +67,7 @@ class X963KDF(object):
outlen += len(output[-1])
counter += 1
- return b"".join(output)[:self._length]
+ return b"".join(output)[: self._length]
def verify(self, key_material, expected_key):
if not constant_time.bytes_eq(self.derive(key_material), expected_key):
diff --git a/src/cryptography/hazmat/primitives/keywrap.py b/src/cryptography/hazmat/primitives/keywrap.py
index f55c519cf..2439cafe6 100644
--- a/src/cryptography/hazmat/primitives/keywrap.py
+++ b/src/cryptography/hazmat/primitives/keywrap.py
@@ -6,6 +6,7 @@ from __future__ import absolute_import, division, print_function
import struct
+from cryptography.hazmat.backends import _get_backend
from cryptography.hazmat.primitives.ciphers import Cipher
from cryptography.hazmat.primitives.ciphers.algorithms import AES
from cryptography.hazmat.primitives.ciphers.modes import ECB
@@ -33,7 +34,8 @@ def _wrap_core(wrapping_key, a, r, backend):
return a + b"".join(r)
-def aes_key_wrap(wrapping_key, key_to_wrap, backend):
+def aes_key_wrap(wrapping_key, key_to_wrap, backend=None):
+ backend = _get_backend(backend)
if len(wrapping_key) not in [16, 24, 32]:
raise ValueError("The wrapping key must be a valid AES key length")
@@ -44,7 +46,7 @@ def aes_key_wrap(wrapping_key, key_to_wrap, backend):
raise ValueError("The key to wrap must be a multiple of 8 bytes")
a = b"\xa6\xa6\xa6\xa6\xa6\xa6\xa6\xa6"
- r = [key_to_wrap[i:i + 8] for i in range(0, len(key_to_wrap), 8)]
+ r = [key_to_wrap[i : i + 8] for i in range(0, len(key_to_wrap), 8)]
return _wrap_core(wrapping_key, a, r, backend)
@@ -55,9 +57,12 @@ def _unwrap_core(wrapping_key, a, r, backend):
for j in reversed(range(6)):
for i in reversed(range(n)):
# pack/unpack are safe as these are always 64-bit chunks
- atr = struct.pack(
- ">Q", struct.unpack(">Q", a)[0] ^ ((n * j) + i + 1)
- ) + r[i]
+ atr = (
+ struct.pack(
+ ">Q", struct.unpack(">Q", a)[0] ^ ((n * j) + i + 1)
+ )
+ + r[i]
+ )
# every decryption operation is a discrete 16 byte chunk so
# it is safe to reuse the decryptor for the entire operation
b = decryptor.update(atr)
@@ -68,7 +73,8 @@ def _unwrap_core(wrapping_key, a, r, backend):
return a, r
-def aes_key_wrap_with_padding(wrapping_key, key_to_wrap, backend):
+def aes_key_wrap_with_padding(wrapping_key, key_to_wrap, backend=None):
+ backend = _get_backend(backend)
if len(wrapping_key) not in [16, 24, 32]:
raise ValueError("The wrapping key must be a valid AES key length")
@@ -83,11 +89,12 @@ def aes_key_wrap_with_padding(wrapping_key, key_to_wrap, backend):
assert encryptor.finalize() == b""
return b
else:
- r = [key_to_wrap[i:i + 8] for i in range(0, len(key_to_wrap), 8)]
+ r = [key_to_wrap[i : i + 8] for i in range(0, len(key_to_wrap), 8)]
return _wrap_core(wrapping_key, aiv, r, backend)
-def aes_key_unwrap_with_padding(wrapping_key, wrapped_key, backend):
+def aes_key_unwrap_with_padding(wrapping_key, wrapped_key, backend=None):
+ backend = _get_backend(backend)
if len(wrapped_key) < 16:
raise InvalidUnwrap("Must be at least 16 bytes")
@@ -103,7 +110,7 @@ def aes_key_unwrap_with_padding(wrapping_key, wrapped_key, backend):
data = b[8:]
n = 1
else:
- r = [wrapped_key[i:i + 8] for i in range(0, len(wrapped_key), 8)]
+ r = [wrapped_key[i : i + 8] for i in range(0, len(wrapped_key), 8)]
encrypted_aiv = r.pop(0)
n = len(r)
a, r = _unwrap_core(wrapping_key, encrypted_aiv, r, backend)
@@ -117,10 +124,9 @@ def aes_key_unwrap_with_padding(wrapping_key, wrapped_key, backend):
(mli,) = struct.unpack(">I", a[4:])
b = (8 * n) - mli
if (
- not bytes_eq(a[:4], b"\xa6\x59\x59\xa6") or not
- 8 * (n - 1) < mli <= 8 * n or (
- b != 0 and not bytes_eq(data[-b:], b"\x00" * b)
- )
+ not bytes_eq(a[:4], b"\xa6\x59\x59\xa6")
+ or not 8 * (n - 1) < mli <= 8 * n
+ or (b != 0 and not bytes_eq(data[-b:], b"\x00" * b))
):
raise InvalidUnwrap()
@@ -130,7 +136,8 @@ def aes_key_unwrap_with_padding(wrapping_key, wrapped_key, backend):
return data[:-b]
-def aes_key_unwrap(wrapping_key, wrapped_key, backend):
+def aes_key_unwrap(wrapping_key, wrapped_key, backend=None):
+ backend = _get_backend(backend)
if len(wrapped_key) < 24:
raise InvalidUnwrap("Must be at least 24 bytes")
@@ -141,7 +148,7 @@ def aes_key_unwrap(wrapping_key, wrapped_key, backend):
raise ValueError("The wrapping key must be a valid AES key length")
aiv = b"\xa6\xa6\xa6\xa6\xa6\xa6\xa6\xa6"
- r = [wrapped_key[i:i + 8] for i in range(0, len(wrapped_key), 8)]
+ r = [wrapped_key[i : i + 8] for i in range(0, len(wrapped_key), 8)]
a = r.pop(0)
a, r = _unwrap_core(wrapping_key, a, r, backend)
if not bytes_eq(a, aiv):
diff --git a/src/cryptography/hazmat/primitives/mac.py b/src/cryptography/hazmat/primitives/mac.py
deleted file mode 100644
index 4c95190ba..000000000
--- a/src/cryptography/hazmat/primitives/mac.py
+++ /dev/null
@@ -1,37 +0,0 @@
-# This file is dual licensed under the terms of the Apache License, Version
-# 2.0, and the BSD License. See the LICENSE file in the root of this repository
-# for complete details.
-
-from __future__ import absolute_import, division, print_function
-
-import abc
-
-import six
-
-
-@six.add_metaclass(abc.ABCMeta)
-class MACContext(object):
- @abc.abstractmethod
- def update(self, data):
- """
- Processes the provided bytes.
- """
-
- @abc.abstractmethod
- def finalize(self):
- """
- Returns the message authentication code as bytes.
- """
-
- @abc.abstractmethod
- def copy(self):
- """
- Return a MACContext that is a copy of the current context.
- """
-
- @abc.abstractmethod
- def verify(self, signature):
- """
- Checks if the generated message authentication code matches the
- signature.
- """
diff --git a/src/cryptography/hazmat/primitives/padding.py b/src/cryptography/hazmat/primitives/padding.py
index 170c80218..98abffbc0 100644
--- a/src/cryptography/hazmat/primitives/padding.py
+++ b/src/cryptography/hazmat/primitives/padding.py
@@ -40,14 +40,17 @@ def _byte_padding_update(buffer_, data, block_size):
if buffer_ is None:
raise AlreadyFinalized("Context was already finalized.")
- utils._check_bytes("data", data)
+ utils._check_byteslike("data", data)
- buffer_ += data
+ # six.PY2: Only coerce non-bytes objects to avoid triggering bad behavior
+ # of future's newbytes type. Unconditionally call bytes() after Python 2
+ # support is gone.
+ buffer_ += data if isinstance(data, bytes) else bytes(data)
finished_blocks = len(buffer_) // (block_size // 8)
- result = buffer_[:finished_blocks * (block_size // 8)]
- buffer_ = buffer_[finished_blocks * (block_size // 8):]
+ result = buffer_[: finished_blocks * (block_size // 8)]
+ buffer_ = buffer_[finished_blocks * (block_size // 8) :]
return buffer_, result
@@ -64,14 +67,17 @@ def _byte_unpadding_update(buffer_, data, block_size):
if buffer_ is None:
raise AlreadyFinalized("Context was already finalized.")
- utils._check_bytes("data", data)
+ utils._check_byteslike("data", data)
- buffer_ += data
+ # six.PY2: Only coerce non-bytes objects to avoid triggering bad behavior
+ # of future's newbytes type. Unconditionally call bytes() after Python 2
+ # support is gone.
+ buffer_ += data if isinstance(data, bytes) else bytes(data)
finished_blocks = max(len(buffer_) // (block_size // 8) - 1, 0)
- result = buffer_[:finished_blocks * (block_size // 8)]
- buffer_ = buffer_[finished_blocks * (block_size // 8):]
+ result = buffer_[: finished_blocks * (block_size // 8)]
+ buffer_ = buffer_[finished_blocks * (block_size // 8) :]
return buffer_, result
@@ -113,7 +119,8 @@ class _PKCS7PaddingContext(object):
def update(self, data):
self._buffer, result = _byte_padding_update(
- self._buffer, data, self.block_size)
+ self._buffer, data, self.block_size
+ )
return result
def _padding(self, size):
@@ -121,7 +128,8 @@ class _PKCS7PaddingContext(object):
def finalize(self):
result = _byte_padding_pad(
- self._buffer, self.block_size, self._padding)
+ self._buffer, self.block_size, self._padding
+ )
self._buffer = None
return result
@@ -135,13 +143,14 @@ class _PKCS7UnpaddingContext(object):
def update(self, data):
self._buffer, result = _byte_unpadding_update(
- self._buffer, data, self.block_size)
+ self._buffer, data, self.block_size
+ )
return result
def finalize(self):
result = _byte_unpadding_check(
- self._buffer, self.block_size,
- lib.Cryptography_check_pkcs7_padding)
+ self._buffer, self.block_size, lib.Cryptography_check_pkcs7_padding
+ )
self._buffer = None
return result
@@ -167,7 +176,8 @@ class _ANSIX923PaddingContext(object):
def update(self, data):
self._buffer, result = _byte_padding_update(
- self._buffer, data, self.block_size)
+ self._buffer, data, self.block_size
+ )
return result
def _padding(self, size):
@@ -175,7 +185,8 @@ class _ANSIX923PaddingContext(object):
def finalize(self):
result = _byte_padding_pad(
- self._buffer, self.block_size, self._padding)
+ self._buffer, self.block_size, self._padding
+ )
self._buffer = None
return result
@@ -189,12 +200,15 @@ class _ANSIX923UnpaddingContext(object):
def update(self, data):
self._buffer, result = _byte_unpadding_update(
- self._buffer, data, self.block_size)
+ self._buffer, data, self.block_size
+ )
return result
def finalize(self):
result = _byte_unpadding_check(
- self._buffer, self.block_size,
- lib.Cryptography_check_ansix923_padding)
+ self._buffer,
+ self.block_size,
+ lib.Cryptography_check_ansix923_padding,
+ )
self._buffer = None
return result
diff --git a/src/cryptography/hazmat/primitives/poly1305.py b/src/cryptography/hazmat/primitives/poly1305.py
new file mode 100644
index 000000000..643968620
--- /dev/null
+++ b/src/cryptography/hazmat/primitives/poly1305.py
@@ -0,0 +1,58 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import absolute_import, division, print_function
+
+
+from cryptography import utils
+from cryptography.exceptions import (
+ AlreadyFinalized,
+ UnsupportedAlgorithm,
+ _Reasons,
+)
+
+
+class Poly1305(object):
+ def __init__(self, key):
+ from cryptography.hazmat.backends.openssl.backend import backend
+
+ if not backend.poly1305_supported():
+ raise UnsupportedAlgorithm(
+ "poly1305 is not supported by this version of OpenSSL.",
+ _Reasons.UNSUPPORTED_MAC,
+ )
+ self._ctx = backend.create_poly1305_ctx(key)
+
+ def update(self, data):
+ if self._ctx is None:
+ raise AlreadyFinalized("Context was already finalized.")
+ utils._check_byteslike("data", data)
+ self._ctx.update(data)
+
+ def finalize(self):
+ if self._ctx is None:
+ raise AlreadyFinalized("Context was already finalized.")
+ mac = self._ctx.finalize()
+ self._ctx = None
+ return mac
+
+ def verify(self, tag):
+ utils._check_bytes("tag", tag)
+ if self._ctx is None:
+ raise AlreadyFinalized("Context was already finalized.")
+
+ ctx, self._ctx = self._ctx, None
+ ctx.verify(tag)
+
+ @classmethod
+ def generate_tag(cls, key, data):
+ p = Poly1305(key)
+ p.update(data)
+ return p.finalize()
+
+ @classmethod
+ def verify_tag(cls, key, data, tag):
+ p = Poly1305(key)
+ p.update(data)
+ p.verify(tag)
diff --git a/src/cryptography/hazmat/primitives/serialization/__init__.py b/src/cryptography/hazmat/primitives/serialization/__init__.py
index f6d4ce994..c2f9b014a 100644
--- a/src/cryptography/hazmat/primitives/serialization/__init__.py
+++ b/src/cryptography/hazmat/primitives/serialization/__init__.py
@@ -5,22 +5,40 @@
from __future__ import absolute_import, division, print_function
from cryptography.hazmat.primitives.serialization.base import (
- BestAvailableEncryption, Encoding, KeySerializationEncryption,
- NoEncryption, ParameterFormat, PrivateFormat, PublicFormat,
- load_der_parameters, load_der_private_key, load_der_public_key,
- load_pem_parameters, load_pem_private_key, load_pem_public_key,
+ BestAvailableEncryption,
+ Encoding,
+ KeySerializationEncryption,
+ NoEncryption,
+ ParameterFormat,
+ PrivateFormat,
+ PublicFormat,
+ load_der_parameters,
+ load_der_private_key,
+ load_der_public_key,
+ load_pem_parameters,
+ load_pem_private_key,
+ load_pem_public_key,
)
from cryptography.hazmat.primitives.serialization.ssh import (
- load_ssh_public_key
+ load_ssh_private_key,
+ load_ssh_public_key,
)
-_PEM_DER = (Encoding.PEM, Encoding.DER)
-
__all__ = [
- "load_der_parameters", "load_der_private_key", "load_der_public_key",
- "load_pem_parameters", "load_pem_private_key", "load_pem_public_key",
- "load_ssh_public_key", "Encoding", "PrivateFormat", "PublicFormat",
- "ParameterFormat", "KeySerializationEncryption", "BestAvailableEncryption",
+ "load_der_parameters",
+ "load_der_private_key",
+ "load_der_public_key",
+ "load_pem_parameters",
+ "load_pem_private_key",
+ "load_pem_public_key",
+ "load_ssh_private_key",
+ "load_ssh_public_key",
+ "Encoding",
+ "PrivateFormat",
+ "PublicFormat",
+ "ParameterFormat",
+ "KeySerializationEncryption",
+ "BestAvailableEncryption",
"NoEncryption",
]
diff --git a/src/cryptography/hazmat/primitives/serialization/base.py b/src/cryptography/hazmat/primitives/serialization/base.py
index 4218ea824..fc27235c5 100644
--- a/src/cryptography/hazmat/primitives/serialization/base.py
+++ b/src/cryptography/hazmat/primitives/serialization/base.py
@@ -10,29 +10,36 @@ from enum import Enum
import six
from cryptography import utils
+from cryptography.hazmat.backends import _get_backend
-def load_pem_private_key(data, password, backend):
+def load_pem_private_key(data, password, backend=None):
+ backend = _get_backend(backend)
return backend.load_pem_private_key(data, password)
-def load_pem_public_key(data, backend):
+def load_pem_public_key(data, backend=None):
+ backend = _get_backend(backend)
return backend.load_pem_public_key(data)
-def load_pem_parameters(data, backend):
+def load_pem_parameters(data, backend=None):
+ backend = _get_backend(backend)
return backend.load_pem_parameters(data)
-def load_der_private_key(data, password, backend):
+def load_der_private_key(data, password, backend=None):
+ backend = _get_backend(backend)
return backend.load_der_private_key(data, password)
-def load_der_public_key(data, backend):
+def load_der_public_key(data, backend=None):
+ backend = _get_backend(backend)
return backend.load_der_public_key(data)
-def load_der_parameters(data, backend):
+def load_der_parameters(data, backend=None):
+ backend = _get_backend(backend)
return backend.load_der_parameters(data)
@@ -42,12 +49,14 @@ class Encoding(Enum):
OpenSSH = "OpenSSH"
Raw = "Raw"
X962 = "ANSI X9.62"
+ SMIME = "S/MIME"
class PrivateFormat(Enum):
PKCS8 = "PKCS8"
TraditionalOpenSSL = "TraditionalOpenSSL"
Raw = "Raw"
+ OpenSSH = "OpenSSH"
class PublicFormat(Enum):
diff --git a/src/cryptography/hazmat/primitives/serialization/pkcs12.py b/src/cryptography/hazmat/primitives/serialization/pkcs12.py
index 98161d57a..201f32941 100644
--- a/src/cryptography/hazmat/primitives/serialization/pkcs12.py
+++ b/src/cryptography/hazmat/primitives/serialization/pkcs12.py
@@ -4,6 +4,47 @@
from __future__ import absolute_import, division, print_function
+from cryptography import x509
+from cryptography.hazmat.backends import _get_backend
+from cryptography.hazmat.primitives import serialization
+from cryptography.hazmat.primitives.asymmetric import dsa, ec, rsa
-def load_key_and_certificates(data, password, backend):
+
+def load_key_and_certificates(data, password, backend=None):
+ backend = _get_backend(backend)
return backend.load_key_and_certificates_from_pkcs12(data, password)
+
+
+def serialize_key_and_certificates(name, key, cert, cas, encryption_algorithm):
+ if key is not None and not isinstance(
+ key,
+ (
+ rsa.RSAPrivateKeyWithSerialization,
+ dsa.DSAPrivateKeyWithSerialization,
+ ec.EllipticCurvePrivateKeyWithSerialization,
+ ),
+ ):
+ raise TypeError("Key must be RSA, DSA, or EllipticCurve private key.")
+ if cert is not None and not isinstance(cert, x509.Certificate):
+ raise TypeError("cert must be a certificate")
+
+ if cas is not None:
+ cas = list(cas)
+ if not all(isinstance(val, x509.Certificate) for val in cas):
+ raise TypeError("all values in cas must be certificates")
+
+ if not isinstance(
+ encryption_algorithm, serialization.KeySerializationEncryption
+ ):
+ raise TypeError(
+ "Key encryption algorithm must be a "
+ "KeySerializationEncryption instance"
+ )
+
+ if key is None and cert is None and not cas:
+ raise ValueError("You must supply at least one of key, cert, or cas")
+
+ backend = _get_backend(None)
+ return backend.serialize_key_and_certificates_to_pkcs12(
+ name, key, cert, cas, encryption_algorithm
+ )
diff --git a/src/cryptography/hazmat/primitives/serialization/pkcs7.py b/src/cryptography/hazmat/primitives/serialization/pkcs7.py
new file mode 100644
index 000000000..1e11e28ef
--- /dev/null
+++ b/src/cryptography/hazmat/primitives/serialization/pkcs7.py
@@ -0,0 +1,132 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import absolute_import, division, print_function
+
+from enum import Enum
+
+from cryptography import x509
+from cryptography.hazmat.backends import _get_backend
+from cryptography.hazmat.primitives import hashes, serialization
+from cryptography.hazmat.primitives.asymmetric import ec, rsa
+from cryptography.utils import _check_byteslike
+
+
+def load_pem_pkcs7_certificates(data):
+ backend = _get_backend(None)
+ return backend.load_pem_pkcs7_certificates(data)
+
+
+def load_der_pkcs7_certificates(data):
+ backend = _get_backend(None)
+ return backend.load_der_pkcs7_certificates(data)
+
+
+class PKCS7SignatureBuilder(object):
+ def __init__(self, data=None, signers=[], additional_certs=[]):
+ self._data = data
+ self._signers = signers
+ self._additional_certs = additional_certs
+
+ def set_data(self, data):
+ _check_byteslike("data", data)
+ if self._data is not None:
+ raise ValueError("data may only be set once")
+
+ return PKCS7SignatureBuilder(data, self._signers)
+
+ def add_signer(self, certificate, private_key, hash_algorithm):
+ if not isinstance(
+ hash_algorithm,
+ (
+ hashes.SHA1,
+ hashes.SHA224,
+ hashes.SHA256,
+ hashes.SHA384,
+ hashes.SHA512,
+ ),
+ ):
+ raise TypeError(
+ "hash_algorithm must be one of hashes.SHA1, SHA224, "
+ "SHA256, SHA384, or SHA512"
+ )
+ if not isinstance(certificate, x509.Certificate):
+ raise TypeError("certificate must be a x509.Certificate")
+
+ if not isinstance(
+ private_key, (rsa.RSAPrivateKey, ec.EllipticCurvePrivateKey)
+ ):
+ raise TypeError("Only RSA & EC keys are supported at this time.")
+
+ return PKCS7SignatureBuilder(
+ self._data,
+ self._signers + [(certificate, private_key, hash_algorithm)],
+ )
+
+ def add_certificate(self, certificate):
+ if not isinstance(certificate, x509.Certificate):
+ raise TypeError("certificate must be a x509.Certificate")
+
+ return PKCS7SignatureBuilder(
+ self._data, self._signers, self._additional_certs + [certificate]
+ )
+
+ def sign(self, encoding, options, backend=None):
+ if len(self._signers) == 0:
+ raise ValueError("Must have at least one signer")
+ if self._data is None:
+ raise ValueError("You must add data to sign")
+ options = list(options)
+ if not all(isinstance(x, PKCS7Options) for x in options):
+ raise ValueError("options must be from the PKCS7Options enum")
+ if encoding not in (
+ serialization.Encoding.PEM,
+ serialization.Encoding.DER,
+ serialization.Encoding.SMIME,
+ ):
+ raise ValueError(
+ "Must be PEM, DER, or SMIME from the Encoding enum"
+ )
+
+ # Text is a meaningless option unless it is accompanied by
+ # DetachedSignature
+ if (
+ PKCS7Options.Text in options
+ and PKCS7Options.DetachedSignature not in options
+ ):
+ raise ValueError(
+ "When passing the Text option you must also pass "
+ "DetachedSignature"
+ )
+
+ if PKCS7Options.Text in options and encoding in (
+ serialization.Encoding.DER,
+ serialization.Encoding.PEM,
+ ):
+ raise ValueError(
+ "The Text option is only available for SMIME serialization"
+ )
+
+ # No attributes implies no capabilities so we'll error if you try to
+ # pass both.
+ if (
+ PKCS7Options.NoAttributes in options
+ and PKCS7Options.NoCapabilities in options
+ ):
+ raise ValueError(
+ "NoAttributes is a superset of NoCapabilities. Do not pass "
+ "both values."
+ )
+
+ backend = _get_backend(backend)
+ return backend.pkcs7_sign(self, encoding, options)
+
+
+class PKCS7Options(Enum):
+ Text = "Add text/plain MIME type"
+ Binary = "Don't translate input data into canonical MIME format"
+ DetachedSignature = "Don't embed data in the PKCS7 structure"
+ NoCapabilities = "Don't embed SMIME capabilities"
+ NoAttributes = "Don't embed authenticatedAttributes"
+ NoCerts = "Don't embed signer certificate"
diff --git a/src/cryptography/hazmat/primitives/serialization/ssh.py b/src/cryptography/hazmat/primitives/serialization/ssh.py
index cb838927d..5ecae59f8 100644
--- a/src/cryptography/hazmat/primitives/serialization/ssh.py
+++ b/src/cryptography/hazmat/primitives/serialization/ssh.py
@@ -4,139 +4,680 @@
from __future__ import absolute_import, division, print_function
-import base64
+import binascii
+import os
+import re
import struct
import six
from cryptography import utils
from cryptography.exceptions import UnsupportedAlgorithm
-from cryptography.hazmat.primitives.asymmetric import dsa, ec, rsa
-
-
-def load_ssh_public_key(data, backend):
- key_parts = data.split(b' ', 2)
-
- if len(key_parts) < 2:
- raise ValueError(
- 'Key is not in the proper format or contains extra data.')
-
- key_type = key_parts[0]
-
- if key_type == b'ssh-rsa':
- loader = _load_ssh_rsa_public_key
- elif key_type == b'ssh-dss':
- loader = _load_ssh_dss_public_key
- elif key_type in [
- b'ecdsa-sha2-nistp256', b'ecdsa-sha2-nistp384', b'ecdsa-sha2-nistp521',
- ]:
- loader = _load_ssh_ecdsa_public_key
- else:
- raise UnsupportedAlgorithm('Key type is not supported.')
-
- key_body = key_parts[1]
-
- try:
- decoded_data = base64.b64decode(key_body)
- except TypeError:
- raise ValueError('Key is not in the proper format.')
-
- inner_key_type, rest = _ssh_read_next_string(decoded_data)
-
- if inner_key_type != key_type:
+from cryptography.hazmat.backends import _get_backend
+from cryptography.hazmat.primitives.asymmetric import dsa, ec, ed25519, rsa
+from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
+from cryptography.hazmat.primitives.serialization import (
+ Encoding,
+ NoEncryption,
+ PrivateFormat,
+ PublicFormat,
+)
+
+try:
+ from bcrypt import kdf as _bcrypt_kdf
+
+ _bcrypt_supported = True
+except ImportError:
+ _bcrypt_supported = False
+
+ def _bcrypt_kdf(*args, **kwargs):
+ raise UnsupportedAlgorithm("Need bcrypt module")
+
+
+try:
+ from base64 import encodebytes as _base64_encode
+except ImportError:
+ from base64 import encodestring as _base64_encode
+
+_SSH_ED25519 = b"ssh-ed25519"
+_SSH_RSA = b"ssh-rsa"
+_SSH_DSA = b"ssh-dss"
+_ECDSA_NISTP256 = b"ecdsa-sha2-nistp256"
+_ECDSA_NISTP384 = b"ecdsa-sha2-nistp384"
+_ECDSA_NISTP521 = b"ecdsa-sha2-nistp521"
+_CERT_SUFFIX = b"-cert-v01@openssh.com"
+
+_SSH_PUBKEY_RC = re.compile(br"\A(\S+)[ \t]+(\S+)")
+_SK_MAGIC = b"openssh-key-v1\0"
+_SK_START = b"-----BEGIN OPENSSH PRIVATE KEY-----"
+_SK_END = b"-----END OPENSSH PRIVATE KEY-----"
+_BCRYPT = b"bcrypt"
+_NONE = b"none"
+_DEFAULT_CIPHER = b"aes256-ctr"
+_DEFAULT_ROUNDS = 16
+_MAX_PASSWORD = 72
+
+# re is only way to work on bytes-like data
+_PEM_RC = re.compile(_SK_START + b"(.*?)" + _SK_END, re.DOTALL)
+
+# padding for max blocksize
+_PADDING = memoryview(bytearray(range(1, 1 + 16)))
+
+# ciphers that are actually used in key wrapping
+_SSH_CIPHERS = {
+ b"aes256-ctr": (algorithms.AES, 32, modes.CTR, 16),
+ b"aes256-cbc": (algorithms.AES, 32, modes.CBC, 16),
+}
+
+# map local curve name to key type
+_ECDSA_KEY_TYPE = {
+ "secp256r1": _ECDSA_NISTP256,
+ "secp384r1": _ECDSA_NISTP384,
+ "secp521r1": _ECDSA_NISTP521,
+}
+
+_U32 = struct.Struct(b">I")
+_U64 = struct.Struct(b">Q")
+
+
+def _ecdsa_key_type(public_key):
+ """Return SSH key_type and curve_name for private key."""
+ curve = public_key.curve
+ if curve.name not in _ECDSA_KEY_TYPE:
raise ValueError(
- 'Key header and key body contain different key type values.'
+ "Unsupported curve for ssh private key: %r" % curve.name
)
-
- return loader(key_type, rest, backend)
+ return _ECDSA_KEY_TYPE[curve.name]
-def _load_ssh_rsa_public_key(key_type, decoded_data, backend):
- e, rest = _ssh_read_next_mpint(decoded_data)
- n, rest = _ssh_read_next_mpint(rest)
+def _ssh_pem_encode(data, prefix=_SK_START + b"\n", suffix=_SK_END + b"\n"):
+ return b"".join([prefix, _base64_encode(data), suffix])
- if rest:
- raise ValueError('Key body contains extra bytes.')
- return rsa.RSAPublicNumbers(e, n).public_key(backend)
+def _check_block_size(data, block_len):
+ """Require data to be full blocks"""
+ if not data or len(data) % block_len != 0:
+ raise ValueError("Corrupt data: missing padding")
-def _load_ssh_dss_public_key(key_type, decoded_data, backend):
- p, rest = _ssh_read_next_mpint(decoded_data)
- q, rest = _ssh_read_next_mpint(rest)
- g, rest = _ssh_read_next_mpint(rest)
- y, rest = _ssh_read_next_mpint(rest)
+def _check_empty(data):
+ """All data should have been parsed."""
+ if data:
+ raise ValueError("Corrupt data: unparsed data")
- if rest:
- raise ValueError('Key body contains extra bytes.')
- parameter_numbers = dsa.DSAParameterNumbers(p, q, g)
- public_numbers = dsa.DSAPublicNumbers(y, parameter_numbers)
+def _init_cipher(ciphername, password, salt, rounds, backend):
+ """Generate key + iv and return cipher."""
+ if not password:
+ raise ValueError("Key is password-protected.")
- return public_numbers.public_key(backend)
+ algo, key_len, mode, iv_len = _SSH_CIPHERS[ciphername]
+ seed = _bcrypt_kdf(password, salt, key_len + iv_len, rounds, True)
+ return Cipher(algo(seed[:key_len]), mode(seed[key_len:]), backend)
-def _load_ssh_ecdsa_public_key(expected_key_type, decoded_data, backend):
- curve_name, rest = _ssh_read_next_string(decoded_data)
- data, rest = _ssh_read_next_string(rest)
+def _get_u32(data):
+ """Uint32"""
+ if len(data) < 4:
+ raise ValueError("Invalid data")
+ return _U32.unpack(data[:4])[0], data[4:]
+
+
+def _get_u64(data):
+ """Uint64"""
+ if len(data) < 8:
+ raise ValueError("Invalid data")
+ return _U64.unpack(data[:8])[0], data[8:]
+
+
+def _get_sshstr(data):
+ """Bytes with u32 length prefix"""
+ n, data = _get_u32(data)
+ if n > len(data):
+ raise ValueError("Invalid data")
+ return data[:n], data[n:]
+
+
+def _get_mpint(data):
+ """Big integer."""
+ val, data = _get_sshstr(data)
+ if val and six.indexbytes(val, 0) > 0x7F:
+ raise ValueError("Invalid data")
+ return utils.int_from_bytes(val, "big"), data
+
+
+def _to_mpint(val):
+ """Storage format for signed bigint."""
+ if val < 0:
+ raise ValueError("negative mpint not allowed")
+ if not val:
+ return b""
+ nbytes = (val.bit_length() + 8) // 8
+ return utils.int_to_bytes(val, nbytes)
+
+
+class _FragList(object):
+ """Build recursive structure without data copy."""
+
+ def __init__(self, init=None):
+ self.flist = []
+ if init:
+ self.flist.extend(init)
+
+ def put_raw(self, val):
+ """Add plain bytes"""
+ self.flist.append(val)
+
+ def put_u32(self, val):
+ """Big-endian uint32"""
+ self.flist.append(_U32.pack(val))
+
+ def put_sshstr(self, val):
+ """Bytes prefixed with u32 length"""
+ if isinstance(val, (bytes, memoryview, bytearray)):
+ self.put_u32(len(val))
+ self.flist.append(val)
+ else:
+ self.put_u32(val.size())
+ self.flist.extend(val.flist)
+
+ def put_mpint(self, val):
+ """Big-endian bigint prefixed with u32 length"""
+ self.put_sshstr(_to_mpint(val))
+
+ def size(self):
+ """Current number of bytes"""
+ return sum(map(len, self.flist))
+
+ def render(self, dstbuf, pos=0):
+ """Write into bytearray"""
+ for frag in self.flist:
+ flen = len(frag)
+ start, pos = pos, pos + flen
+ dstbuf[start:pos] = frag
+ return pos
+
+ def tobytes(self):
+ """Return as bytes"""
+ buf = memoryview(bytearray(self.size()))
+ self.render(buf)
+ return buf.tobytes()
+
+
+class _SSHFormatRSA(object):
+ """Format for RSA keys.
+
+ Public:
+ mpint e, n
+ Private:
+ mpint n, e, d, iqmp, p, q
+ """
- if expected_key_type != b"ecdsa-sha2-" + curve_name:
- raise ValueError(
- 'Key header and key body contain different key type values.'
+ def get_public(self, data):
+ """RSA public fields"""
+ e, data = _get_mpint(data)
+ n, data = _get_mpint(data)
+ return (e, n), data
+
+ def load_public(self, key_type, data, backend):
+ """Make RSA public key from data."""
+ (e, n), data = self.get_public(data)
+ public_numbers = rsa.RSAPublicNumbers(e, n)
+ public_key = public_numbers.public_key(backend)
+ return public_key, data
+
+ def load_private(self, data, pubfields, backend):
+ """Make RSA private key from data."""
+ n, data = _get_mpint(data)
+ e, data = _get_mpint(data)
+ d, data = _get_mpint(data)
+ iqmp, data = _get_mpint(data)
+ p, data = _get_mpint(data)
+ q, data = _get_mpint(data)
+
+ if (e, n) != pubfields:
+ raise ValueError("Corrupt data: rsa field mismatch")
+ dmp1 = rsa.rsa_crt_dmp1(d, p)
+ dmq1 = rsa.rsa_crt_dmq1(d, q)
+ public_numbers = rsa.RSAPublicNumbers(e, n)
+ private_numbers = rsa.RSAPrivateNumbers(
+ p, q, d, dmp1, dmq1, iqmp, public_numbers
)
+ private_key = private_numbers.private_key(backend)
+ return private_key, data
- if rest:
- raise ValueError('Key body contains extra bytes.')
+ def encode_public(self, public_key, f_pub):
+ """Write RSA public key"""
+ pubn = public_key.public_numbers()
+ f_pub.put_mpint(pubn.e)
+ f_pub.put_mpint(pubn.n)
- curve = {
- b"nistp256": ec.SECP256R1,
- b"nistp384": ec.SECP384R1,
- b"nistp521": ec.SECP521R1,
- }[curve_name]()
+ def encode_private(self, private_key, f_priv):
+ """Write RSA private key"""
+ private_numbers = private_key.private_numbers()
+ public_numbers = private_numbers.public_numbers
- if six.indexbytes(data, 0) != 4:
- raise NotImplementedError(
- "Compressed elliptic curve points are not supported"
- )
+ f_priv.put_mpint(public_numbers.n)
+ f_priv.put_mpint(public_numbers.e)
+
+ f_priv.put_mpint(private_numbers.d)
+ f_priv.put_mpint(private_numbers.iqmp)
+ f_priv.put_mpint(private_numbers.p)
+ f_priv.put_mpint(private_numbers.q)
- return ec.EllipticCurvePublicKey.from_encoded_point(curve, data)
+class _SSHFormatDSA(object):
+ """Format for DSA keys.
-def _ssh_read_next_string(data):
+ Public:
+ mpint p, q, g, y
+ Private:
+ mpint p, q, g, y, x
"""
- Retrieves the next RFC 4251 string value from the data.
- While the RFC calls these strings, in Python they are bytes objects.
+ def get_public(self, data):
+ """DSA public fields"""
+ p, data = _get_mpint(data)
+ q, data = _get_mpint(data)
+ g, data = _get_mpint(data)
+ y, data = _get_mpint(data)
+ return (p, q, g, y), data
+
+ def load_public(self, key_type, data, backend):
+ """Make DSA public key from data."""
+ (p, q, g, y), data = self.get_public(data)
+ parameter_numbers = dsa.DSAParameterNumbers(p, q, g)
+ public_numbers = dsa.DSAPublicNumbers(y, parameter_numbers)
+ self._validate(public_numbers)
+ public_key = public_numbers.public_key(backend)
+ return public_key, data
+
+ def load_private(self, data, pubfields, backend):
+ """Make DSA private key from data."""
+ (p, q, g, y), data = self.get_public(data)
+ x, data = _get_mpint(data)
+
+ if (p, q, g, y) != pubfields:
+ raise ValueError("Corrupt data: dsa field mismatch")
+ parameter_numbers = dsa.DSAParameterNumbers(p, q, g)
+ public_numbers = dsa.DSAPublicNumbers(y, parameter_numbers)
+ self._validate(public_numbers)
+ private_numbers = dsa.DSAPrivateNumbers(x, public_numbers)
+ private_key = private_numbers.private_key(backend)
+ return private_key, data
+
+ def encode_public(self, public_key, f_pub):
+ """Write DSA public key"""
+ public_numbers = public_key.public_numbers()
+ parameter_numbers = public_numbers.parameter_numbers
+ self._validate(public_numbers)
+
+ f_pub.put_mpint(parameter_numbers.p)
+ f_pub.put_mpint(parameter_numbers.q)
+ f_pub.put_mpint(parameter_numbers.g)
+ f_pub.put_mpint(public_numbers.y)
+
+ def encode_private(self, private_key, f_priv):
+ """Write DSA private key"""
+ self.encode_public(private_key.public_key(), f_priv)
+ f_priv.put_mpint(private_key.private_numbers().x)
+
+ def _validate(self, public_numbers):
+ parameter_numbers = public_numbers.parameter_numbers
+ if parameter_numbers.p.bit_length() != 1024:
+ raise ValueError("SSH supports only 1024 bit DSA keys")
+
+
+class _SSHFormatECDSA(object):
+ """Format for ECDSA keys.
+
+ Public:
+ str curve
+ bytes point
+ Private:
+ str curve
+ bytes point
+ mpint secret
"""
- if len(data) < 4:
- raise ValueError("Key is not in the proper format")
- str_len, = struct.unpack('>I', data[:4])
- if len(data) < str_len + 4:
- raise ValueError("Key is not in the proper format")
+ def __init__(self, ssh_curve_name, curve):
+ self.ssh_curve_name = ssh_curve_name
+ self.curve = curve
+
+ def get_public(self, data):
+ """ECDSA public fields"""
+ curve, data = _get_sshstr(data)
+ point, data = _get_sshstr(data)
+ if curve != self.ssh_curve_name:
+ raise ValueError("Curve name mismatch")
+ if six.indexbytes(point, 0) != 4:
+ raise NotImplementedError("Need uncompressed point")
+ return (curve, point), data
+
+ def load_public(self, key_type, data, backend):
+ """Make ECDSA public key from data."""
+ (curve_name, point), data = self.get_public(data)
+ public_key = ec.EllipticCurvePublicKey.from_encoded_point(
+ self.curve, point.tobytes()
+ )
+ return public_key, data
+
+ def load_private(self, data, pubfields, backend):
+ """Make ECDSA private key from data."""
+ (curve_name, point), data = self.get_public(data)
+ secret, data = _get_mpint(data)
+
+ if (curve_name, point) != pubfields:
+ raise ValueError("Corrupt data: ecdsa field mismatch")
+ private_key = ec.derive_private_key(secret, self.curve, backend)
+ return private_key, data
+
+ def encode_public(self, public_key, f_pub):
+ """Write ECDSA public key"""
+ point = public_key.public_bytes(
+ Encoding.X962, PublicFormat.UncompressedPoint
+ )
+ f_pub.put_sshstr(self.ssh_curve_name)
+ f_pub.put_sshstr(point)
- return data[4:4 + str_len], data[4 + str_len:]
+ def encode_private(self, private_key, f_priv):
+ """Write ECDSA private key"""
+ public_key = private_key.public_key()
+ private_numbers = private_key.private_numbers()
+ self.encode_public(public_key, f_priv)
+ f_priv.put_mpint(private_numbers.private_value)
-def _ssh_read_next_mpint(data):
- """
- Reads the next mpint from the data.
- Currently, all mpints are interpreted as unsigned.
+class _SSHFormatEd25519(object):
+ """Format for Ed25519 keys.
+
+ Public:
+ bytes point
+ Private:
+ bytes point
+ bytes secret_and_point
"""
- mpint_data, rest = _ssh_read_next_string(data)
- return (
- utils.int_from_bytes(mpint_data, byteorder='big', signed=False), rest
- )
+ def get_public(self, data):
+ """Ed25519 public fields"""
+ point, data = _get_sshstr(data)
+ return (point,), data
+ def load_public(self, key_type, data, backend):
+ """Make Ed25519 public key from data."""
+ (point,), data = self.get_public(data)
+ public_key = ed25519.Ed25519PublicKey.from_public_bytes(
+ point.tobytes()
+ )
+ return public_key, data
+
+ def load_private(self, data, pubfields, backend):
+ """Make Ed25519 private key from data."""
+ (point,), data = self.get_public(data)
+ keypair, data = _get_sshstr(data)
+
+ secret = keypair[:32]
+ point2 = keypair[32:]
+ if point != point2 or (point,) != pubfields:
+ raise ValueError("Corrupt data: ed25519 field mismatch")
+ private_key = ed25519.Ed25519PrivateKey.from_private_bytes(secret)
+ return private_key, data
+
+ def encode_public(self, public_key, f_pub):
+ """Write Ed25519 public key"""
+ raw_public_key = public_key.public_bytes(
+ Encoding.Raw, PublicFormat.Raw
+ )
+ f_pub.put_sshstr(raw_public_key)
-def _ssh_write_string(data):
- return struct.pack(">I", len(data)) + data
+ def encode_private(self, private_key, f_priv):
+ """Write Ed25519 private key"""
+ public_key = private_key.public_key()
+ raw_private_key = private_key.private_bytes(
+ Encoding.Raw, PrivateFormat.Raw, NoEncryption()
+ )
+ raw_public_key = public_key.public_bytes(
+ Encoding.Raw, PublicFormat.Raw
+ )
+ f_keypair = _FragList([raw_private_key, raw_public_key])
+
+ self.encode_public(public_key, f_priv)
+ f_priv.put_sshstr(f_keypair)
+
+
+_KEY_FORMATS = {
+ _SSH_RSA: _SSHFormatRSA(),
+ _SSH_DSA: _SSHFormatDSA(),
+ _SSH_ED25519: _SSHFormatEd25519(),
+ _ECDSA_NISTP256: _SSHFormatECDSA(b"nistp256", ec.SECP256R1()),
+ _ECDSA_NISTP384: _SSHFormatECDSA(b"nistp384", ec.SECP384R1()),
+ _ECDSA_NISTP521: _SSHFormatECDSA(b"nistp521", ec.SECP521R1()),
+}
+
+
+def _lookup_kformat(key_type):
+ """Return valid format or throw error"""
+ if not isinstance(key_type, bytes):
+ key_type = memoryview(key_type).tobytes()
+ if key_type in _KEY_FORMATS:
+ return _KEY_FORMATS[key_type]
+ raise UnsupportedAlgorithm("Unsupported key type: %r" % key_type)
+
+
+def load_ssh_private_key(data, password, backend=None):
+ """Load private key from OpenSSH custom encoding."""
+ utils._check_byteslike("data", data)
+ backend = _get_backend(backend)
+ if password is not None:
+ utils._check_bytes("password", password)
+
+ m = _PEM_RC.search(data)
+ if not m:
+ raise ValueError("Not OpenSSH private key format")
+ p1 = m.start(1)
+ p2 = m.end(1)
+ data = binascii.a2b_base64(memoryview(data)[p1:p2])
+ if not data.startswith(_SK_MAGIC):
+ raise ValueError("Not OpenSSH private key format")
+ data = memoryview(data)[len(_SK_MAGIC) :]
+
+ # parse header
+ ciphername, data = _get_sshstr(data)
+ kdfname, data = _get_sshstr(data)
+ kdfoptions, data = _get_sshstr(data)
+ nkeys, data = _get_u32(data)
+ if nkeys != 1:
+ raise ValueError("Only one key supported")
+
+ # load public key data
+ pubdata, data = _get_sshstr(data)
+ pub_key_type, pubdata = _get_sshstr(pubdata)
+ kformat = _lookup_kformat(pub_key_type)
+ pubfields, pubdata = kformat.get_public(pubdata)
+ _check_empty(pubdata)
+
+ # load secret data
+ edata, data = _get_sshstr(data)
+ _check_empty(data)
+
+ if (ciphername, kdfname) != (_NONE, _NONE):
+ ciphername = ciphername.tobytes()
+ if ciphername not in _SSH_CIPHERS:
+ raise UnsupportedAlgorithm("Unsupported cipher: %r" % ciphername)
+ if kdfname != _BCRYPT:
+ raise UnsupportedAlgorithm("Unsupported KDF: %r" % kdfname)
+ blklen = _SSH_CIPHERS[ciphername][3]
+ _check_block_size(edata, blklen)
+ salt, kbuf = _get_sshstr(kdfoptions)
+ rounds, kbuf = _get_u32(kbuf)
+ _check_empty(kbuf)
+ ciph = _init_cipher(
+ ciphername, password, salt.tobytes(), rounds, backend
+ )
+ edata = memoryview(ciph.decryptor().update(edata))
+ else:
+ blklen = 8
+ _check_block_size(edata, blklen)
+ ck1, edata = _get_u32(edata)
+ ck2, edata = _get_u32(edata)
+ if ck1 != ck2:
+ raise ValueError("Corrupt data: broken checksum")
+
+ # load per-key struct
+ key_type, edata = _get_sshstr(edata)
+ if key_type != pub_key_type:
+ raise ValueError("Corrupt data: key type mismatch")
+ private_key, edata = kformat.load_private(edata, pubfields, backend)
+ comment, edata = _get_sshstr(edata)
+
+ # yes, SSH does padding check *after* all other parsing is done.
+ # need to follow as it writes zero-byte padding too.
+ if edata != _PADDING[: len(edata)]:
+ raise ValueError("Corrupt data: invalid padding")
+
+ return private_key
+
+
+def serialize_ssh_private_key(private_key, password=None):
+ """Serialize private key with OpenSSH custom encoding."""
+ if password is not None:
+ utils._check_bytes("password", password)
+ if password and len(password) > _MAX_PASSWORD:
+ raise ValueError(
+ "Passwords longer than 72 bytes are not supported by "
+ "OpenSSH private key format"
+ )
+
+ if isinstance(private_key, ec.EllipticCurvePrivateKey):
+ key_type = _ecdsa_key_type(private_key.public_key())
+ elif isinstance(private_key, rsa.RSAPrivateKey):
+ key_type = _SSH_RSA
+ elif isinstance(private_key, dsa.DSAPrivateKey):
+ key_type = _SSH_DSA
+ elif isinstance(private_key, ed25519.Ed25519PrivateKey):
+ key_type = _SSH_ED25519
+ else:
+ raise ValueError("Unsupported key type")
+ kformat = _lookup_kformat(key_type)
+
+ # setup parameters
+ f_kdfoptions = _FragList()
+ if password:
+ ciphername = _DEFAULT_CIPHER
+ blklen = _SSH_CIPHERS[ciphername][3]
+ kdfname = _BCRYPT
+ rounds = _DEFAULT_ROUNDS
+ salt = os.urandom(16)
+ f_kdfoptions.put_sshstr(salt)
+ f_kdfoptions.put_u32(rounds)
+ backend = _get_backend(None)
+ ciph = _init_cipher(ciphername, password, salt, rounds, backend)
+ else:
+ ciphername = kdfname = _NONE
+ blklen = 8
+ ciph = None
+ nkeys = 1
+ checkval = os.urandom(4)
+ comment = b""
+
+ # encode public and private parts together
+ f_public_key = _FragList()
+ f_public_key.put_sshstr(key_type)
+ kformat.encode_public(private_key.public_key(), f_public_key)
+
+ f_secrets = _FragList([checkval, checkval])
+ f_secrets.put_sshstr(key_type)
+ kformat.encode_private(private_key, f_secrets)
+ f_secrets.put_sshstr(comment)
+ f_secrets.put_raw(_PADDING[: blklen - (f_secrets.size() % blklen)])
+
+ # top-level structure
+ f_main = _FragList()
+ f_main.put_raw(_SK_MAGIC)
+ f_main.put_sshstr(ciphername)
+ f_main.put_sshstr(kdfname)
+ f_main.put_sshstr(f_kdfoptions)
+ f_main.put_u32(nkeys)
+ f_main.put_sshstr(f_public_key)
+ f_main.put_sshstr(f_secrets)
+
+ # copy result info bytearray
+ slen = f_secrets.size()
+ mlen = f_main.size()
+ buf = memoryview(bytearray(mlen + blklen))
+ f_main.render(buf)
+ ofs = mlen - slen
+
+ # encrypt in-place
+ if ciph is not None:
+ ciph.encryptor().update_into(buf[ofs:mlen], buf[ofs:])
+
+ txt = _ssh_pem_encode(buf[:mlen])
+ buf[ofs:mlen] = bytearray(slen)
+ return txt
+
+
+def load_ssh_public_key(data, backend=None):
+ """Load public key from OpenSSH one-line format."""
+ backend = _get_backend(backend)
+ utils._check_byteslike("data", data)
+
+ m = _SSH_PUBKEY_RC.match(data)
+ if not m:
+ raise ValueError("Invalid line format")
+ key_type = orig_key_type = m.group(1)
+ key_body = m.group(2)
+ with_cert = False
+ if _CERT_SUFFIX == key_type[-len(_CERT_SUFFIX) :]:
+ with_cert = True
+ key_type = key_type[: -len(_CERT_SUFFIX)]
+ kformat = _lookup_kformat(key_type)
+
+ try:
+ data = memoryview(binascii.a2b_base64(key_body))
+ except (TypeError, binascii.Error):
+ raise ValueError("Invalid key format")
+
+ inner_key_type, data = _get_sshstr(data)
+ if inner_key_type != orig_key_type:
+ raise ValueError("Invalid key format")
+ if with_cert:
+ nonce, data = _get_sshstr(data)
+ public_key, data = kformat.load_public(key_type, data, backend)
+ if with_cert:
+ serial, data = _get_u64(data)
+ cctype, data = _get_u32(data)
+ key_id, data = _get_sshstr(data)
+ principals, data = _get_sshstr(data)
+ valid_after, data = _get_u64(data)
+ valid_before, data = _get_u64(data)
+ crit_options, data = _get_sshstr(data)
+ extensions, data = _get_sshstr(data)
+ reserved, data = _get_sshstr(data)
+ sig_key, data = _get_sshstr(data)
+ signature, data = _get_sshstr(data)
+ _check_empty(data)
+ return public_key
+
+
+def serialize_ssh_public_key(public_key):
+ """One-line public key format for OpenSSH"""
+ if isinstance(public_key, ec.EllipticCurvePublicKey):
+ key_type = _ecdsa_key_type(public_key)
+ elif isinstance(public_key, rsa.RSAPublicKey):
+ key_type = _SSH_RSA
+ elif isinstance(public_key, dsa.DSAPublicKey):
+ key_type = _SSH_DSA
+ elif isinstance(public_key, ed25519.Ed25519PublicKey):
+ key_type = _SSH_ED25519
+ else:
+ raise ValueError("Unsupported key type")
+ kformat = _lookup_kformat(key_type)
+ f_pub = _FragList()
+ f_pub.put_sshstr(key_type)
+ kformat.encode_public(public_key, f_pub)
-def _ssh_write_mpint(value):
- data = utils.int_to_bytes(value)
- if six.indexbytes(data, 0) & 0x80:
- data = b"\x00" + data
- return _ssh_write_string(data)
+ pub = binascii.b2a_base64(f_pub.tobytes()).strip()
+ return b"".join([key_type, b" ", pub])
diff --git a/src/cryptography/hazmat/primitives/twofactor/hotp.py b/src/cryptography/hazmat/primitives/twofactor/hotp.py
index 4ad1bdc2f..c00eec0e5 100644
--- a/src/cryptography/hazmat/primitives/twofactor/hotp.py
+++ b/src/cryptography/hazmat/primitives/twofactor/hotp.py
@@ -8,9 +8,8 @@ import struct
import six
-from cryptography.exceptions import (
- UnsupportedAlgorithm, _Reasons
-)
+from cryptography.exceptions import UnsupportedAlgorithm, _Reasons
+from cryptography.hazmat.backends import _get_backend
from cryptography.hazmat.backends.interfaces import HMACBackend
from cryptography.hazmat.primitives import constant_time, hmac
from cryptography.hazmat.primitives.hashes import SHA1, SHA256, SHA512
@@ -19,12 +18,14 @@ from cryptography.hazmat.primitives.twofactor.utils import _generate_uri
class HOTP(object):
- def __init__(self, key, length, algorithm, backend,
- enforce_key_length=True):
+ def __init__(
+ self, key, length, algorithm, backend=None, enforce_key_length=True
+ ):
+ backend = _get_backend(backend)
if not isinstance(backend, HMACBackend):
raise UnsupportedAlgorithm(
"Backend object does not implement HMACBackend.",
- _Reasons.BACKEND_MISSING_INTERFACE
+ _Reasons.BACKEND_MISSING_INTERFACE,
)
if len(key) < 16 and enforce_key_length is True:
@@ -59,10 +60,10 @@ class HOTP(object):
hmac_value = ctx.finalize()
offset = six.indexbytes(hmac_value, len(hmac_value) - 1) & 0b1111
- p = hmac_value[offset:offset + 4]
- return struct.unpack(">I", p)[0] & 0x7fffffff
+ p = hmac_value[offset : offset + 4]
+ return struct.unpack(">I", p)[0] & 0x7FFFFFFF
def get_provisioning_uri(self, account_name, counter, issuer):
- return _generate_uri(self, "hotp", account_name, issuer, [
- ("counter", int(counter)),
- ])
+ return _generate_uri(
+ self, "hotp", account_name, issuer, [("counter", int(counter))]
+ )
diff --git a/src/cryptography/hazmat/primitives/twofactor/totp.py b/src/cryptography/hazmat/primitives/twofactor/totp.py
index 499f2824a..d59539b3f 100644
--- a/src/cryptography/hazmat/primitives/twofactor/totp.py
+++ b/src/cryptography/hazmat/primitives/twofactor/totp.py
@@ -4,9 +4,8 @@
from __future__ import absolute_import, division, print_function
-from cryptography.exceptions import (
- UnsupportedAlgorithm, _Reasons
-)
+from cryptography.exceptions import UnsupportedAlgorithm, _Reasons
+from cryptography.hazmat.backends import _get_backend
from cryptography.hazmat.backends.interfaces import HMACBackend
from cryptography.hazmat.primitives import constant_time
from cryptography.hazmat.primitives.twofactor import InvalidToken
@@ -15,12 +14,20 @@ from cryptography.hazmat.primitives.twofactor.utils import _generate_uri
class TOTP(object):
- def __init__(self, key, length, algorithm, time_step, backend,
- enforce_key_length=True):
+ def __init__(
+ self,
+ key,
+ length,
+ algorithm,
+ time_step,
+ backend=None,
+ enforce_key_length=True,
+ ):
+ backend = _get_backend(backend)
if not isinstance(backend, HMACBackend):
raise UnsupportedAlgorithm(
"Backend object does not implement HMACBackend.",
- _Reasons.BACKEND_MISSING_INTERFACE
+ _Reasons.BACKEND_MISSING_INTERFACE,
)
self._time_step = time_step
@@ -35,6 +42,10 @@ class TOTP(object):
raise InvalidToken("Supplied TOTP value does not match.")
def get_provisioning_uri(self, account_name, issuer):
- return _generate_uri(self._hotp, "totp", account_name, issuer, [
- ("period", int(self._time_step)),
- ])
+ return _generate_uri(
+ self._hotp,
+ "totp",
+ account_name,
+ issuer,
+ [("period", int(self._time_step))],
+ )
diff --git a/src/cryptography/hazmat/primitives/twofactor/utils.py b/src/cryptography/hazmat/primitives/twofactor/utils.py
index 0ed8c4c89..0afa1ccc0 100644
--- a/src/cryptography/hazmat/primitives/twofactor/utils.py
+++ b/src/cryptography/hazmat/primitives/twofactor/utils.py
@@ -23,8 +23,11 @@ def _generate_uri(hotp, type_name, account_name, issuer, extra_parameters):
uriparts = {
"type": type_name,
- "label": ("%s:%s" % (quote(issuer), quote(account_name)) if issuer
- else quote(account_name)),
+ "label": (
+ "%s:%s" % (quote(issuer), quote(account_name))
+ if issuer
+ else quote(account_name)
+ ),
"parameters": urlencode(parameters),
}
return "otpauth://{type}/{label}?{parameters}".format(**uriparts)