update windows build to Python 3.7

This commit is contained in:
j 2019-01-20 16:05:31 +05:30
commit ddc59ab92d
5761 changed files with 750298 additions and 213405 deletions

View file

@ -11,6 +11,10 @@ import six
from cryptography import utils
def generate_parameters(generator, key_size, backend):
return backend.generate_dh_parameters(generator, key_size)
class DHPrivateNumbers(object):
def __init__(self, x, public_numbers):
if not isinstance(x, six.integer_types):
@ -35,6 +39,9 @@ class DHPrivateNumbers(object):
def __ne__(self, other):
return not self == other
def private_key(self, backend):
return backend.load_dh_private_numbers(self)
public_numbers = utils.read_only_property("_public_numbers")
x = utils.read_only_property("_x")
@ -63,20 +70,29 @@ class DHPublicNumbers(object):
def __ne__(self, other):
return not self == other
def public_key(self, backend):
return backend.load_dh_public_numbers(self)
y = utils.read_only_property("_y")
parameter_numbers = utils.read_only_property("_parameter_numbers")
class DHParameterNumbers(object):
def __init__(self, p, g):
def __init__(self, p, g, q=None):
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):
raise TypeError("q must be integer or None")
if g < 2:
raise ValueError("DH generator must be 2 or greater")
self._p = p
self._g = g
self._q = q
def __eq__(self, other):
if not isinstance(other, DHParameterNumbers):
@ -84,14 +100,19 @@ class DHParameterNumbers(object):
return (
self._p == other._p and
self._g == other._g
self._g == other._g and
self._q == other._q
)
def __ne__(self, other):
return not self == other
def parameters(self, backend):
return backend.load_dh_parameter_numbers(self)
p = utils.read_only_property("_p")
g = utils.read_only_property("_g")
q = utils.read_only_property("_q")
@six.add_metaclass(abc.ABCMeta)
@ -102,9 +123,12 @@ class DHParameters(object):
Generates and returns a DHPrivateKey.
"""
@abc.abstractmethod
def parameter_bytes(self, encoding, format):
"""
Returns the parameters serialized as bytes.
"""
@six.add_metaclass(abc.ABCMeta)
class DHParametersWithSerialization(DHParameters):
@abc.abstractmethod
def parameter_numbers(self):
"""
@ -112,6 +136,9 @@ class DHParametersWithSerialization(DHParameters):
"""
DHParametersWithSerialization = DHParameters
@six.add_metaclass(abc.ABCMeta)
class DHPrivateKey(object):
@abc.abstractproperty
@ -132,6 +159,13 @@ class DHPrivateKey(object):
The DHParameters object associated with this private key.
"""
@abc.abstractmethod
def exchange(self, peer_public_key):
"""
Given peer's DHPublicKey, carry out the key exchange and
return shared key as bytes.
"""
@six.add_metaclass(abc.ABCMeta)
class DHPrivateKeyWithSerialization(DHPrivateKey):
@ -141,6 +175,12 @@ class DHPrivateKeyWithSerialization(DHPrivateKey):
Returns a DHPrivateNumbers.
"""
@abc.abstractmethod
def private_bytes(self, encoding, format, encryption_algorithm):
"""
Returns the key serialized as bytes.
"""
@six.add_metaclass(abc.ABCMeta)
class DHPublicKey(object):
@ -156,11 +196,17 @@ class DHPublicKey(object):
The DHParameters object associated with this public key.
"""
@six.add_metaclass(abc.ABCMeta)
class DHPublicKeyWithSerialization(DHPublicKey):
@abc.abstractmethod
def public_numbers(self):
"""
Returns a DHPublicNumbers.
"""
@abc.abstractmethod
def public_bytes(self, encoding, format):
"""
Returns the key serialized as bytes.
"""
DHPublicKeyWithSerialization = DHPublicKey

View file

@ -55,6 +55,12 @@ class DSAPrivateKey(object):
Returns an AsymmetricSignatureContext used for signing data.
"""
@abc.abstractmethod
def sign(self, data, algorithm):
"""
Signs the data
"""
@six.add_metaclass(abc.ABCMeta)
class DSAPrivateKeyWithSerialization(DSAPrivateKey):
@ -103,6 +109,12 @@ class DSAPublicKey(object):
Returns the key serialized as bytes.
"""
@abc.abstractmethod
def verify(self, signature, data, algorithm):
"""
Verifies the signature of the data.
"""
DSAPublicKeyWithSerialization = DSAPublicKey
@ -116,10 +128,10 @@ def generate_private_key(key_size, backend):
def _check_dsa_parameters(parameters):
if utils.bit_length(parameters.p) not in [1024, 2048, 3072]:
if parameters.p.bit_length() not in [1024, 2048, 3072]:
raise ValueError("p must be exactly 1024, 2048, or 3072 bits long")
if utils.bit_length(parameters.q) not in [160, 256]:
raise ValueError("q must be exactly 160 or 256 bits long")
if parameters.q.bit_length() not in [160, 224, 256]:
raise ValueError("q must be exactly 160, 224, or 256 bits long")
if not (1 < parameters.g < parameters.p):
raise ValueError("g, p don't satisfy 1 < g < p.")
@ -166,6 +178,13 @@ class DSAParameterNumbers(object):
def __ne__(self, other):
return not self == other
def __repr__(self):
return (
"<DSAParameterNumbers(p={self.p}, q={self.q}, g={self.g})>".format(
self=self
)
)
class DSAPublicNumbers(object):
def __init__(self, y, parameter_numbers):
@ -198,6 +217,12 @@ class DSAPublicNumbers(object):
def __ne__(self, other):
return not self == other
def __repr__(self):
return (
"<DSAPublicNumbers(y={self.y}, "
"parameter_numbers={self.parameter_numbers})>".format(self=self)
)
class DSAPrivateNumbers(object):
def __init__(self, x, public_numbers):

View file

