summaryrefslogtreecommitdiff
path: root/src/cryptography/hazmat/primitives
diff options
context:
space:
mode:
Diffstat (limited to 'src/cryptography/hazmat/primitives')
-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
37 files changed, 1556 insertions, 497 deletions
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)