@ -9,6 +9,16 @@ import abc
import six
from cryptography import utils
from cryptography.hazmat._oid import ObjectIdentifier
class EllipticCurveOID(object):
SECP192R1 = ObjectIdentifier("1.2.840.10045.3.1.1")
SECP224R1 = ObjectIdentifier("1.3.132.0.33")
SECP256K1 = ObjectIdentifier("1.3.132.0.10")
SECP256R1 = ObjectIdentifier("1.2.840.10045.3.1.7")
SECP384R1 = ObjectIdentifier("1.3.132.0.34")
SECP521R1 = ObjectIdentifier("1.3.132.0.35")
@six.add_metaclass(abc.ABCMeta)
@ -22,7 +32,7 @@ class EllipticCurve(object):
@abc.abstractproperty
def key_size(self):
"""
The bit length of the base point of the curve.
Bit size of a secret scalar for the curve.
"""
@ -62,6 +72,18 @@ class EllipticCurvePrivateKey(object):
The EllipticCurve that this key is on.
"""
@abc.abstractproperty
def key_size(self):
"""
Bit size of a secret scalar for the curve.
"""
@abc.abstractmethod
def sign(self, data, signature_algorithm):
"""
Signs the data
"""
@six.add_metaclass(abc.ABCMeta)
class EllipticCurvePrivateKeyWithSerialization(EllipticCurvePrivateKey):
@ -92,6 +114,12 @@ class EllipticCurvePublicKey(object):
The EllipticCurve that this key is on.
"""
@abc.abstractproperty
def key_size(self):
"""
Bit size of a secret scalar for the curve.
"""
@abc.abstractmethod
def public_numbers(self):
"""
@ -104,6 +132,12 @@ class EllipticCurvePublicKey(object):
Returns the key serialized as bytes.
"""
@abc.abstractmethod
def verify(self, signature, data, signature_algorithm):
"""
Verifies the signature of the data.
"""
EllipticCurvePublicKeyWithSerialization = EllipticCurvePublicKey
@ -111,7 +145,7 @@ EllipticCurvePublicKeyWithSerialization = EllipticCurvePublicKey
@utils.register_interface(EllipticCurve)
class SECT571R1(object):
name = "sect571r1"
key_size = 571
key_size = 570
@utils.register_interface(EllipticCurve)
@ -204,6 +238,24 @@ class SECP192R1(object):
key_size = 192
@utils.register_interface(EllipticCurve)
class BrainpoolP256R1(object):
name = "brainpoolP256r1"
key_size = 256
@utils.register_interface(EllipticCurve)
class BrainpoolP384R1(object):
name = "brainpoolP384r1"
key_size = 384
@utils.register_interface(EllipticCurve)
class BrainpoolP512R1(object):
name = "brainpoolP512r1"
key_size = 512
_CURVE_TYPES = {
"prime192v1": SECP192R1,
"prime256v1": SECP256R1,
@ -226,6 +278,10 @@ _CURVE_TYPES = {
"sect283r1": SECT283R1,
"sect409r1": SECT409R1,
"sect571r1": SECT571R1,
"brainpoolP256r1": BrainpoolP256R1,
"brainpoolP384r1": BrainpoolP384R1,
"brainpoolP512r1": BrainpoolP512R1,
}
@ -241,6 +297,19 @@ def generate_private_key(curve, backend):
return backend.generate_elliptic_curve_private_key(curve)
def derive_private_key(private_value, curve, backend):
if not isinstance(private_value, six.integer_types):
raise TypeError("private_value must be an integer type.")
if private_value <= 0:
raise ValueError("private_value must be a positive integer.")
if not isinstance(curve, EllipticCurve):
raise TypeError("curve must provide the EllipticCurve interface.")
return backend.derive_elliptic_curve_private_key(private_value, curve)
class EllipticCurvePublicNumbers(object):
def __init__(self, x, y, curve):
if (
@ -302,6 +371,9 @@ class EllipticCurvePublicNumbers(object):
def __ne__(self, other):
return not self == other
def __hash__(self):
return hash((self.x, self.y, self.curve.name, self.curve.key_size))
def __repr__(self):
return (
"<EllipticCurvePublicNumbers(curve={0.curve.name}, x={0.x}, "
@ -341,6 +413,9 @@ class EllipticCurvePrivateNumbers(object):
def __ne__(self, other):
return not self == other
def __hash__(self):
return hash((self.private_value, self.public_numbers))
class ECDH(object):
pass

View file

@ -5,11 +5,13 @@
from __future__ import absolute_import, division, print_function
import abc
import math
import six
from cryptography import utils
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import rsa
@six.add_metaclass(abc.ABCMeta)
@ -65,3 +67,13 @@ class MGF1(object):
raise TypeError("Expected instance of hashes.HashAlgorithm.")
self._algorithm = algorithm
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))
salt_length = emlen - hash_algorithm.digest_size - 2
assert salt_length >= 0
return salt_length

View file

@ -5,7 +5,11 @@
from __future__ import absolute_import, division, print_function
import abc
from fractions import gcd
try:
# Only available in math in 3.5+
from math import gcd
except ImportError:
from fractions import gcd
import six
@ -40,6 +44,12 @@ class RSAPrivateKey(object):
The RSAPublicKey associated with this private key.
"""
@abc.abstractmethod
def sign(self, data, padding, algorithm):
"""
Signs the data.
"""
@six.add_metaclass(abc.ABCMeta)
class RSAPrivateKeyWithSerialization(RSAPrivateKey):
@ -88,6 +98,12 @@ class RSAPublicKey(object):
Returns the key serialized as bytes.
"""
@abc.abstractmethod
def verify(self, signature, data, padding, algorithm):
"""
Verifies the signature of the data.
"""
RSAPublicKeyWithSerialization = RSAPublicKey
@ -187,7 +203,7 @@ def rsa_crt_iqmp(p, q):
def rsa_crt_dmp1(private_exponent, p):
"""
Compute the CRT private_exponent % (p - 1) value from the RSA
private_exponent and p.
private_exponent (d) and p.
"""
return private_exponent % (p - 1)
@ -195,7 +211,7 @@ def rsa_crt_dmp1(private_exponent, p):
def rsa_crt_dmq1(private_exponent, q):
"""
Compute the CRT private_exponent % (q - 1) value from the RSA
private_exponent and q.
private_exponent (d) and q.
"""
return private_exponent % (q - 1)
@ -245,7 +261,7 @@ def rsa_recover_prime_factors(n, e, d):
# Found !
q, r = divmod(n, p)
assert r == 0
p, q = sorted((p, q), reverse=True)
return (p, q)

View file

@ -6,55 +6,34 @@ from __future__ import absolute_import, division, print_function
import warnings
from pyasn1.codec.der import decoder, encoder
from pyasn1.error import PyAsn1Error
from pyasn1.type import namedtype, univ
from asn1crypto.algos import DSASignature
import six
from cryptography import utils
class _DSSSigValue(univ.Sequence):
componentType = namedtype.NamedTypes(
namedtype.NamedType('r', univ.Integer()),
namedtype.NamedType('s', univ.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 "
"instead.",
utils.DeprecatedIn10,
"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):
try:
data, remaining = decoder.decode(signature, asn1Spec=_DSSSigValue())
except PyAsn1Error:
raise ValueError("Invalid signature data. Unable to decode ASN.1")
if remaining:
raise ValueError(
"The signature contains bytes after the end of the ASN.1 sequence."
)
r = int(data.getComponentByName('r'))
s = int(data.getComponentByName('s'))
return (r, s)
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 "
"instead.",
utils.DeprecatedIn10,
"be removed in a future version, use encode_dss_signature instead.",
utils.PersistentlyDeprecated,
stacklevel=2
)
return encode_dss_signature(r, s)
@ -67,7 +46,15 @@ def encode_dss_signature(r, s):
):
raise ValueError("Both r and s must be integers")
sig = _DSSSigValue()
sig.setComponentByName('r', r)
sig.setComponentByName('s', s)
return encoder.encode(sig)
return DSASignature({'r': r, 's': s}).dump()
class Prehashed(object):
def __init__(self, algorithm):
if not isinstance(algorithm, hashes.HashAlgorithm):
raise TypeError("Expected instance of HashAlgorithm.")
self._algorithm = algorithm
self._digest_size = algorithm.digest_size
digest_size = utils.read_only_property("_digest_size")

View file

@ -0,0 +1,54 @@
# 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 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
)
return backend.x25519_load_public_bytes(data)
@abc.abstractmethod
def public_bytes(self):
pass
@six.add_metaclass(abc.ABCMeta)
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
)
return backend.x25519_generate_key()
@classmethod
def _from_private_bytes(cls, data):
from cryptography.hazmat.backends.openssl.backend import backend
return backend.x25519_load_private_bytes(data)
@abc.abstractmethod
def public_key(self):
pass
@abc.abstractmethod
def exchange(self, peer_public_key):
pass

View file

@ -5,8 +5,8 @@
from __future__ import absolute_import, division, print_function
from cryptography.hazmat.primitives.ciphers.base import (
AEADCipherContext, AEADEncryptionContext, BlockCipherAlgorithm, Cipher,
CipherAlgorithm, CipherContext
AEADCipherContext, AEADDecryptionContext, AEADEncryptionContext,
BlockCipherAlgorithm, Cipher, CipherAlgorithm, CipherContext
)
@ -16,5 +16,6 @@ __all__ = [
"BlockCipherAlgorithm",
"CipherContext",
"AEADCipherContext",
"AEADDecryptionContext",
"AEADEncryptionContext",
]

View file

@ -0,0 +1,188 @@
# 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 os
from cryptography import exceptions, utils
from cryptography.hazmat.backends.openssl import aead
from cryptography.hazmat.backends.openssl.backend import backend
class ChaCha20Poly1305(object):
_MAX_SIZE = 2 ** 32
def __init__(self, key):
if not backend.aead_cipher_supported(self):
raise exceptions.UnsupportedAlgorithm(
"ChaCha20Poly1305 is not supported by this version of OpenSSL",
exceptions._Reasons.UNSUPPORTED_CIPHER
)
utils._check_bytes("key", key)
if len(key) != 32:
raise ValueError("ChaCha20Poly1305 key must be 32 bytes.")
self._key = key
@classmethod
def generate_key(cls):
return os.urandom(32)
def encrypt(self, nonce, data, associated_data):
if associated_data is None:
associated_data = b""
if len(data) > self._MAX_SIZE or len(associated_data) > self._MAX_SIZE:
# This is OverflowError to match what cffi would raise
raise OverflowError(
"Data or associated data too long. Max 2**32 bytes"
)
self._check_params(nonce, data, associated_data)
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
)
def _check_params(self, nonce, data, associated_data):
utils._check_bytes("nonce", nonce)
utils._check_bytes("data", data)
utils._check_bytes("associated_data", associated_data)
if len(nonce) != 12:
raise ValueError("Nonce must be 12 bytes")
class AESCCM(object):
_MAX_SIZE = 2 ** 32
def __init__(self, key, tag_length=16):
utils._check_bytes("key", key)
if len(key) not in (16, 24, 32):
raise ValueError("AESCCM key must be 128, 192, or 256 bits.")
self._key = key
if not isinstance(tag_length, int):
raise TypeError("tag_length must be an integer")
if tag_length not in (4, 6, 8, 10, 12, 14, 16):
raise ValueError("Invalid tag_length")
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):
raise TypeError("bit_length must be an integer")
if bit_length not in (128, 192, 256):
raise ValueError("bit_length must be 128, 192, or 256")
return os.urandom(bit_length // 8)
def encrypt(self, nonce, data, associated_data):
if associated_data is None:
associated_data = b""
if len(data) > self._MAX_SIZE or len(associated_data) > self._MAX_SIZE:
# This is OverflowError to match what cffi would raise
raise OverflowError(
"Data or associated data too long. Max 2**32 bytes"
)
self._check_params(nonce, data, associated_data)
self._validate_lengths(nonce, len(data))
return aead._encrypt(
backend, self, nonce, data, associated_data, self._tag_length
)
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, self._tag_length
)
def _validate_lengths(self, nonce, data_len):
# For information about computing this, see
# 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")
def _check_params(self, nonce, data, associated_data):
utils._check_bytes("nonce", nonce)
utils._check_bytes("data", data)
utils._check_bytes("associated_data", associated_data)
if not 7 <= len(nonce) <= 13:
raise ValueError("Nonce must be between 7 and 13 bytes")
class AESGCM(object):
_MAX_SIZE = 2 ** 32
def __init__(self, key):
utils._check_bytes("key", key)
if len(key) not in (16, 24, 32):
raise ValueError("AESGCM key must be 128, 192, or 256 bits.")
self._key = key
@classmethod
def generate_key(cls, bit_length):
if not isinstance(bit_length, int):
raise TypeError("bit_length must be an integer")
if bit_length not in (128, 192, 256):
raise ValueError("bit_length must be 128, 192, or 256")
return os.urandom(bit_length // 8)
def encrypt(self, nonce, data, associated_data):
if associated_data is None:
associated_data = b""
if len(data) > self._MAX_SIZE or len(associated_data) > self._MAX_SIZE:
# This is OverflowError to match what cffi would raise
raise OverflowError(
"Data or associated data too long. Max 2**32 bytes"
)
self._check_params(nonce, data, associated_data)
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
)
def _check_params(self, nonce, data, associated_data):
utils._check_bytes("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")

View file

@ -8,9 +8,13 @@ from cryptography import utils
from cryptography.hazmat.primitives.ciphers import (
BlockCipherAlgorithm, CipherAlgorithm
)
from cryptography.hazmat.primitives.ciphers.modes import ModeWithNonce
def _verify_key_size(algorithm, key):
# Verify that the key is instance of bytes
utils._check_bytes("key", 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(
@ -24,7 +28,8 @@ def _verify_key_size(algorithm, key):
class AES(object):
name = "AES"
block_size = 128
key_sizes = frozenset([128, 192, 256])
# 512 added to support AES-256-XTS, which uses 512-bit keys
key_sizes = frozenset([128, 192, 256, 512])
def __init__(self, key):
self.key = _verify_key_size(self, key)
@ -138,3 +143,26 @@ class SEED(object):
@property
def key_size(self):
return len(self.key) * 8
@utils.register_interface(CipherAlgorithm)
@utils.register_interface(ModeWithNonce)
class ChaCha20(object):
name = "ChaCha20"
key_sizes = frozenset([256])
def __init__(self, key, nonce):
self.key = _verify_key_size(self, key)
if not isinstance(nonce, bytes):
raise TypeError("nonce must be bytes")
if len(nonce) != 16:
raise ValueError("nonce must be 128-bits (16 bytes)")
self._nonce = nonce
nonce = utils.read_only_property("_nonce")
@property
def key_size(self):
return len(self.key) * 8

View file

@ -50,6 +50,13 @@ class CipherContext(object):
as bytes.
"""
@abc.abstractmethod
def update_into(self, data, buf):
"""
Processes the provided bytes and writes the resulting data into the
provided buffer. Returns the number of bytes written.
"""
@abc.abstractmethod
def finalize(self):
"""
@ -66,6 +73,16 @@ class AEADCipherContext(object):
"""
@six.add_metaclass(abc.ABCMeta)
class AEADDecryptionContext(object):
@abc.abstractmethod
def finalize_with_tag(self, tag):
"""
Returns the results of processing the final block as bytes and allows
delayed passing of the authentication tag.
"""
@six.add_metaclass(abc.ABCMeta)
class AEADEncryptionContext(object):
@abc.abstractproperty
@ -106,11 +123,6 @@ class Cipher(object):
return self._wrap_ctx(ctx, encrypt=True)
def decryptor(self):
if isinstance(self.mode, modes.ModeWithAuthenticationTag):
if self.mode.tag is None:
raise ValueError(
"Authentication tag must be provided when decrypting."
)
ctx = self._backend.create_symmetric_decryption_ctx(
self.algorithm, self.mode
)
@ -136,6 +148,11 @@ class _CipherContext(object):
raise AlreadyFinalized("Context was already finalized.")
return self._ctx.update(data)
def update_into(self, data, buf):
if self._ctx is None:
raise AlreadyFinalized("Context was already finalized.")
return self._ctx.update_into(data, buf)
def finalize(self):
if self._ctx is None:
raise AlreadyFinalized("Context was already finalized.")
@ -146,6 +163,7 @@ class _CipherContext(object):
@utils.register_interface(AEADCipherContext)
@utils.register_interface(CipherContext)
@utils.register_interface(AEADDecryptionContext)
class _AEADCipherContext(object):
def __init__(self, ctx):
self._ctx = ctx
@ -154,11 +172,11 @@ class _AEADCipherContext(object):
self._tag = None
self._updated = False
def update(self, data):
def _check_limit(self, data_size):
if self._ctx is None:
raise AlreadyFinalized("Context was already finalized.")
self._updated = True
self._bytes_processed += len(data)
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(
@ -166,8 +184,14 @@ class _AEADCipherContext(object):
)
)
def update(self, data):
self._check_limit(len(data))
return self._ctx.update(data)
def update_into(self, data, buf):
self._check_limit(len(data))
return self._ctx.update_into(data, buf)
def finalize(self):
if self._ctx is None:
raise AlreadyFinalized("Context was already finalized.")
@ -176,6 +200,14 @@ class _AEADCipherContext(object):
self._ctx = None
return data
def finalize_with_tag(self, tag):
if self._ctx is None:
raise AlreadyFinalized("Context was already finalized.")
data = self._ctx.finalize_with_tag(tag)
self._tag = self._ctx.tag
self._ctx = None
return data
def authenticate_additional_data(self, data):
if self._ctx is None:
raise AlreadyFinalized("Context was already finalized.")
@ -185,7 +217,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 {0}".format(
"{0} has a maximum AAD byte limit of {1}".format(
self._ctx._mode.name, self._ctx._mode._MAX_AAD_BYTES
)
)

View file

@ -36,6 +36,15 @@ class ModeWithInitializationVector(object):
"""
@six.add_metaclass(abc.ABCMeta)
class ModeWithTweak(object):
@abc.abstractproperty
def tweak(self):
"""
The value of the tweak for this mode as bytes.
"""
@six.add_metaclass(abc.ABCMeta)
class ModeWithNonce(object):
@abc.abstractproperty
@ -54,6 +63,13 @@ class ModeWithAuthenticationTag(object):
"""
def _check_aes_key_length(self, algorithm):
if algorithm.key_size > 256 and algorithm.name == "AES":
raise ValueError(
"Only 128, 192, and 256 bit keys are allowed for this AES mode"
)
def _check_iv_length(self, algorithm):
if len(self.initialization_vector) * 8 != algorithm.block_size:
raise ValueError("Invalid IV size ({0}) for {1}.".format(
@ -61,24 +77,55 @@ def _check_iv_length(self, algorithm):
))
def _check_iv_and_key_length(self, algorithm):
_check_aes_key_length(self, algorithm)
_check_iv_length(self, algorithm)
@utils.register_interface(Mode)
@utils.register_interface(ModeWithInitializationVector)
class CBC(object):
name = "CBC"
def __init__(self, initialization_vector):
if not isinstance(initialization_vector, bytes):
raise TypeError("initialization_vector must be bytes")
self._initialization_vector = initialization_vector
initialization_vector = utils.read_only_property("_initialization_vector")
validate_for_algorithm = _check_iv_length
validate_for_algorithm = _check_iv_and_key_length
@utils.register_interface(Mode)
@utils.register_interface(ModeWithTweak)
class XTS(object):
name = "XTS"
def __init__(self, tweak):
if not isinstance(tweak, bytes):
raise TypeError("tweak must be bytes")
if len(tweak) != 16:
raise ValueError("tweak must be 128-bits (16 bytes)")
self._tweak = tweak
tweak = utils.read_only_property("_tweak")
def validate_for_algorithm(self, algorithm):
if algorithm.key_size not in (256, 512):
raise ValueError(
"The XTS specification requires a 256-bit key for AES-128-XTS"
" and 512-bit key for AES-256-XTS"
)
@utils.register_interface(Mode)
class ECB(object):
name = "ECB"
def validate_for_algorithm(self, algorithm):
pass
validate_for_algorithm = _check_aes_key_length
@utils.register_interface(Mode)
@ -87,10 +134,13 @@ class OFB(object):
name = "OFB"
def __init__(self, initialization_vector):
if not isinstance(initialization_vector, bytes):
raise TypeError("initialization_vector must be bytes")
self._initialization_vector = initialization_vector
initialization_vector = utils.read_only_property("_initialization_vector")
validate_for_algorithm = _check_iv_length
validate_for_algorithm = _check_iv_and_key_length
@utils.register_interface(Mode)
@ -99,10 +149,13 @@ class CFB(object):
name = "CFB"
def __init__(self, initialization_vector):
if not isinstance(initialization_vector, bytes):
raise TypeError("initialization_vector must be bytes")
self._initialization_vector = initialization_vector
initialization_vector = utils.read_only_property("_initialization_vector")
validate_for_algorithm = _check_iv_length
validate_for_algorithm = _check_iv_and_key_length
@utils.register_interface(Mode)
@ -111,10 +164,13 @@ class CFB8(object):
name = "CFB8"
def __init__(self, initialization_vector):
if not isinstance(initialization_vector, bytes):
raise TypeError("initialization_vector must be bytes")
self._initialization_vector = initialization_vector
initialization_vector = utils.read_only_property("_initialization_vector")
validate_for_algorithm = _check_iv_length
validate_for_algorithm = _check_iv_and_key_length
@utils.register_interface(Mode)
@ -123,11 +179,15 @@ class CTR(object):
name = "CTR"
def __init__(self, nonce):
if not isinstance(nonce, bytes):
raise TypeError("nonce must be bytes")
self._nonce = nonce
nonce = utils.read_only_property("_nonce")
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
@ -146,19 +206,26 @@ class GCM(object):
# 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
if min_tag_length < 4:
raise ValueError("min_tag_length must be >= 4")
if tag is not None and len(tag) < min_tag_length:
raise ValueError(
"Authentication tag must be {0} bytes or longer.".format(
min_tag_length)
)
if not isinstance(initialization_vector, bytes):
raise TypeError("initialization_vector must be bytes")
if len(initialization_vector) == 0:
raise ValueError("initialization_vector must be at least 1 byte")
self._initialization_vector = initialization_vector
if tag is not None:
if not isinstance(tag, bytes):
raise TypeError("tag must be bytes or None")
if min_tag_length < 4:
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)
)
self._tag = tag
self._min_tag_length = min_tag_length
tag = utils.read_only_property("_tag")
initialization_vector = utils.read_only_property("_initialization_vector")
def validate_for_algorithm(self, algorithm):
pass
_check_aes_key_length(self, algorithm)

View file

@ -9,10 +9,10 @@ from cryptography.exceptions import (
AlreadyFinalized, UnsupportedAlgorithm, _Reasons
)
from cryptography.hazmat.backends.interfaces import CMACBackend
from cryptography.hazmat.primitives import ciphers, interfaces
from cryptography.hazmat.primitives import ciphers, mac
@utils.register_interface(interfaces.MACContext)
@utils.register_interface(mac.MACContext)
class CMAC(object):
def __init__(self, algorithm, backend, ctx=None):
if not isinstance(backend, CMACBackend):

View file

@ -5,7 +5,9 @@
from __future__ import absolute_import, division, print_function
import hmac
import warnings
from cryptography import utils
from cryptography.hazmat.bindings._constant_time import lib
@ -17,6 +19,13 @@ if hasattr(hmac, "compare_digest"):
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.")

View file

@ -29,12 +29,6 @@ class HashAlgorithm(object):
The size of the resulting digest in bytes.
"""
@abc.abstractproperty
def block_size(self):
"""
The internal block size of the hash algorithm in bytes.
"""
@six.add_metaclass(abc.ABCMeta)
class HashContext(object):
@ -142,22 +136,50 @@ class SHA512(object):
block_size = 128
@utils.register_interface(HashAlgorithm)
class RIPEMD160(object):
name = "ripemd160"
digest_size = 20
block_size = 64
@utils.register_interface(HashAlgorithm)
class Whirlpool(object):
name = "whirlpool"
digest_size = 64
block_size = 64
@utils.register_interface(HashAlgorithm)
class MD5(object):
name = "md5"
digest_size = 16
block_size = 64
@utils.register_interface(HashAlgorithm)
class BLAKE2b(object):
name = "blake2b"
_max_digest_size = 64
_min_digest_size = 1
block_size = 128
def __init__(self, digest_size):
if (
digest_size > self._max_digest_size or
digest_size < self._min_digest_size
):
raise ValueError("Digest size must be {0}-{1}".format(
self._min_digest_size, self._max_digest_size)
)
self._digest_size = digest_size
digest_size = utils.read_only_property("_digest_size")
@utils.register_interface(HashAlgorithm)
class BLAKE2s(object):
name = "blake2s"
block_size = 64
_max_digest_size = 32
_min_digest_size = 1
def __init__(self, digest_size):
if (
digest_size > self._max_digest_size or
digest_size < self._min_digest_size
):
raise ValueError("Digest size must be {0}-{1}".format(
self._min_digest_size, self._max_digest_size)
)
self._digest_size = digest_size
digest_size = utils.read_only_property("_digest_size")

View file

@ -9,10 +9,10 @@ from cryptography.exceptions import (
AlreadyFinalized, UnsupportedAlgorithm, _Reasons
)
from cryptography.hazmat.backends.interfaces import HMACBackend
from cryptography.hazmat.primitives import hashes, interfaces
from cryptography.hazmat.primitives import hashes, mac
@utils.register_interface(interfaces.MACContext)
@utils.register_interface(mac.MACContext)
@utils.register_interface(hashes.HashContext)
class HMAC(object):
def __init__(self, key, algorithm, backend, ctx=None):

View file

@ -30,7 +30,7 @@ class HKDF(object):
raise TypeError("salt must be bytes.")
if salt is None:
salt = b"\x00" * (self._algorithm.digest_size // 8)
salt = b"\x00" * self._algorithm.digest_size
self._salt = salt
@ -67,7 +67,7 @@ class HKDFExpand(object):
self._backend = backend
max_length = 255 * (algorithm.digest_size // 8)
max_length = 255 * algorithm.digest_size
if length > max_length:
raise ValueError(
@ -91,7 +91,7 @@ class HKDFExpand(object):
output = [b""]
counter = 1
while (self._algorithm.digest_size // 8) * len(output) < self._length:
while self._algorithm.digest_size * (len(output) - 1) < self._length:
h = hmac.HMAC(key_material, self._algorithm, backend=self._backend)
h.update(output[-1])
h.update(self._info)

View file

@ -0,0 +1,148 @@
# 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 six.moves import range
from cryptography import utils
from cryptography.exceptions import (
AlreadyFinalized, InvalidKey, UnsupportedAlgorithm, _Reasons
)
from cryptography.hazmat.backends.interfaces import HMACBackend
from cryptography.hazmat.primitives import constant_time, hashes, hmac
from cryptography.hazmat.primitives.kdf import KeyDerivationFunction
class Mode(Enum):
CounterMode = "ctr"
class CounterLocation(Enum):
BeforeFixed = "before_fixed"
AfterFixed = "after_fixed"
@utils.register_interface(KeyDerivationFunction)
class KBKDFHMAC(object):
def __init__(self, algorithm, mode, length, rlen, llen,
location, label, context, fixed, backend):
if not isinstance(backend, HMACBackend):
raise UnsupportedAlgorithm(
"Backend object does not implement HMACBackend.",
_Reasons.BACKEND_MISSING_INTERFACE
)
if not isinstance(algorithm, hashes.HashAlgorithm):
raise UnsupportedAlgorithm(
"Algorithm supplied is not a supported hash algorithm.",
_Reasons.UNSUPPORTED_HASH
)
if not backend.hmac_supported(algorithm):
raise UnsupportedAlgorithm(
"Algorithm supplied is not a supported hmac algorithm.",
_Reasons.UNSUPPORTED_HASH
)
if not isinstance(mode, Mode):
raise TypeError("mode must be of type Mode")
if not isinstance(location, CounterLocation):
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.")
if rlen is None or not self._valid_byte_length(rlen):
raise ValueError("rlen must be between 1 and 4")
if llen is None and fixed is None:
raise ValueError("Please specify an llen")
if llen is not None and not isinstance(llen, int):
raise TypeError("llen must be an integer")
if label is None:
label = b''
if context is None:
context = b''
if (not isinstance(label, bytes) or
not isinstance(context, bytes)):
raise TypeError('label and context must be of type bytes')
self._algorithm = algorithm
self._mode = mode
self._length = length
self._rlen = rlen
self._llen = llen
self._location = location
self._label = label
self._context = context
self._backend = backend
self._used = False
self._fixed_data = fixed
def _valid_byte_length(self, value):
if not isinstance(value, int):
raise TypeError('value must be of type int')
value_bin = utils.int_to_bytes(1, value)
if not 1 <= len(value_bin) <= 4:
return False
return True
def derive(self, key_material):
if self._used:
raise AlreadyFinalized
if not isinstance(key_material, bytes):
raise TypeError('key_material must be bytes')
self._used = True
# inverse floor division (equivalent to ceiling)
rounds = -(-self._length // self._algorithm.digest_size)
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
# This ensures that the counter values used as an input to the
# 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.')
for i in range(1, rounds + 1):
h = hmac.HMAC(key_material, self._algorithm, backend=self._backend)
counter = utils.int_to_bytes(i, self._rlen)
if self._location == CounterLocation.BeforeFixed:
h.update(counter)
h.update(self._generate_fixed_input())
if self._location == CounterLocation.AfterFixed:
h.update(counter)
output.append(h.finalize())
return b''.join(output)[:self._length]
def _generate_fixed_input(self):
if self._fixed_data and isinstance(self._fixed_data, bytes):
return self._fixed_data
l_val = utils.int_to_bytes(self._length * 8, self._llen)
return b"".join([self._label, b"\x00", self._context, l_val])
def verify(self, key_material, expected_key):
if not constant_time.bytes_eq(self.derive(key_material), expected_key):
raise InvalidKey

View file

@ -0,0 +1,66 @@
# 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 sys
from cryptography import utils
from cryptography.exceptions import (
AlreadyFinalized, InvalidKey, UnsupportedAlgorithm, _Reasons
)
from cryptography.hazmat.backends.interfaces import ScryptBackend
from cryptography.hazmat.primitives import constant_time
from cryptography.hazmat.primitives.kdf import KeyDerivationFunction
# This is used by the scrypt tests to skip tests that require more memory
# than the MEM_LIMIT
_MEM_LIMIT = sys.maxsize // 2
@utils.register_interface(KeyDerivationFunction)
class Scrypt(object):
def __init__(self, salt, length, n, r, p, backend):
if not isinstance(backend, ScryptBackend):
raise UnsupportedAlgorithm(
"Backend object does not implement ScryptBackend.",
_Reasons.BACKEND_MISSING_INTERFACE
)
self._length = length
if not isinstance(salt, bytes):
raise TypeError("salt must be bytes.")
if n < 2 or (n & (n - 1)) != 0:
raise ValueError("n must be greater than 1 and be a power of 2.")
if r < 1:
raise ValueError("r must be greater than or equal to 1.")
if p < 1:
raise ValueError("p must be greater than or equal to 1.")
self._used = False
self._salt = salt
self._n = n
self._r = r
self._p = p
self._backend = backend
def derive(self, key_material):
if self._used:
raise AlreadyFinalized("Scrypt instances can only be used once.")
self._used = True
if not isinstance(key_material, bytes):
raise TypeError("key_material must be bytes.")
return self._backend.derive_scrypt(
key_material, self._salt, self._length, self._n, self._r, self._p
)
def verify(self, key_material, expected_key):
derived_key = self.derive(key_material)
if not constant_time.bytes_eq(derived_key, expected_key):
raise InvalidKey("Keys do not match.")

View file

@ -12,20 +12,9 @@ from cryptography.hazmat.primitives.ciphers.modes import ECB
from cryptography.hazmat.primitives.constant_time import bytes_eq
def aes_key_wrap(wrapping_key, key_to_wrap, backend):
if len(wrapping_key) not in [16, 24, 32]:
raise ValueError("The wrapping key must be a valid AES key length")
if len(key_to_wrap) < 16:
raise ValueError("The key to wrap must be at least 16 bytes")
if len(key_to_wrap) % 8 != 0:
raise ValueError("The key to wrap must be a multiple of 8 bytes")
def _wrap_core(wrapping_key, a, r, backend):
# RFC 3394 Key Wrap - 2.2.1 (index method)
encryptor = Cipher(AES(wrapping_key), ECB(), backend).encryptor()
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)]
n = len(r)
for j in range(6):
for i in range(n):
@ -44,22 +33,24 @@ def aes_key_wrap(wrapping_key, key_to_wrap, backend):
return a + b"".join(r)
def aes_key_unwrap(wrapping_key, wrapped_key, backend):
if len(wrapped_key) < 24:
raise ValueError("Must be at least 24 bytes")
if len(wrapped_key) % 8 != 0:
raise ValueError("The wrapped key must be a multiple of 8 bytes")
def aes_key_wrap(wrapping_key, key_to_wrap, backend):
if len(wrapping_key) not in [16, 24, 32]:
raise ValueError("The wrapping key must be a valid AES key length")
if len(key_to_wrap) < 16:
raise ValueError("The key to wrap must be at least 16 bytes")
if len(key_to_wrap) % 8 != 0:
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)]
return _wrap_core(wrapping_key, a, r, backend)
def _unwrap_core(wrapping_key, a, r, backend):
# Implement RFC 3394 Key Unwrap - 2.2.2 (index method)
decryptor = Cipher(AES(wrapping_key), ECB(), backend).decryptor()
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)]
a = r.pop(0)
n = len(r)
for j in reversed(range(6)):
for i in reversed(range(n)):
@ -74,7 +65,85 @@ def aes_key_unwrap(wrapping_key, wrapped_key, backend):
r[i] = b[-8:]
assert decryptor.finalize() == b""
return a, r
def aes_key_wrap_with_padding(wrapping_key, key_to_wrap, backend):
if len(wrapping_key) not in [16, 24, 32]:
raise ValueError("The wrapping key must be a valid AES key length")
aiv = b"\xA6\x59\x59\xA6" + struct.pack(">i", len(key_to_wrap))
# pad the key to wrap if necessary
pad = (8 - (len(key_to_wrap) % 8)) % 8
key_to_wrap = key_to_wrap + b"\x00" * pad
if len(key_to_wrap) == 8:
# RFC 5649 - 4.1 - exactly 8 octets after padding
encryptor = Cipher(AES(wrapping_key), ECB(), backend).encryptor()
b = encryptor.update(aiv + key_to_wrap)
assert encryptor.finalize() == b""
return b
else:
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):
if len(wrapped_key) < 16:
raise InvalidUnwrap("Must be at least 16 bytes")
if len(wrapping_key) not in [16, 24, 32]:
raise ValueError("The wrapping key must be a valid AES key length")
if len(wrapped_key) == 16:
# RFC 5649 - 4.2 - exactly two 64-bit blocks
decryptor = Cipher(AES(wrapping_key), ECB(), backend).decryptor()
b = decryptor.update(wrapped_key)
assert decryptor.finalize() == b""
a = b[:8]
data = b[8:]
n = 1
else:
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)
data = b"".join(r)
# 1) Check that MSB(32,A) = A65959A6.
# 2) Check that 8*(n-1) < LSB(32,A) <= 8*n. If so, let
# MLI = LSB(32,A).
# 3) Let b = (8*n)-MLI, and then check that the rightmost b octets of
# the output data are zero.
(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)
)
):
raise InvalidUnwrap()
if b == 0:
return data
else:
return data[:-b]
def aes_key_unwrap(wrapping_key, wrapped_key, backend):
if len(wrapped_key) < 24:
raise InvalidUnwrap("Must be at least 24 bytes")
if len(wrapped_key) % 8 != 0:
raise InvalidUnwrap("The wrapped key must be a multiple of 8 bytes")
if len(wrapping_key) not in [16, 24, 32]:
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)]
a = r.pop(0)
a, r = _unwrap_core(wrapping_key, a, r, backend)
if not bytes_eq(a, aiv):
raise InvalidUnwrap()

View file

@ -28,14 +28,75 @@ class PaddingContext(object):
"""
def _byte_padding_check(block_size):
if not (0 <= block_size <= 2040):
raise ValueError("block_size must be in range(0, 2041).")
if block_size % 8 != 0:
raise ValueError("block_size must be a multiple of 8.")
def _byte_padding_update(buffer_, data, block_size):
if buffer_ is None:
raise AlreadyFinalized("Context was already finalized.")
if not isinstance(data, bytes):
raise TypeError("data must be bytes.")
buffer_ += data
finished_blocks = len(buffer_) // (block_size // 8)
result = buffer_[:finished_blocks * (block_size // 8)]
buffer_ = buffer_[finished_blocks * (block_size // 8):]
return buffer_, result
def _byte_padding_pad(buffer_, block_size, paddingfn):
if buffer_ is None:
raise AlreadyFinalized("Context was already finalized.")
pad_size = block_size // 8 - len(buffer_)
return buffer_ + paddingfn(pad_size)
def _byte_unpadding_update(buffer_, data, block_size):
if buffer_ is None:
raise AlreadyFinalized("Context was already finalized.")
if not isinstance(data, bytes):
raise TypeError("data must be bytes.")
buffer_ += 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):]
return buffer_, result
def _byte_unpadding_check(buffer_, block_size, checkfn):
if buffer_ is None:
raise AlreadyFinalized("Context was already finalized.")
if len(buffer_) != block_size // 8:
raise ValueError("Invalid padding bytes.")
valid = checkfn(buffer_, block_size // 8)
if not valid:
raise ValueError("Invalid padding bytes.")
pad_size = six.indexbytes(buffer_, -1)
return buffer_[:-pad_size]
class PKCS7(object):
def __init__(self, block_size):
if not (0 <= block_size < 256):
raise ValueError("block_size must be in range(0, 256).")
if block_size % 8 != 0:
raise ValueError("block_size must be a multiple of 8.")
_byte_padding_check(block_size)
self.block_size = block_size
def padder(self):
@ -53,27 +114,16 @@ class _PKCS7PaddingContext(object):
self._buffer = b""
def update(self, data):
if self._buffer is None:
raise AlreadyFinalized("Context was already finalized.")
if not isinstance(data, bytes):
raise TypeError("data must be bytes.")
self._buffer += data
finished_blocks = len(self._buffer) // (self.block_size // 8)
result = self._buffer[:finished_blocks * (self.block_size // 8)]
self._buffer = self._buffer[finished_blocks * (self.block_size // 8):]
self._buffer, result = _byte_padding_update(
self._buffer, data, self.block_size)
return result
def finalize(self):
if self._buffer is None:
raise AlreadyFinalized("Context was already finalized.")
def _padding(self, size):
return six.int2byte(size) * size
pad_size = self.block_size // 8 - len(self._buffer)
result = self._buffer + six.int2byte(pad_size) * pad_size
def finalize(self):
result = _byte_padding_pad(
self._buffer, self.block_size, self._padding)
self._buffer = None
return result
@ -86,39 +136,67 @@ class _PKCS7UnpaddingContext(object):
self._buffer = b""
def update(self, data):
if self._buffer is None:
raise AlreadyFinalized("Context was already finalized.")
if not isinstance(data, bytes):
raise TypeError("data must be bytes.")
self._buffer += data
finished_blocks = max(
len(self._buffer) // (self.block_size // 8) - 1,
0
)
result = self._buffer[:finished_blocks * (self.block_size // 8)]
self._buffer = self._buffer[finished_blocks * (self.block_size // 8):]
self._buffer, result = _byte_unpadding_update(
self._buffer, data, self.block_size)
return result
def finalize(self):
if self._buffer is None:
raise AlreadyFinalized("Context was already finalized.")
if len(self._buffer) != self.block_size // 8:
raise ValueError("Invalid padding bytes.")
valid = lib.Cryptography_check_pkcs7_padding(
self._buffer, self.block_size // 8
)
if not valid:
raise ValueError("Invalid padding bytes.")
pad_size = six.indexbytes(self._buffer, -1)
res = self._buffer[:-pad_size]
result = _byte_unpadding_check(
self._buffer, self.block_size,
lib.Cryptography_check_pkcs7_padding)
self._buffer = None
return res
return result
class ANSIX923(object):
def __init__(self, block_size):
_byte_padding_check(block_size)
self.block_size = block_size
def padder(self):
return _ANSIX923PaddingContext(self.block_size)
def unpadder(self):
return _ANSIX923UnpaddingContext(self.block_size)
@utils.register_interface(PaddingContext)
class _ANSIX923PaddingContext(object):
def __init__(self, block_size):
self.block_size = block_size
# TODO: more copies than necessary, we should use zero-buffer (#193)
self._buffer = b""
def update(self, data):
self._buffer, result = _byte_padding_update(
self._buffer, data, self.block_size)
return result
def _padding(self, size):
return six.int2byte(0) * (size - 1) + six.int2byte(size)
def finalize(self):
result = _byte_padding_pad(
self._buffer, self.block_size, self._padding)
self._buffer = None
return result
@utils.register_interface(PaddingContext)
class _ANSIX923UnpaddingContext(object):
def __init__(self, block_size):
self.block_size = block_size
# TODO: more copies than necessary, we should use zero-buffer (#193)
self._buffer = b""
def update(self, data):
self._buffer, result = _byte_unpadding_update(
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 = None
return result

View file

@ -24,6 +24,10 @@ def load_pem_public_key(data, backend):
return backend.load_pem_public_key(data)
def load_pem_parameters(data, backend):
return backend.load_pem_parameters(data)
def load_der_private_key(data, password, backend):
return backend.load_der_private_key(data, password)
@ -32,6 +36,10 @@ def load_der_public_key(data, backend):
return backend.load_der_public_key(data)
def load_der_parameters(data, backend):
return backend.load_der_parameters(data)
def load_ssh_public_key(data, backend):
key_parts = data.split(b' ', 2)
@ -59,7 +67,7 @@ def load_ssh_public_key(data, backend):
except TypeError:
raise ValueError('Key is not in the proper format.')
inner_key_type, rest = _read_next_string(decoded_data)
inner_key_type, rest = _ssh_read_next_string(decoded_data)
if inner_key_type != key_type:
raise ValueError(
@ -70,8 +78,8 @@ def load_ssh_public_key(data, backend):
def _load_ssh_rsa_public_key(key_type, decoded_data, backend):
e, rest = _read_next_mpint(decoded_data)
n, rest = _read_next_mpint(rest)
e, rest = _ssh_read_next_mpint(decoded_data)
n, rest = _ssh_read_next_mpint(rest)
if rest:
raise ValueError('Key body contains extra bytes.')
@ -80,10 +88,10 @@ def _load_ssh_rsa_public_key(key_type, decoded_data, backend):
def _load_ssh_dss_public_key(key_type, decoded_data, backend):
p, rest = _read_next_mpint(decoded_data)
q, rest = _read_next_mpint(rest)
g, rest = _read_next_mpint(rest)
y, rest = _read_next_mpint(rest)
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)
if rest:
raise ValueError('Key body contains extra bytes.')
@ -95,8 +103,8 @@ def _load_ssh_dss_public_key(key_type, decoded_data, backend):
def _load_ssh_ecdsa_public_key(expected_key_type, decoded_data, backend):
curve_name, rest = _read_next_string(decoded_data)
data, rest = _read_next_string(rest)
curve_name, rest = _ssh_read_next_string(decoded_data)
data, rest = _ssh_read_next_string(rest)
if expected_key_type != b"ecdsa-sha2-" + curve_name:
raise ValueError(
@ -117,46 +125,54 @@ def _load_ssh_ecdsa_public_key(expected_key_type, decoded_data, backend):
"Compressed elliptic curve points are not supported"
)
# key_size is in bits, and sometimes it's not evenly divisible by 8, so we
# add 7 to round up the number of bytes.
if len(data) != 1 + 2 * ((curve.key_size + 7) // 8):
raise ValueError("Malformed key bytes")
x = utils.int_from_bytes(
data[1:1 + (curve.key_size + 7) // 8], byteorder='big'
)
y = utils.int_from_bytes(
data[1 + (curve.key_size + 7) // 8:], byteorder='big'
)
return ec.EllipticCurvePublicNumbers(x, y, curve).public_key(backend)
numbers = ec.EllipticCurvePublicNumbers.from_encoded_point(curve, data)
return numbers.public_key(backend)
def _read_next_string(data):
def _ssh_read_next_string(data):
"""
Retrieves the next RFC 4251 string value from the data.
While the RFC calls these strings, in Python they are bytes objects.
"""
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")
return data[4:4 + str_len], data[4 + str_len:]
def _read_next_mpint(data):
def _ssh_read_next_mpint(data):
"""
Reads the next mpint from the data.
Currently, all mpints are interpreted as unsigned.
"""
mpint_data, rest = _read_next_string(data)
mpint_data, rest = _ssh_read_next_string(data)
return (
utils.int_from_bytes(mpint_data, byteorder='big', signed=False), rest
)
def _ssh_write_string(data):
return struct.pack(">I", len(data)) + data
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)
class Encoding(Enum):
PEM = "PEM"
DER = "DER"
OpenSSH = "OpenSSH"
class PrivateFormat(Enum):
@ -167,6 +183,11 @@ class PrivateFormat(Enum):
class PublicFormat(Enum):
SubjectPublicKeyInfo = "X.509 subjectPublicKeyInfo with PKCS#1"
PKCS1 = "Raw PKCS#1"
OpenSSH = "OpenSSH"
class ParameterFormat(Enum):
PKCS3 = "PKCS3"
@six.add_metaclass(abc.ABCMeta)

View file

@ -19,14 +19,15 @@ from cryptography.hazmat.primitives.twofactor.utils import _generate_uri
class HOTP(object):
def __init__(self, key, length, algorithm, backend):
def __init__(self, key, length, algorithm, backend,
enforce_key_length=True):
if not isinstance(backend, HMACBackend):
raise UnsupportedAlgorithm(
"Backend object does not implement HMACBackend.",
_Reasons.BACKEND_MISSING_INTERFACE
)
if len(key) < 16:
if len(key) < 16 and enforce_key_length is True:
raise ValueError("Key length has to be at least 128 bits.")
if not isinstance(length, six.integer_types):

View file

@ -15,7 +15,8 @@ from cryptography.hazmat.primitives.twofactor.utils import _generate_uri
class TOTP(object):
def __init__(self, key, length, algorithm, time_step, backend):
def __init__(self, key, length, algorithm, time_step, backend,
enforce_key_length=True):
if not isinstance(backend, HMACBackend):
raise UnsupportedAlgorithm(
"Backend object does not implement HMACBackend.",
@ -23,7 +24,7 @@ class TOTP(object):
)
self._time_step = time_step
self._hotp = HOTP(key, length, algorithm, backend)
self._hotp = HOTP(key, length, algorithm, backend, enforce_key_length)
def generate(self, time):
counter = int(time / self._time_step)