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

@ -14,10 +14,10 @@ __summary__ = ("cryptography is a package which provides cryptographic recipes"
" and primitives to Python developers.")
__uri__ = "https://github.com/pyca/cryptography"
__version__ = "1.2.2"
__version__ = "2.4.2"
__author__ = "The cryptography developers"
__email__ = "cryptography-dev@python.org"
__license__ = "BSD or Apache License, Version 2.0"
__copyright__ = "Copyright 2013-2016 {0}".format(__author__)
__copyright__ = "Copyright 2013-2017 {0}".format(__author__)

View file

@ -4,9 +4,6 @@
from __future__ import absolute_import, division, print_function
import sys
import warnings
from cryptography.__about__ import (
__author__, __copyright__, __email__, __license__, __summary__, __title__,
__uri__, __version__
@ -17,11 +14,3 @@ __all__ = [
"__title__", "__summary__", "__uri__", "__version__", "__author__",
"__email__", "__license__", "__copyright__",
]
if sys.version_info[:2] == (2, 6):
warnings.warn(
"Python 2.6 is no longer supported by the Python core team, please "
"upgrade your Python. A future version of cryptography will drop "
"support for Python 2.6",
DeprecationWarning
)

View file

@ -18,6 +18,7 @@ class _Reasons(Enum):
UNSUPPORTED_SERIALIZATION = 7
UNSUPPORTED_X509 = 8
UNSUPPORTED_EXCHANGE_ALGORITHM = 9
UNSUPPORTED_DIFFIE_HELLMAN = 10
class UnsupportedAlgorithm(Exception):

View file

@ -71,11 +71,20 @@ class Fernet(object):
return base64.urlsafe_b64encode(basic_parts + hmac)
def decrypt(self, token, ttl=None):
timestamp, data = Fernet._get_unverified_token_data(token)
return self._decrypt_data(data, timestamp, ttl)
def extract_timestamp(self, token):
timestamp, data = Fernet._get_unverified_token_data(token)
# Verify the token was not tampered with.
self._verify_signature(data)
return timestamp
@staticmethod
def _get_unverified_token_data(token):
if not isinstance(token, bytes):
raise TypeError("token must be bytes.")
current_time = int(time.time())
try:
data = base64.urlsafe_b64decode(token)
except (TypeError, binascii.Error):
@ -88,11 +97,9 @@ class Fernet(object):
timestamp, = struct.unpack(">Q", data[1:9])
except struct.error:
raise InvalidToken
if ttl is not None:
if timestamp + ttl < current_time:
raise InvalidToken
if current_time + _MAX_CLOCK_SKEW < timestamp:
raise InvalidToken
return timestamp, data
def _verify_signature(self, data):
h = HMAC(self._signing_key, hashes.SHA256(), backend=self._backend)
h.update(data[:-32])
try:
@ -100,6 +107,17 @@ class Fernet(object):
except InvalidSignature:
raise InvalidToken
def _decrypt_data(self, data, timestamp, ttl):
current_time = int(time.time())
if ttl is not None:
if timestamp + ttl < current_time:
raise InvalidToken
if current_time + _MAX_CLOCK_SKEW < timestamp:
raise InvalidToken
self._verify_signature(data)
iv = data[9:25]
ciphertext = data[25:-32]
decryptor = Cipher(
@ -132,6 +150,20 @@ class MultiFernet(object):
def encrypt(self, msg):
return self._fernets[0].encrypt(msg)
def rotate(self, msg):
timestamp, data = Fernet._get_unverified_token_data(msg)
for f in self._fernets:
try:
p = f._decrypt_data(data, timestamp, None)
break
except InvalidToken:
pass
else:
raise InvalidToken
iv = os.urandom(16)
return self._fernets[0]._encrypt_from_parts(p, timestamp, iv)
def decrypt(self, msg, ttl=None):
for f in self._fernets:
try:

View file

@ -1,5 +1,11 @@
# 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.
"""
Hazardous Materials
This is a "Hazardous Materials" module. You should ONLY use it if you're
100% absolutely sure that you know what you're doing because this module
is full of land mines, dragons, and dinosaurs with laser guns.
"""
from __future__ import absolute_import, division, print_function

View file

@ -0,0 +1,67 @@
# 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
class ObjectIdentifier(object):
def __init__(self, dotted_string):
self._dotted_string = dotted_string
nodes = self._dotted_string.split(".")
intnodes = []
# There must be at least 2 nodes, the first node must be 0..2, and
# if less than 2, the second node cannot have a value outside the
# range 0..39. All nodes must be integers.
for node in nodes:
try:
intnodes.append(int(node, 0))
except ValueError:
raise ValueError(
"Malformed OID: %s (non-integer nodes)" % (
self._dotted_string))
if len(nodes) < 2:
raise ValueError(
"Malformed OID: %s (insufficient number of nodes)" % (
self._dotted_string))
if intnodes[0] > 2:
raise ValueError(
"Malformed OID: %s (first node outside valid range)" % (
self._dotted_string))
if intnodes[0] < 2 and intnodes[1] >= 40:
raise ValueError(
"Malformed OID: %s (second node outside valid range)" % (
self._dotted_string))
def __eq__(self, other):
if not isinstance(other, ObjectIdentifier):
return NotImplemented
return self.dotted_string == other.dotted_string
def __ne__(self, other):
return not self == other
def __repr__(self):
return "<ObjectIdentifier(oid={0}, name={1})>".format(
self.dotted_string,
self._name
)
def __hash__(self):
return hash(self.dotted_string)
@property
def _name(self):
# Lazy import to avoid an import cycle
from cryptography.x509.oid import _OID_NAMES
return _OID_NAMES.get(self, "Unknown OID")
dotted_string = utils.read_only_property("_dotted_string")

View file

@ -4,31 +4,6 @@
from __future__ import absolute_import, division, print_function
import pkg_resources
from cryptography.hazmat.backends.multibackend import MultiBackend
_available_backends_list = None
def _available_backends():
global _available_backends_list
if _available_backends_list is None:
_available_backends_list = [
# setuptools 11.3 deprecated support for the require parameter to
# load(), and introduced the new resolve() method instead.
# This can be removed if/when we can assume setuptools>=11.3. At
# some point we may wish to add a warning, to push people along,
# but at present this would result in too many warnings.
ep.resolve() if hasattr(ep, "resolve") else ep.load(require=False)
for ep in pkg_resources.iter_entry_points(
"cryptography.backends"
)
]
return _available_backends_list
_default_backend = None
@ -37,6 +12,7 @@ def default_backend():
global _default_backend
if _default_backend is None:
_default_backend = MultiBackend(_available_backends())
from cryptography.hazmat.backends.openssl.backend import backend
_default_backend = backend
return _default_backend

View file

@ -1,10 +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
from cryptography.hazmat.backends.commoncrypto.backend import backend
__all__ = ["backend"]

View file

@ -1,245 +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
from collections import namedtuple
from cryptography import utils
from cryptography.exceptions import InternalError
from cryptography.hazmat.backends.commoncrypto.ciphers import (
_CipherContext, _GCMCipherContext
)
from cryptography.hazmat.backends.commoncrypto.hashes import _HashContext
from cryptography.hazmat.backends.commoncrypto.hmac import _HMACContext
from cryptography.hazmat.backends.interfaces import (
CipherBackend, HMACBackend, HashBackend, PBKDF2HMACBackend
)
from cryptography.hazmat.bindings.commoncrypto.binding import Binding
from cryptography.hazmat.primitives.ciphers.algorithms import (
AES, ARC4, Blowfish, CAST5, TripleDES
)
from cryptography.hazmat.primitives.ciphers.modes import (
CBC, CFB, CFB8, CTR, ECB, GCM, OFB
)
HashMethods = namedtuple(
"HashMethods", ["ctx", "hash_init", "hash_update", "hash_final"]
)
@utils.register_interface(CipherBackend)
@utils.register_interface(HashBackend)
@utils.register_interface(HMACBackend)
@utils.register_interface(PBKDF2HMACBackend)
class Backend(object):
"""
CommonCrypto API wrapper.
"""
name = "commoncrypto"
def __init__(self):
self._binding = Binding()
self._ffi = self._binding.ffi
self._lib = self._binding.lib
self._cipher_registry = {}
self._register_default_ciphers()
self._hash_mapping = {
"md5": HashMethods(
"CC_MD5_CTX *", self._lib.CC_MD5_Init,
self._lib.CC_MD5_Update, self._lib.CC_MD5_Final
),
"sha1": HashMethods(
"CC_SHA1_CTX *", self._lib.CC_SHA1_Init,
self._lib.CC_SHA1_Update, self._lib.CC_SHA1_Final
),
"sha224": HashMethods(
"CC_SHA256_CTX *", self._lib.CC_SHA224_Init,
self._lib.CC_SHA224_Update, self._lib.CC_SHA224_Final
),
"sha256": HashMethods(
"CC_SHA256_CTX *", self._lib.CC_SHA256_Init,
self._lib.CC_SHA256_Update, self._lib.CC_SHA256_Final
),
"sha384": HashMethods(
"CC_SHA512_CTX *", self._lib.CC_SHA384_Init,
self._lib.CC_SHA384_Update, self._lib.CC_SHA384_Final
),
"sha512": HashMethods(
"CC_SHA512_CTX *", self._lib.CC_SHA512_Init,
self._lib.CC_SHA512_Update, self._lib.CC_SHA512_Final
),
}
self._supported_hmac_algorithms = {
"md5": self._lib.kCCHmacAlgMD5,
"sha1": self._lib.kCCHmacAlgSHA1,
"sha224": self._lib.kCCHmacAlgSHA224,
"sha256": self._lib.kCCHmacAlgSHA256,
"sha384": self._lib.kCCHmacAlgSHA384,
"sha512": self._lib.kCCHmacAlgSHA512,
}
self._supported_pbkdf2_hmac_algorithms = {
"sha1": self._lib.kCCPRFHmacAlgSHA1,
"sha224": self._lib.kCCPRFHmacAlgSHA224,
"sha256": self._lib.kCCPRFHmacAlgSHA256,
"sha384": self._lib.kCCPRFHmacAlgSHA384,
"sha512": self._lib.kCCPRFHmacAlgSHA512,
}
def hash_supported(self, algorithm):
return algorithm.name in self._hash_mapping
def hmac_supported(self, algorithm):
return algorithm.name in self._supported_hmac_algorithms
def create_hash_ctx(self, algorithm):
return _HashContext(self, algorithm)
def create_hmac_ctx(self, key, algorithm):
return _HMACContext(self, key, algorithm)
def cipher_supported(self, cipher, mode):
return (type(cipher), type(mode)) in self._cipher_registry
def create_symmetric_encryption_ctx(self, cipher, mode):
if isinstance(mode, GCM):
return _GCMCipherContext(
self, cipher, mode, self._lib.kCCEncrypt
)
else:
return _CipherContext(self, cipher, mode, self._lib.kCCEncrypt)
def create_symmetric_decryption_ctx(self, cipher, mode):
if isinstance(mode, GCM):
return _GCMCipherContext(
self, cipher, mode, self._lib.kCCDecrypt
)
else:
return _CipherContext(self, cipher, mode, self._lib.kCCDecrypt)
def pbkdf2_hmac_supported(self, algorithm):
return algorithm.name in self._supported_pbkdf2_hmac_algorithms
def derive_pbkdf2_hmac(self, algorithm, length, salt, iterations,
key_material):
alg_enum = self._supported_pbkdf2_hmac_algorithms[algorithm.name]
buf = self._ffi.new("char[]", length)
res = self._lib.CCKeyDerivationPBKDF(
self._lib.kCCPBKDF2,
key_material,
len(key_material),
salt,
len(salt),
alg_enum,
iterations,
buf,
length
)
self._check_cipher_response(res)
return self._ffi.buffer(buf)[:]
def _register_cipher_adapter(self, cipher_cls, cipher_const, mode_cls,
mode_const):
if (cipher_cls, mode_cls) in self._cipher_registry:
raise ValueError("Duplicate registration for: {0} {1}.".format(
cipher_cls, mode_cls)
)
self._cipher_registry[cipher_cls, mode_cls] = (cipher_const,
mode_const)
def _register_default_ciphers(self):
for mode_cls, mode_const in [
(CBC, self._lib.kCCModeCBC),
(ECB, self._lib.kCCModeECB),
(CFB, self._lib.kCCModeCFB),
(CFB8, self._lib.kCCModeCFB8),
(OFB, self._lib.kCCModeOFB),
(CTR, self._lib.kCCModeCTR),
(GCM, self._lib.kCCModeGCM),
]:
self._register_cipher_adapter(
AES,
self._lib.kCCAlgorithmAES128,
mode_cls,
mode_const
)
for mode_cls, mode_const in [
(CBC, self._lib.kCCModeCBC),
(ECB, self._lib.kCCModeECB),
(CFB, self._lib.kCCModeCFB),
(CFB8, self._lib.kCCModeCFB8),
(OFB, self._lib.kCCModeOFB),
]:
self._register_cipher_adapter(
TripleDES,
self._lib.kCCAlgorithm3DES,
mode_cls,
mode_const
)
for mode_cls, mode_const in [
(CBC, self._lib.kCCModeCBC),
(ECB, self._lib.kCCModeECB),
(CFB, self._lib.kCCModeCFB),
(OFB, self._lib.kCCModeOFB)
]:
self._register_cipher_adapter(
Blowfish,
self._lib.kCCAlgorithmBlowfish,
mode_cls,
mode_const
)
for mode_cls, mode_const in [
(CBC, self._lib.kCCModeCBC),
(ECB, self._lib.kCCModeECB),
(CFB, self._lib.kCCModeCFB),
(OFB, self._lib.kCCModeOFB),
(CTR, self._lib.kCCModeCTR)
]:
self._register_cipher_adapter(
CAST5,
self._lib.kCCAlgorithmCAST,
mode_cls,
mode_const
)
self._register_cipher_adapter(
ARC4,
self._lib.kCCAlgorithmRC4,
type(None),
self._lib.kCCModeRC4
)
def _check_cipher_response(self, response):
if response == self._lib.kCCSuccess:
return
elif response == self._lib.kCCAlignmentError:
# This error is not currently triggered due to a bug filed as
# rdar://15589470
raise ValueError(
"The length of the provided data is not a multiple of "
"the block length."
)
else:
raise InternalError(
"The backend returned an unknown error, consider filing a bug."
" Code: {0}.".format(response),
response
)
def _release_cipher_ctx(self, ctx):
"""
Called by the garbage collector and used to safely dereference and
release the context.
"""
if ctx[0] != self._ffi.NULL:
res = self._lib.CCCryptorRelease(ctx[0])
self._check_cipher_response(res)
ctx[0] = self._ffi.NULL
backend = Backend()

View file

@ -1,193 +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
from cryptography import utils
from cryptography.exceptions import (
InvalidTag, UnsupportedAlgorithm, _Reasons
)
from cryptography.hazmat.primitives import ciphers, constant_time
from cryptography.hazmat.primitives.ciphers import modes
from cryptography.hazmat.primitives.ciphers.modes import (
CFB, CFB8, CTR, OFB
)
@utils.register_interface(ciphers.CipherContext)
class _CipherContext(object):
def __init__(self, backend, cipher, mode, operation):
self._backend = backend
self._cipher = cipher
self._mode = mode
self._operation = operation
# There is a bug in CommonCrypto where block ciphers do not raise
# kCCAlignmentError when finalizing if you supply non-block aligned
# data. To work around this we need to keep track of the block
# alignment ourselves, but only for alg+mode combos that require
# block alignment. OFB, CFB, and CTR make a block cipher algorithm
# into a stream cipher so we don't need to track them (and thus their
# block size is effectively 1 byte just like OpenSSL/CommonCrypto
# treat RC4 and other stream cipher block sizes).
# This bug has been filed as rdar://15589470
self._bytes_processed = 0
if (isinstance(cipher, ciphers.BlockCipherAlgorithm) and not
isinstance(mode, (OFB, CFB, CFB8, CTR))):
self._byte_block_size = cipher.block_size // 8
else:
self._byte_block_size = 1
registry = self._backend._cipher_registry
try:
cipher_enum, mode_enum = registry[type(cipher), type(mode)]
except KeyError:
raise UnsupportedAlgorithm(
"cipher {0} in {1} mode is not supported "
"by this backend.".format(
cipher.name, mode.name if mode else mode),
_Reasons.UNSUPPORTED_CIPHER
)
ctx = self._backend._ffi.new("CCCryptorRef *")
ctx = self._backend._ffi.gc(ctx, self._backend._release_cipher_ctx)
if isinstance(mode, modes.ModeWithInitializationVector):
iv_nonce = mode.initialization_vector
elif isinstance(mode, modes.ModeWithNonce):
iv_nonce = mode.nonce
else:
iv_nonce = self._backend._ffi.NULL
if isinstance(mode, CTR):
mode_option = self._backend._lib.kCCModeOptionCTR_BE
else:
mode_option = 0
res = self._backend._lib.CCCryptorCreateWithMode(
operation,
mode_enum, cipher_enum,
self._backend._lib.ccNoPadding, iv_nonce,
cipher.key, len(cipher.key),
self._backend._ffi.NULL, 0, 0, mode_option, ctx)
self._backend._check_cipher_response(res)
self._ctx = ctx
def update(self, data):
# Count bytes processed to handle block alignment.
self._bytes_processed += len(data)
buf = self._backend._ffi.new(
"unsigned char[]", len(data) + self._byte_block_size - 1)
outlen = self._backend._ffi.new("size_t *")
res = self._backend._lib.CCCryptorUpdate(
self._ctx[0], data, len(data), buf,
len(data) + self._byte_block_size - 1, outlen)
self._backend._check_cipher_response(res)
return self._backend._ffi.buffer(buf)[:outlen[0]]
def finalize(self):
# Raise error if block alignment is wrong.
if self._bytes_processed % self._byte_block_size:
raise ValueError(
"The length of the provided data is not a multiple of "
"the block length."
)
buf = self._backend._ffi.new("unsigned char[]", self._byte_block_size)
outlen = self._backend._ffi.new("size_t *")
res = self._backend._lib.CCCryptorFinal(
self._ctx[0], buf, len(buf), outlen)
self._backend._check_cipher_response(res)
self._backend._release_cipher_ctx(self._ctx)
return self._backend._ffi.buffer(buf)[:outlen[0]]
@utils.register_interface(ciphers.AEADCipherContext)
@utils.register_interface(ciphers.AEADEncryptionContext)
class _GCMCipherContext(object):
def __init__(self, backend, cipher, mode, operation):
self._backend = backend
self._cipher = cipher
self._mode = mode
self._operation = operation
self._tag = None
registry = self._backend._cipher_registry
try:
cipher_enum, mode_enum = registry[type(cipher), type(mode)]
except KeyError:
raise UnsupportedAlgorithm(
"cipher {0} in {1} mode is not supported "
"by this backend.".format(
cipher.name, mode.name if mode else mode),
_Reasons.UNSUPPORTED_CIPHER
)
ctx = self._backend._ffi.new("CCCryptorRef *")
ctx = self._backend._ffi.gc(ctx, self._backend._release_cipher_ctx)
self._ctx = ctx
res = self._backend._lib.CCCryptorCreateWithMode(
operation,
mode_enum, cipher_enum,
self._backend._lib.ccNoPadding,
self._backend._ffi.NULL,
cipher.key, len(cipher.key),
self._backend._ffi.NULL, 0, 0, 0, self._ctx)
self._backend._check_cipher_response(res)
res = self._backend._lib.CCCryptorGCMAddIV(
self._ctx[0],
mode.initialization_vector,
len(mode.initialization_vector)
)
self._backend._check_cipher_response(res)
# CommonCrypto has a bug where calling update without at least one
# call to authenticate_additional_data will result in null byte output
# for ciphertext. The following empty byte string call prevents the
# issue, which is present in at least 10.8 and 10.9.
# Filed as rdar://18314544
self.authenticate_additional_data(b"")
def update(self, data):
buf = self._backend._ffi.new("unsigned char[]", len(data))
args = (self._ctx[0], data, len(data), buf)
if self._operation == self._backend._lib.kCCEncrypt:
res = self._backend._lib.CCCryptorGCMEncrypt(*args)
else:
res = self._backend._lib.CCCryptorGCMDecrypt(*args)
self._backend._check_cipher_response(res)
return self._backend._ffi.buffer(buf)[:]
def finalize(self):
# CommonCrypto has a yet another bug where you must make at least one
# call to update. If you pass just AAD and call finalize without a call
# to update you'll get null bytes for tag. The following update call
# prevents this issue, which is present in at least 10.8 and 10.9.
# Filed as rdar://18314580
self.update(b"")
tag_size = self._cipher.block_size // 8
tag_buf = self._backend._ffi.new("unsigned char[]", tag_size)
tag_len = self._backend._ffi.new("size_t *", tag_size)
res = self._backend._lib.CCCryptorGCMFinal(
self._ctx[0], tag_buf, tag_len
)
self._backend._check_cipher_response(res)
self._backend._release_cipher_ctx(self._ctx)
self._tag = self._backend._ffi.buffer(tag_buf)[:]
if (self._operation == self._backend._lib.kCCDecrypt and
not constant_time.bytes_eq(
self._tag[:len(self._mode.tag)], self._mode.tag
)):
raise InvalidTag
return b""
def authenticate_additional_data(self, data):
res = self._backend._lib.CCCryptorGCMAddAAD(
self._ctx[0], data, len(data)
)
self._backend._check_cipher_response(res)
tag = utils.read_only_property("_tag")

View file

@ -1,55 +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
from cryptography import utils
from cryptography.exceptions import UnsupportedAlgorithm, _Reasons
from cryptography.hazmat.primitives import hashes
@utils.register_interface(hashes.HashContext)
class _HashContext(object):
def __init__(self, backend, algorithm, ctx=None):
self._algorithm = algorithm
self._backend = backend
if ctx is None:
try:
methods = self._backend._hash_mapping[self.algorithm.name]
except KeyError:
raise UnsupportedAlgorithm(
"{0} is not a supported hash on this backend.".format(
algorithm.name),
_Reasons.UNSUPPORTED_HASH
)
ctx = self._backend._ffi.new(methods.ctx)
res = methods.hash_init(ctx)
assert res == 1
self._ctx = ctx
algorithm = utils.read_only_property("_algorithm")
def copy(self):
methods = self._backend._hash_mapping[self.algorithm.name]
new_ctx = self._backend._ffi.new(methods.ctx)
# CommonCrypto has no APIs for copying hashes, so we have to copy the
# underlying struct.
new_ctx[0] = self._ctx[0]
return _HashContext(self._backend, self.algorithm, ctx=new_ctx)
def update(self, data):
methods = self._backend._hash_mapping[self.algorithm.name]
res = methods.hash_update(self._ctx, data, len(data))
assert res == 1
def finalize(self):
methods = self._backend._hash_mapping[self.algorithm.name]
buf = self._backend._ffi.new("unsigned char[]",
self.algorithm.digest_size)
res = methods.hash_final(buf, self._ctx)
assert res == 1
return self._backend._ffi.buffer(buf)[:]

View file

@ -1,59 +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
from cryptography import utils
from cryptography.exceptions import (
InvalidSignature, UnsupportedAlgorithm, _Reasons
)
from cryptography.hazmat.primitives import constant_time, hashes, interfaces
@utils.register_interface(interfaces.MACContext)
@utils.register_interface(hashes.HashContext)
class _HMACContext(object):
def __init__(self, backend, key, algorithm, ctx=None):
self._algorithm = algorithm
self._backend = backend
if ctx is None:
ctx = self._backend._ffi.new("CCHmacContext *")
try:
alg = self._backend._supported_hmac_algorithms[algorithm.name]
except KeyError:
raise UnsupportedAlgorithm(
"{0} is not a supported HMAC hash on this backend.".format(
algorithm.name),
_Reasons.UNSUPPORTED_HASH
)
self._backend._lib.CCHmacInit(ctx, alg, key, len(key))
self._ctx = ctx
self._key = key
algorithm = utils.read_only_property("_algorithm")
def copy(self):
copied_ctx = self._backend._ffi.new("CCHmacContext *")
# CommonCrypto has no APIs for copying HMACs, so we have to copy the
# underlying struct.
copied_ctx[0] = self._ctx[0]
return _HMACContext(
self._backend, self._key, self.algorithm, ctx=copied_ctx
)
def update(self, data):
self._backend._lib.CCHmacUpdate(self._ctx, data, len(data))
def finalize(self):
buf = self._backend._ffi.new("unsigned char[]",
self.algorithm.digest_size)
self._backend._lib.CCHmacFinal(self._ctx, buf)
return self._backend._ffi.buffer(buf)[:]
def verify(self, signature):
digest = self.finalize()
if not constant_time.bytes_eq(digest, signature):
raise InvalidSignature("Signature did not match digest.")

View file

@ -221,6 +221,12 @@ class EllipticCurveBackend(object):
Returns whether the exchange algorithm is supported by this backend.
"""
@abc.abstractmethod
def derive_elliptic_curve_private_key(self, private_value, curve):
"""
Compute the private key given the private value and curve.
"""
@six.add_metaclass(abc.ABCMeta)
class PEMSerializationBackend(object):
@ -237,6 +243,12 @@ class PEMSerializationBackend(object):
Loads a public key from PEM encoded data.
"""
@abc.abstractmethod
def load_pem_parameters(self, data):
"""
Load encryption parameters from PEM encoded data.
"""
@six.add_metaclass(abc.ABCMeta)
class DERSerializationBackend(object):
@ -253,6 +265,12 @@ class DERSerializationBackend(object):
Loads a public key from DER encoded data.
"""
@abc.abstractmethod
def load_der_parameters(self, data):
"""
Load encryption parameters from DER encoded data.
"""
@six.add_metaclass(abc.ABCMeta)
class X509Backend(object):
@ -306,13 +324,20 @@ class X509Backend(object):
object.
"""
@abc.abstractmethod
def x509_name_bytes(self, name):
"""
Compute the DER encoded bytes of an X509 Name object.
"""
@six.add_metaclass(abc.ABCMeta)
class DHBackend(object):
@abc.abstractmethod
def generate_dh_parameters(self, key_size):
def generate_dh_parameters(self, generator, key_size):
"""
Generate a DHParameters instance with a modulus of key_size bits.
Using the given generator. Often 2 or 5.
"""
@abc.abstractmethod
@ -323,37 +348,48 @@ class DHBackend(object):
"""
@abc.abstractmethod
def generate_dh_private_key_and_parameters(self, key_size):
def generate_dh_private_key_and_parameters(self, generator, key_size):
"""
Generate a DHPrivateKey instance using key size only.
Using the given generator. Often 2 or 5.
"""
@abc.abstractmethod
def load_dh_private_numbers(self, numbers):
"""
Returns a DHPrivateKey provider.
Load a DHPrivateKey from DHPrivateNumbers
"""
@abc.abstractmethod
def load_dh_public_numbers(self, numbers):
"""
Returns a DHPublicKey provider.
Load a DHPublicKey from DHPublicNumbers.
"""
@abc.abstractmethod
def load_dh_parameter_numbers(self, numbers):
"""
Returns a DHParameters provider.
Load DHParameters from DHParameterNumbers.
"""
@abc.abstractmethod
def dh_exchange_algorithm_supported(self, exchange_algorithm):
"""
Returns whether the exchange algorithm is supported by this backend.
"""
@abc.abstractmethod
def dh_parameters_supported(self, p, g):
def dh_parameters_supported(self, p, g, q=None):
"""
Returns whether the backend supports DH with these parameter values.
"""
@abc.abstractmethod
def dh_x942_serialization_supported(self):
"""
Returns True if the backend supports the serialization of DH objects
with subgroup order (q).
"""
@six.add_metaclass(abc.ABCMeta)
class ScryptBackend(object):
@abc.abstractmethod
def derive_scrypt(self, key_material, salt, length, n, r, p):
"""
Return bytes derived from provided Scrypt parameters.
"""

View file

@ -1,404 +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
from cryptography import utils
from cryptography.exceptions import UnsupportedAlgorithm, _Reasons
from cryptography.hazmat.backends.interfaces import (
CMACBackend, CipherBackend, DERSerializationBackend, DSABackend,
EllipticCurveBackend, HMACBackend, HashBackend, PBKDF2HMACBackend,
PEMSerializationBackend, RSABackend, X509Backend
)
@utils.register_interface(CMACBackend)
@utils.register_interface(CipherBackend)
@utils.register_interface(DERSerializationBackend)
@utils.register_interface(HashBackend)
@utils.register_interface(HMACBackend)
@utils.register_interface(PBKDF2HMACBackend)
@utils.register_interface(RSABackend)
@utils.register_interface(DSABackend)
@utils.register_interface(EllipticCurveBackend)
@utils.register_interface(PEMSerializationBackend)
@utils.register_interface(X509Backend)
class MultiBackend(object):
name = "multibackend"
def __init__(self, backends):
self._backends = backends
def _filtered_backends(self, interface):
for b in self._backends:
if isinstance(b, interface):
yield b
def cipher_supported(self, cipher, mode):
return any(
b.cipher_supported(cipher, mode)
for b in self._filtered_backends(CipherBackend)
)
def create_symmetric_encryption_ctx(self, cipher, mode):
for b in self._filtered_backends(CipherBackend):
try:
return b.create_symmetric_encryption_ctx(cipher, mode)
except UnsupportedAlgorithm:
pass
raise UnsupportedAlgorithm(
"cipher {0} in {1} mode is not supported by this backend.".format(
cipher.name, mode.name if mode else mode),
_Reasons.UNSUPPORTED_CIPHER
)
def create_symmetric_decryption_ctx(self, cipher, mode):
for b in self._filtered_backends(CipherBackend):
try:
return b.create_symmetric_decryption_ctx(cipher, mode)
except UnsupportedAlgorithm:
pass
raise UnsupportedAlgorithm(
"cipher {0} in {1} mode is not supported by this backend.".format(
cipher.name, mode.name if mode else mode),
_Reasons.UNSUPPORTED_CIPHER
)
def hash_supported(self, algorithm):
return any(
b.hash_supported(algorithm)
for b in self._filtered_backends(HashBackend)
)
def create_hash_ctx(self, algorithm):
for b in self._filtered_backends(HashBackend):
try:
return b.create_hash_ctx(algorithm)
except UnsupportedAlgorithm:
pass
raise UnsupportedAlgorithm(
"{0} is not a supported hash on this backend.".format(
algorithm.name),
_Reasons.UNSUPPORTED_HASH
)
def hmac_supported(self, algorithm):
return any(
b.hmac_supported(algorithm)
for b in self._filtered_backends(HMACBackend)
)
def create_hmac_ctx(self, key, algorithm):
for b in self._filtered_backends(HMACBackend):
try:
return b.create_hmac_ctx(key, algorithm)
except UnsupportedAlgorithm:
pass
raise UnsupportedAlgorithm(
"{0} is not a supported hash on this backend.".format(
algorithm.name),
_Reasons.UNSUPPORTED_HASH
)
def pbkdf2_hmac_supported(self, algorithm):
return any(
b.pbkdf2_hmac_supported(algorithm)
for b in self._filtered_backends(PBKDF2HMACBackend)
)
def derive_pbkdf2_hmac(self, algorithm, length, salt, iterations,
key_material):
for b in self._filtered_backends(PBKDF2HMACBackend):
try:
return b.derive_pbkdf2_hmac(
algorithm, length, salt, iterations, key_material
)
except UnsupportedAlgorithm:
pass
raise UnsupportedAlgorithm(
"{0} is not a supported hash on this backend.".format(
algorithm.name),
_Reasons.UNSUPPORTED_HASH
)
def generate_rsa_private_key(self, public_exponent, key_size):
for b in self._filtered_backends(RSABackend):
return b.generate_rsa_private_key(public_exponent, key_size)
raise UnsupportedAlgorithm("RSA is not supported by the backend.",
_Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
def generate_rsa_parameters_supported(self, public_exponent, key_size):
for b in self._filtered_backends(RSABackend):
return b.generate_rsa_parameters_supported(
public_exponent, key_size
)
raise UnsupportedAlgorithm("RSA is not supported by the backend.",
_Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
def rsa_padding_supported(self, padding):
for b in self._filtered_backends(RSABackend):
return b.rsa_padding_supported(padding)
raise UnsupportedAlgorithm("RSA is not supported by the backend.",
_Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
def load_rsa_private_numbers(self, numbers):
for b in self._filtered_backends(RSABackend):
return b.load_rsa_private_numbers(numbers)
raise UnsupportedAlgorithm("RSA is not supported by the backend",
_Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
def load_rsa_public_numbers(self, numbers):
for b in self._filtered_backends(RSABackend):
return b.load_rsa_public_numbers(numbers)
raise UnsupportedAlgorithm("RSA is not supported by the backend",
_Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
def generate_dsa_parameters(self, key_size):
for b in self._filtered_backends(DSABackend):
return b.generate_dsa_parameters(key_size)
raise UnsupportedAlgorithm("DSA is not supported by the backend.",
_Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
def generate_dsa_private_key(self, parameters):
for b in self._filtered_backends(DSABackend):
return b.generate_dsa_private_key(parameters)
raise UnsupportedAlgorithm("DSA is not supported by the backend.",
_Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
def generate_dsa_private_key_and_parameters(self, key_size):
for b in self._filtered_backends(DSABackend):
return b.generate_dsa_private_key_and_parameters(key_size)
raise UnsupportedAlgorithm("DSA is not supported by the backend.",
_Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
def dsa_hash_supported(self, algorithm):
for b in self._filtered_backends(DSABackend):
return b.dsa_hash_supported(algorithm)
raise UnsupportedAlgorithm("DSA is not supported by the backend.",
_Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
def dsa_parameters_supported(self, p, q, g):
for b in self._filtered_backends(DSABackend):
return b.dsa_parameters_supported(p, q, g)
raise UnsupportedAlgorithm("DSA is not supported by the backend.",
_Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
def load_dsa_public_numbers(self, numbers):
for b in self._filtered_backends(DSABackend):
return b.load_dsa_public_numbers(numbers)
raise UnsupportedAlgorithm("DSA is not supported by the backend.",
_Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
def load_dsa_private_numbers(self, numbers):
for b in self._filtered_backends(DSABackend):
return b.load_dsa_private_numbers(numbers)
raise UnsupportedAlgorithm("DSA is not supported by the backend.",
_Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
def load_dsa_parameter_numbers(self, numbers):
for b in self._filtered_backends(DSABackend):
return b.load_dsa_parameter_numbers(numbers)
raise UnsupportedAlgorithm("DSA is not supported by the backend.",
_Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
def cmac_algorithm_supported(self, algorithm):
return any(
b.cmac_algorithm_supported(algorithm)
for b in self._filtered_backends(CMACBackend)
)
def create_cmac_ctx(self, algorithm):
for b in self._filtered_backends(CMACBackend):
try:
return b.create_cmac_ctx(algorithm)
except UnsupportedAlgorithm:
pass
raise UnsupportedAlgorithm("This backend does not support CMAC.",
_Reasons.UNSUPPORTED_CIPHER)
def elliptic_curve_supported(self, curve):
return any(
b.elliptic_curve_supported(curve)
for b in self._filtered_backends(EllipticCurveBackend)
)
def elliptic_curve_signature_algorithm_supported(
self, signature_algorithm, curve
):
return any(
b.elliptic_curve_signature_algorithm_supported(
signature_algorithm, curve
)
for b in self._filtered_backends(EllipticCurveBackend)
)
def generate_elliptic_curve_private_key(self, curve):
for b in self._filtered_backends(EllipticCurveBackend):
try:
return b.generate_elliptic_curve_private_key(curve)
except UnsupportedAlgorithm:
continue
raise UnsupportedAlgorithm(
"This backend does not support this elliptic curve.",
_Reasons.UNSUPPORTED_ELLIPTIC_CURVE
)
def load_elliptic_curve_private_numbers(self, numbers):
for b in self._filtered_backends(EllipticCurveBackend):
try:
return b.load_elliptic_curve_private_numbers(numbers)
except UnsupportedAlgorithm:
continue
raise UnsupportedAlgorithm(
"This backend does not support this elliptic curve.",
_Reasons.UNSUPPORTED_ELLIPTIC_CURVE
)
def load_elliptic_curve_public_numbers(self, numbers):
for b in self._filtered_backends(EllipticCurveBackend):
try:
return b.load_elliptic_curve_public_numbers(numbers)
except UnsupportedAlgorithm:
continue
raise UnsupportedAlgorithm(
"This backend does not support this elliptic curve.",
_Reasons.UNSUPPORTED_ELLIPTIC_CURVE
)
def elliptic_curve_exchange_algorithm_supported(self, algorithm, curve):
return any(
b.elliptic_curve_exchange_algorithm_supported(algorithm, curve)
for b in self._filtered_backends(EllipticCurveBackend)
)
def load_pem_private_key(self, data, password):
for b in self._filtered_backends(PEMSerializationBackend):
return b.load_pem_private_key(data, password)
raise UnsupportedAlgorithm(
"This backend does not support this key serialization.",
_Reasons.UNSUPPORTED_SERIALIZATION
)
def load_pem_public_key(self, data):
for b in self._filtered_backends(PEMSerializationBackend):
return b.load_pem_public_key(data)
raise UnsupportedAlgorithm(
"This backend does not support this key serialization.",
_Reasons.UNSUPPORTED_SERIALIZATION
)
def load_der_private_key(self, data, password):
for b in self._filtered_backends(DERSerializationBackend):
return b.load_der_private_key(data, password)
raise UnsupportedAlgorithm(
"This backend does not support this key serialization.",
_Reasons.UNSUPPORTED_SERIALIZATION
)
def load_der_public_key(self, data):
for b in self._filtered_backends(DERSerializationBackend):
return b.load_der_public_key(data)
raise UnsupportedAlgorithm(
"This backend does not support this key serialization.",
_Reasons.UNSUPPORTED_SERIALIZATION
)
def load_pem_x509_certificate(self, data):
for b in self._filtered_backends(X509Backend):
return b.load_pem_x509_certificate(data)
raise UnsupportedAlgorithm(
"This backend does not support X.509.",
_Reasons.UNSUPPORTED_X509
)
def load_der_x509_certificate(self, data):
for b in self._filtered_backends(X509Backend):
return b.load_der_x509_certificate(data)
raise UnsupportedAlgorithm(
"This backend does not support X.509.",
_Reasons.UNSUPPORTED_X509
)
def load_pem_x509_crl(self, data):
for b in self._filtered_backends(X509Backend):
return b.load_pem_x509_crl(data)
raise UnsupportedAlgorithm(
"This backend does not support X.509.",
_Reasons.UNSUPPORTED_X509
)
def load_der_x509_crl(self, data):
for b in self._filtered_backends(X509Backend):
return b.load_der_x509_crl(data)
raise UnsupportedAlgorithm(
"This backend does not support X.509.",
_Reasons.UNSUPPORTED_X509
)
def load_der_x509_csr(self, data):
for b in self._filtered_backends(X509Backend):
return b.load_der_x509_csr(data)
raise UnsupportedAlgorithm(
"This backend does not support X.509.",
_Reasons.UNSUPPORTED_X509
)
def load_pem_x509_csr(self, data):
for b in self._filtered_backends(X509Backend):
return b.load_pem_x509_csr(data)
raise UnsupportedAlgorithm(
"This backend does not support X.509.",
_Reasons.UNSUPPORTED_X509
)
def create_x509_csr(self, builder, private_key, algorithm):
for b in self._filtered_backends(X509Backend):
return b.create_x509_csr(builder, private_key, algorithm)
raise UnsupportedAlgorithm(
"This backend does not support X.509.",
_Reasons.UNSUPPORTED_X509
)
def create_x509_certificate(self, builder, private_key, algorithm):
for b in self._filtered_backends(X509Backend):
return b.create_x509_certificate(builder, private_key, algorithm)
raise UnsupportedAlgorithm(
"This backend does not support X.509.",
_Reasons.UNSUPPORTED_X509
)
def create_x509_crl(self, builder, private_key, algorithm):
for b in self._filtered_backends(X509Backend):
return b.create_x509_crl(builder, private_key, algorithm)
raise UnsupportedAlgorithm(
"This backend does not support X.509.",
_Reasons.UNSUPPORTED_X509
)
def create_x509_revoked_certificate(self, builder):
for b in self._filtered_backends(X509Backend):
return b.create_x509_revoked_certificate(builder)
raise UnsupportedAlgorithm(
"This backend does not support X.509.",
_Reasons.UNSUPPORTED_X509
)

View file

@ -0,0 +1,159 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
from __future__ import absolute_import, division, print_function
from cryptography.exceptions import InvalidTag
_ENCRYPT = 1
_DECRYPT = 0
def _aead_cipher_name(cipher):
from cryptography.hazmat.primitives.ciphers.aead import (
AESCCM, AESGCM, ChaCha20Poly1305
)
if isinstance(cipher, ChaCha20Poly1305):
return b"chacha20-poly1305"
elif isinstance(cipher, AESCCM):
return "aes-{0}-ccm".format(len(cipher._key) * 8).encode("ascii")
else:
assert isinstance(cipher, AESGCM)
return "aes-{0}-gcm".format(len(cipher._key) * 8).encode("ascii")
def _aead_setup(backend, cipher_name, key, nonce, tag, tag_len, operation):
evp_cipher = backend._lib.EVP_get_cipherbyname(cipher_name)
backend.openssl_assert(evp_cipher != backend._ffi.NULL)
ctx = backend._lib.EVP_CIPHER_CTX_new()
ctx = backend._ffi.gc(ctx, backend._lib.EVP_CIPHER_CTX_free)
res = backend._lib.EVP_CipherInit_ex(
ctx, evp_cipher,
backend._ffi.NULL,
backend._ffi.NULL,
backend._ffi.NULL,
int(operation == _ENCRYPT)
)
backend.openssl_assert(res != 0)
res = backend._lib.EVP_CIPHER_CTX_set_key_length(ctx, len(key))
backend.openssl_assert(res != 0)
res = backend._lib.EVP_CIPHER_CTX_ctrl(
ctx, backend._lib.EVP_CTRL_AEAD_SET_IVLEN, len(nonce),
backend._ffi.NULL
)
backend.openssl_assert(res != 0)
if operation == _DECRYPT:
res = backend._lib.EVP_CIPHER_CTX_ctrl(
ctx, backend._lib.EVP_CTRL_AEAD_SET_TAG, len(tag), tag
)
backend.openssl_assert(res != 0)
else:
res = backend._lib.EVP_CIPHER_CTX_ctrl(
ctx, backend._lib.EVP_CTRL_AEAD_SET_TAG, tag_len, backend._ffi.NULL
)
res = backend._lib.EVP_CipherInit_ex(
ctx,
backend._ffi.NULL,
backend._ffi.NULL,
key,
nonce,
int(operation == _ENCRYPT)
)
backend.openssl_assert(res != 0)
return ctx
def _set_length(backend, ctx, data_len):
intptr = backend._ffi.new("int *")
res = backend._lib.EVP_CipherUpdate(
ctx,
backend._ffi.NULL,
intptr,
backend._ffi.NULL,
data_len
)
backend.openssl_assert(res != 0)
def _process_aad(backend, ctx, associated_data):
outlen = backend._ffi.new("int *")
res = backend._lib.EVP_CipherUpdate(
ctx, backend._ffi.NULL, outlen, associated_data, len(associated_data)
)
backend.openssl_assert(res != 0)
def _process_data(backend, ctx, data):
outlen = backend._ffi.new("int *")
buf = backend._ffi.new("unsigned char[]", len(data))
res = backend._lib.EVP_CipherUpdate(ctx, buf, outlen, data, len(data))
backend.openssl_assert(res != 0)
return backend._ffi.buffer(buf, outlen[0])[:]
def _encrypt(backend, cipher, nonce, data, associated_data, tag_length):
from cryptography.hazmat.primitives.ciphers.aead import AESCCM
cipher_name = _aead_cipher_name(cipher)
ctx = _aead_setup(
backend, cipher_name, cipher._key, nonce, None, tag_length, _ENCRYPT
)
# CCM requires us to pass the length of the data before processing anything
# However calling this with any other AEAD results in an error
if isinstance(cipher, AESCCM):
_set_length(backend, ctx, len(data))
_process_aad(backend, ctx, associated_data)
processed_data = _process_data(backend, ctx, data)
outlen = backend._ffi.new("int *")
res = backend._lib.EVP_CipherFinal_ex(ctx, backend._ffi.NULL, outlen)
backend.openssl_assert(res != 0)
backend.openssl_assert(outlen[0] == 0)
tag_buf = backend._ffi.new("unsigned char[]", tag_length)
res = backend._lib.EVP_CIPHER_CTX_ctrl(
ctx, backend._lib.EVP_CTRL_AEAD_GET_TAG, tag_length, tag_buf
)
backend.openssl_assert(res != 0)
tag = backend._ffi.buffer(tag_buf)[:]
return processed_data + tag
def _decrypt(backend, cipher, nonce, data, associated_data, tag_length):
from cryptography.hazmat.primitives.ciphers.aead import AESCCM
if len(data) < tag_length:
raise InvalidTag
tag = data[-tag_length:]
data = data[:-tag_length]
cipher_name = _aead_cipher_name(cipher)
ctx = _aead_setup(
backend, cipher_name, cipher._key, nonce, tag, tag_length, _DECRYPT
)
# CCM requires us to pass the length of the data before processing anything
# However calling this with any other AEAD results in an error
if isinstance(cipher, AESCCM):
_set_length(backend, ctx, len(data))
_process_aad(backend, ctx, associated_data)
# CCM has a different error path if the tag doesn't match. Errors are
# raised in Update and Final is irrelevant.
if isinstance(cipher, AESCCM):
outlen = backend._ffi.new("int *")
buf = backend._ffi.new("unsigned char[]", len(data))
res = backend._lib.EVP_CipherUpdate(ctx, buf, outlen, data, len(data))
if res != 1:
backend._consume_errors()
raise InvalidTag
processed_data = backend._ffi.buffer(buf, outlen[0])[:]
else:
processed_data = _process_data(backend, ctx, data)
outlen = backend._ffi.new("int *")
res = backend._lib.EVP_CipherFinal_ex(ctx, backend._ffi.NULL, outlen)
if res == 0:
backend._consume_errors()
raise InvalidTag
return processed_data

View file

@ -13,6 +13,7 @@ from cryptography.hazmat.primitives.ciphers import modes
@utils.register_interface(ciphers.CipherContext)
@utils.register_interface(ciphers.AEADCipherContext)
@utils.register_interface(ciphers.AEADEncryptionContext)
@utils.register_interface(ciphers.AEADDecryptionContext)
class _CipherContext(object):
_ENCRYPT = 1
_DECRYPT = 0
@ -25,9 +26,9 @@ class _CipherContext(object):
self._tag = None
if isinstance(self._cipher, ciphers.BlockCipherAlgorithm):
self._block_size = self._cipher.block_size
self._block_size_bytes = self._cipher.block_size // 8
else:
self._block_size = 1
self._block_size_bytes = 1
ctx = self._backend._lib.EVP_CIPHER_CTX_new()
ctx = self._backend._ffi.gc(
@ -56,8 +57,12 @@ class _CipherContext(object):
if isinstance(mode, modes.ModeWithInitializationVector):
iv_nonce = mode.initialization_vector
elif isinstance(mode, modes.ModeWithTweak):
iv_nonce = mode.tweak
elif isinstance(mode, modes.ModeWithNonce):
iv_nonce = mode.nonce
elif isinstance(cipher, modes.ModeWithNonce):
iv_nonce = cipher.nonce
else:
iv_nonce = self._backend._ffi.NULL
# begin init with cipher and operation type
@ -74,16 +79,26 @@ class _CipherContext(object):
self._backend.openssl_assert(res != 0)
if isinstance(mode, modes.GCM):
res = self._backend._lib.EVP_CIPHER_CTX_ctrl(
ctx, self._backend._lib.EVP_CTRL_GCM_SET_IVLEN,
ctx, self._backend._lib.EVP_CTRL_AEAD_SET_IVLEN,
len(iv_nonce), self._backend._ffi.NULL
)
self._backend.openssl_assert(res != 0)
if operation == self._DECRYPT:
if mode.tag is not None:
res = self._backend._lib.EVP_CIPHER_CTX_ctrl(
ctx, self._backend._lib.EVP_CTRL_GCM_SET_TAG,
ctx, self._backend._lib.EVP_CTRL_AEAD_SET_TAG,
len(mode.tag), mode.tag
)
self._backend.openssl_assert(res != 0)
self._tag = mode.tag
elif (
self._operation == self._DECRYPT and
self._backend._lib.CRYPTOGRAPHY_OPENSSL_LESS_THAN_102 and
not self._backend._lib.CRYPTOGRAPHY_IS_LIBRESSL
):
raise NotImplementedError(
"delayed passing of GCM tag requires OpenSSL >= 1.0.2."
" To use this feature please update OpenSSL"
)
# pass key/iv
res = self._backend._lib.EVP_CipherInit_ex(
@ -101,22 +116,25 @@ class _CipherContext(object):
self._ctx = ctx
def update(self, data):
# OpenSSL 0.9.8e has an assertion in its EVP code that causes it
# to SIGABRT if you call update with an empty byte string. This can be
# removed when we drop support for 0.9.8e (CentOS/RHEL 5). This branch
# should be taken only when length is zero and mode is not GCM because
# AES GCM can return improper tag values if you don't call update
# with empty plaintext when authenticating AAD for ...reasons.
if len(data) == 0 and not isinstance(self._mode, modes.GCM):
return b""
buf = bytearray(len(data) + self._block_size_bytes - 1)
n = self.update_into(data, buf)
return bytes(buf[:n])
buf = self._backend._ffi.new("unsigned char[]",
len(data) + self._block_size - 1)
def update_into(self, data, buf):
if len(buf) < (len(data) + self._block_size_bytes - 1):
raise ValueError(
"buffer must be at least {0} bytes for this "
"payload".format(len(data) + self._block_size_bytes - 1)
)
buf = self._backend._ffi.cast(
"unsigned char *", self._backend._ffi.from_buffer(buf)
)
outlen = self._backend._ffi.new("int *")
res = self._backend._lib.EVP_CipherUpdate(self._ctx, buf, outlen, data,
len(data))
res = self._backend._lib.EVP_CipherUpdate(self._ctx, buf, outlen,
data, len(data))
self._backend.openssl_assert(res != 0)
return self._backend._ffi.buffer(buf)[:outlen[0]]
return outlen[0]
def finalize(self):
# OpenSSL 1.0.1 on Ubuntu 12.04 (and possibly other distributions)
@ -127,7 +145,16 @@ class _CipherContext(object):
if isinstance(self._mode, modes.GCM):
self.update(b"")
buf = self._backend._ffi.new("unsigned char[]", self._block_size)
if (
self._operation == self._DECRYPT and
isinstance(self._mode, modes.ModeWithAuthenticationTag) and
self.tag is None
):
raise ValueError(
"Authentication tag must be provided when decrypting."
)
buf = self._backend._ffi.new("unsigned char[]", self._block_size_bytes)
outlen = self._backend._ffi.new("int *")
res = self._backend._lib.EVP_CipherFinal_ex(self._ctx, buf, outlen)
if res == 0:
@ -137,13 +164,8 @@ class _CipherContext(object):
raise InvalidTag
self._backend.openssl_assert(
errors[0][1:] == (
errors[0]._lib_reason_match(
self._backend._lib.ERR_LIB_EVP,
self._backend._lib.EVP_F_EVP_ENCRYPTFINAL_EX,
self._backend._lib.EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH
) or errors[0][1:] == (
self._backend._lib.ERR_LIB_EVP,
self._backend._lib.EVP_F_EVP_DECRYPTFINAL_EX,
self._backend._lib.EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH
)
)
@ -154,13 +176,12 @@ class _CipherContext(object):
if (isinstance(self._mode, modes.GCM) and
self._operation == self._ENCRYPT):
block_byte_size = self._block_size // 8
tag_buf = self._backend._ffi.new(
"unsigned char[]", block_byte_size
"unsigned char[]", self._block_size_bytes
)
res = self._backend._lib.EVP_CIPHER_CTX_ctrl(
self._ctx, self._backend._lib.EVP_CTRL_GCM_GET_TAG,
block_byte_size, tag_buf
self._ctx, self._backend._lib.EVP_CTRL_AEAD_GET_TAG,
self._block_size_bytes, tag_buf
)
self._backend.openssl_assert(res != 0)
self._tag = self._backend._ffi.buffer(tag_buf)[:]
@ -169,6 +190,28 @@ class _CipherContext(object):
self._backend.openssl_assert(res == 1)
return self._backend._ffi.buffer(buf)[:outlen[0]]
def finalize_with_tag(self, tag):
if (
self._backend._lib.CRYPTOGRAPHY_OPENSSL_LESS_THAN_102 and
not self._backend._lib.CRYPTOGRAPHY_IS_LIBRESSL
):
raise NotImplementedError(
"finalize_with_tag requires OpenSSL >= 1.0.2. To use this "
"method please update OpenSSL"
)
if len(tag) < self._mode._min_tag_length:
raise ValueError(
"Authentication tag must be {0} bytes or longer.".format(
self._mode._min_tag_length)
)
res = self._backend._lib.EVP_CIPHER_CTX_ctrl(
self._ctx, self._backend._lib.EVP_CTRL_AEAD_SET_TAG,
len(tag), tag
)
self._backend.openssl_assert(res != 0)
self._tag = tag
return self.finalize()
def authenticate_additional_data(self, data):
outlen = self._backend._ffi.new("int *")
res = self._backend._lib.EVP_CipherUpdate(
@ -177,37 +220,3 @@ class _CipherContext(object):
self._backend.openssl_assert(res != 0)
tag = utils.read_only_property("_tag")
@utils.register_interface(ciphers.CipherContext)
class _AESCTRCipherContext(object):
"""
This is needed to provide support for AES CTR mode in OpenSSL 0.9.8. It can
be removed when we drop 0.9.8 support (RHEL5 extended life ends 2020).
"""
def __init__(self, backend, cipher, mode):
self._backend = backend
self._key = self._backend._ffi.new("AES_KEY *")
res = self._backend._lib.AES_set_encrypt_key(
cipher.key, len(cipher.key) * 8, self._key
)
self._backend.openssl_assert(res == 0)
self._ecount = self._backend._ffi.new("char[]", 16)
self._nonce = self._backend._ffi.new("char[16]", mode.nonce)
self._num = self._backend._ffi.new("unsigned int *", 0)
def update(self, data):
buf = self._backend._ffi.new("unsigned char[]", len(data))
self._backend._lib.AES_ctr128_encrypt(
data, buf, len(data), self._key, self._nonce,
self._ecount, self._num
)
return self._backend._ffi.buffer(buf)[:]
def finalize(self):
self._key = None
self._ecount = None
self._nonce = None
self._num = None
return b""

View file

@ -9,11 +9,11 @@ from cryptography import utils
from cryptography.exceptions import (
InvalidSignature, UnsupportedAlgorithm, _Reasons
)
from cryptography.hazmat.primitives import constant_time, interfaces
from cryptography.hazmat.primitives import constant_time, mac
from cryptography.hazmat.primitives.ciphers.modes import CBC
@utils.register_interface(interfaces.MACContext)
@utils.register_interface(mac.MACContext)
class _CMACContext(object):
def __init__(self, backend, algorithm, ctx=None):
if not backend.cmac_algorithm_supported(algorithm):
@ -36,10 +36,11 @@ class _CMACContext(object):
self._backend.openssl_assert(ctx != self._backend._ffi.NULL)
ctx = self._backend._ffi.gc(ctx, self._backend._lib.CMAC_CTX_free)
self._backend._lib.CMAC_Init(
res = self._backend._lib.CMAC_Init(
ctx, self._key, len(self._key),
evp_cipher, self._backend._ffi.NULL
)
self._backend.openssl_assert(res == 1)
self._ctx = ctx

View file

@ -0,0 +1,867 @@
# 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 datetime
import ipaddress
import asn1crypto.core
import six
from cryptography import x509
from cryptography.x509.extensions import _TLS_FEATURE_TYPE_TO_ENUM
from cryptography.x509.name import _ASN1_TYPE_TO_ENUM
from cryptography.x509.oid import (
CRLEntryExtensionOID, CertificatePoliciesOID, ExtensionOID,
OCSPExtensionOID,
)
class _Integers(asn1crypto.core.SequenceOf):
_child_spec = asn1crypto.core.Integer
def _obj2txt(backend, obj):
# Set to 80 on the recommendation of
# https://www.openssl.org/docs/crypto/OBJ_nid2ln.html#return_values
#
# But OIDs longer than this occur in real life (e.g. Active
# Directory makes some very long OIDs). So we need to detect
# and properly handle the case where the default buffer is not
# big enough.
#
buf_len = 80
buf = backend._ffi.new("char[]", buf_len)
# 'res' is the number of bytes that *would* be written if the
# buffer is large enough. If 'res' > buf_len - 1, we need to
# alloc a big-enough buffer and go again.
res = backend._lib.OBJ_obj2txt(buf, buf_len, obj, 1)
if res > buf_len - 1: # account for terminating null byte
buf_len = res + 1
buf = backend._ffi.new("char[]", buf_len)
res = backend._lib.OBJ_obj2txt(buf, buf_len, obj, 1)
backend.openssl_assert(res > 0)
return backend._ffi.buffer(buf, res)[:].decode()
def _decode_x509_name_entry(backend, x509_name_entry):
obj = backend._lib.X509_NAME_ENTRY_get_object(x509_name_entry)
backend.openssl_assert(obj != backend._ffi.NULL)
data = backend._lib.X509_NAME_ENTRY_get_data(x509_name_entry)
backend.openssl_assert(data != backend._ffi.NULL)
value = _asn1_string_to_utf8(backend, data)
oid = _obj2txt(backend, obj)
type = _ASN1_TYPE_TO_ENUM[data.type]
return x509.NameAttribute(x509.ObjectIdentifier(oid), value, type)
def _decode_x509_name(backend, x509_name):
count = backend._lib.X509_NAME_entry_count(x509_name)
attributes = []
prev_set_id = -1
for x in range(count):
entry = backend._lib.X509_NAME_get_entry(x509_name, x)
attribute = _decode_x509_name_entry(backend, entry)
set_id = backend._lib.Cryptography_X509_NAME_ENTRY_set(entry)
if set_id != prev_set_id:
attributes.append(set([attribute]))
else:
# is in the same RDN a previous entry
attributes[-1].add(attribute)
prev_set_id = set_id
return x509.Name(x509.RelativeDistinguishedName(rdn) for rdn in attributes)
def _decode_general_names(backend, gns):
num = backend._lib.sk_GENERAL_NAME_num(gns)
names = []
for i in range(num):
gn = backend._lib.sk_GENERAL_NAME_value(gns, i)
backend.openssl_assert(gn != backend._ffi.NULL)
names.append(_decode_general_name(backend, gn))
return names
def _decode_general_name(backend, gn):
if gn.type == backend._lib.GEN_DNS:
# Convert to bytes and then decode to utf8. We don't use
# asn1_string_to_utf8 here because it doesn't properly convert
# utf8 from ia5strings.
data = _asn1_string_to_bytes(backend, gn.d.dNSName).decode("utf8")
# We don't use the constructor for DNSName so we can bypass validation
# This allows us to create DNSName objects that have unicode chars
# when a certificate (against the RFC) contains them.
return x509.DNSName._init_without_validation(data)
elif gn.type == backend._lib.GEN_URI:
# Convert to bytes and then decode to utf8. We don't use
# asn1_string_to_utf8 here because it doesn't properly convert
# utf8 from ia5strings.
data = _asn1_string_to_bytes(
backend, gn.d.uniformResourceIdentifier
).decode("utf8")
# We don't use the constructor for URI so we can bypass validation
# This allows us to create URI objects that have unicode chars
# when a certificate (against the RFC) contains them.
return x509.UniformResourceIdentifier._init_without_validation(data)
elif gn.type == backend._lib.GEN_RID:
oid = _obj2txt(backend, gn.d.registeredID)
return x509.RegisteredID(x509.ObjectIdentifier(oid))
elif gn.type == backend._lib.GEN_IPADD:
data = _asn1_string_to_bytes(backend, gn.d.iPAddress)
data_len = len(data)
if data_len == 8 or data_len == 32:
# This is an IPv4 or IPv6 Network and not a single IP. This
# type of data appears in Name Constraints. Unfortunately,
# ipaddress doesn't support packed bytes + netmask. Additionally,
# IPv6Network can only handle CIDR rather than the full 16 byte
# netmask. To handle this we convert the netmask to integer, then
# find the first 0 bit, which will be the prefix. If another 1
# bit is present after that the netmask is invalid.
base = ipaddress.ip_address(data[:data_len // 2])
netmask = ipaddress.ip_address(data[data_len // 2:])
bits = bin(int(netmask))[2:]
prefix = bits.find('0')
# If no 0 bits are found it is a /32 or /128
if prefix == -1:
prefix = len(bits)
if "1" in bits[prefix:]:
raise ValueError("Invalid netmask")
ip = ipaddress.ip_network(base.exploded + u"/{0}".format(prefix))
else:
ip = ipaddress.ip_address(data)
return x509.IPAddress(ip)
elif gn.type == backend._lib.GEN_DIRNAME:
return x509.DirectoryName(
_decode_x509_name(backend, gn.d.directoryName)
)
elif gn.type == backend._lib.GEN_EMAIL:
# Convert to bytes and then decode to utf8. We don't use
# asn1_string_to_utf8 here because it doesn't properly convert
# utf8 from ia5strings.
data = _asn1_string_to_bytes(backend, gn.d.rfc822Name).decode("utf8")
# We don't use the constructor for RFC822Name so we can bypass
# validation. This allows us to create RFC822Name objects that have
# unicode chars when a certificate (against the RFC) contains them.
return x509.RFC822Name._init_without_validation(data)
elif gn.type == backend._lib.GEN_OTHERNAME:
type_id = _obj2txt(backend, gn.d.otherName.type_id)
value = _asn1_to_der(backend, gn.d.otherName.value)
return x509.OtherName(x509.ObjectIdentifier(type_id), value)
else:
# x400Address or ediPartyName
raise x509.UnsupportedGeneralNameType(
"{0} is not a supported type".format(
x509._GENERAL_NAMES.get(gn.type, gn.type)
),
gn.type
)
def _decode_ocsp_no_check(backend, ext):
return x509.OCSPNoCheck()
def _decode_crl_number(backend, ext):
asn1_int = backend._ffi.cast("ASN1_INTEGER *", ext)
asn1_int = backend._ffi.gc(asn1_int, backend._lib.ASN1_INTEGER_free)
return x509.CRLNumber(_asn1_integer_to_int(backend, asn1_int))
def _decode_delta_crl_indicator(backend, ext):
asn1_int = backend._ffi.cast("ASN1_INTEGER *", ext)
asn1_int = backend._ffi.gc(asn1_int, backend._lib.ASN1_INTEGER_free)
return x509.DeltaCRLIndicator(_asn1_integer_to_int(backend, asn1_int))
class _X509ExtensionParser(object):
def __init__(self, ext_count, get_ext, handlers):
self.ext_count = ext_count
self.get_ext = get_ext
self.handlers = handlers
def parse(self, backend, x509_obj):
extensions = []
seen_oids = set()
for i in range(self.ext_count(backend, x509_obj)):
ext = self.get_ext(backend, x509_obj, i)
backend.openssl_assert(ext != backend._ffi.NULL)
crit = backend._lib.X509_EXTENSION_get_critical(ext)
critical = crit == 1
oid = x509.ObjectIdentifier(
_obj2txt(backend, backend._lib.X509_EXTENSION_get_object(ext))
)
if oid in seen_oids:
raise x509.DuplicateExtension(
"Duplicate {0} extension found".format(oid), oid
)
# These OIDs are only supported in OpenSSL 1.1.0+ but we want
# to support them in all versions of OpenSSL so we decode them
# ourselves.
if oid == ExtensionOID.TLS_FEATURE:
data = backend._lib.X509_EXTENSION_get_data(ext)
parsed = _Integers.load(_asn1_string_to_bytes(backend, data))
value = x509.TLSFeature(
[_TLS_FEATURE_TYPE_TO_ENUM[x.native] for x in parsed]
)
extensions.append(x509.Extension(oid, critical, value))
seen_oids.add(oid)
continue
elif oid == ExtensionOID.PRECERT_POISON:
data = backend._lib.X509_EXTENSION_get_data(ext)
parsed = asn1crypto.core.Null.load(
_asn1_string_to_bytes(backend, data)
)
assert parsed == asn1crypto.core.Null()
extensions.append(x509.Extension(
oid, critical, x509.PrecertPoison()
))
seen_oids.add(oid)
continue
try:
handler = self.handlers[oid]
except KeyError:
# Dump the DER payload into an UnrecognizedExtension object
data = backend._lib.X509_EXTENSION_get_data(ext)
backend.openssl_assert(data != backend._ffi.NULL)
der = backend._ffi.buffer(data.data, data.length)[:]
unrecognized = x509.UnrecognizedExtension(oid, der)
extensions.append(
x509.Extension(oid, critical, unrecognized)
)
else:
ext_data = backend._lib.X509V3_EXT_d2i(ext)
if ext_data == backend._ffi.NULL:
backend._consume_errors()
raise ValueError(
"The {0} extension is invalid and can't be "
"parsed".format(oid)
)
value = handler(backend, ext_data)
extensions.append(x509.Extension(oid, critical, value))
seen_oids.add(oid)
return x509.Extensions(extensions)
def _decode_certificate_policies(backend, cp):
cp = backend._ffi.cast("Cryptography_STACK_OF_POLICYINFO *", cp)
cp = backend._ffi.gc(cp, backend._lib.CERTIFICATEPOLICIES_free)
num = backend._lib.sk_POLICYINFO_num(cp)
certificate_policies = []
for i in range(num):
qualifiers = None
pi = backend._lib.sk_POLICYINFO_value(cp, i)
oid = x509.ObjectIdentifier(_obj2txt(backend, pi.policyid))
if pi.qualifiers != backend._ffi.NULL:
qnum = backend._lib.sk_POLICYQUALINFO_num(pi.qualifiers)
qualifiers = []
for j in range(qnum):
pqi = backend._lib.sk_POLICYQUALINFO_value(
pi.qualifiers, j
)
pqualid = x509.ObjectIdentifier(
_obj2txt(backend, pqi.pqualid)
)
if pqualid == CertificatePoliciesOID.CPS_QUALIFIER:
cpsuri = backend._ffi.buffer(
pqi.d.cpsuri.data, pqi.d.cpsuri.length
)[:].decode('ascii')
qualifiers.append(cpsuri)
else:
assert pqualid == CertificatePoliciesOID.CPS_USER_NOTICE
user_notice = _decode_user_notice(
backend, pqi.d.usernotice
)
qualifiers.append(user_notice)
certificate_policies.append(
x509.PolicyInformation(oid, qualifiers)
)
return x509.CertificatePolicies(certificate_policies)
def _decode_user_notice(backend, un):
explicit_text = None
notice_reference = None
if un.exptext != backend._ffi.NULL:
explicit_text = _asn1_string_to_utf8(backend, un.exptext)
if un.noticeref != backend._ffi.NULL:
organization = _asn1_string_to_utf8(
backend, un.noticeref.organization
)
num = backend._lib.sk_ASN1_INTEGER_num(
un.noticeref.noticenos
)
notice_numbers = []
for i in range(num):
asn1_int = backend._lib.sk_ASN1_INTEGER_value(
un.noticeref.noticenos, i
)
notice_num = _asn1_integer_to_int(backend, asn1_int)
notice_numbers.append(notice_num)
notice_reference = x509.NoticeReference(
organization, notice_numbers
)
return x509.UserNotice(notice_reference, explicit_text)
def _decode_basic_constraints(backend, bc_st):
basic_constraints = backend._ffi.cast("BASIC_CONSTRAINTS *", bc_st)
basic_constraints = backend._ffi.gc(
basic_constraints, backend._lib.BASIC_CONSTRAINTS_free
)
# The byte representation of an ASN.1 boolean true is \xff. OpenSSL
# chooses to just map this to its ordinal value, so true is 255 and
# false is 0.
ca = basic_constraints.ca == 255
path_length = _asn1_integer_to_int_or_none(
backend, basic_constraints.pathlen
)
return x509.BasicConstraints(ca, path_length)
def _decode_subject_key_identifier(backend, asn1_string):
asn1_string = backend._ffi.cast("ASN1_OCTET_STRING *", asn1_string)
asn1_string = backend._ffi.gc(
asn1_string, backend._lib.ASN1_OCTET_STRING_free
)
return x509.SubjectKeyIdentifier(
backend._ffi.buffer(asn1_string.data, asn1_string.length)[:]
)
def _decode_authority_key_identifier(backend, akid):
akid = backend._ffi.cast("AUTHORITY_KEYID *", akid)
akid = backend._ffi.gc(akid, backend._lib.AUTHORITY_KEYID_free)
key_identifier = None
authority_cert_issuer = None
if akid.keyid != backend._ffi.NULL:
key_identifier = backend._ffi.buffer(
akid.keyid.data, akid.keyid.length
)[:]
if akid.issuer != backend._ffi.NULL:
authority_cert_issuer = _decode_general_names(
backend, akid.issuer
)
authority_cert_serial_number = _asn1_integer_to_int_or_none(
backend, akid.serial
)
return x509.AuthorityKeyIdentifier(
key_identifier, authority_cert_issuer, authority_cert_serial_number
)
def _decode_authority_information_access(backend, aia):
aia = backend._ffi.cast("Cryptography_STACK_OF_ACCESS_DESCRIPTION *", aia)
aia = backend._ffi.gc(aia, backend._lib.sk_ACCESS_DESCRIPTION_free)
num = backend._lib.sk_ACCESS_DESCRIPTION_num(aia)
access_descriptions = []
for i in range(num):
ad = backend._lib.sk_ACCESS_DESCRIPTION_value(aia, i)
backend.openssl_assert(ad.method != backend._ffi.NULL)
oid = x509.ObjectIdentifier(_obj2txt(backend, ad.method))
backend.openssl_assert(ad.location != backend._ffi.NULL)
gn = _decode_general_name(backend, ad.location)
access_descriptions.append(x509.AccessDescription(oid, gn))
return x509.AuthorityInformationAccess(access_descriptions)
def _decode_key_usage(backend, bit_string):
bit_string = backend._ffi.cast("ASN1_BIT_STRING *", bit_string)
bit_string = backend._ffi.gc(bit_string, backend._lib.ASN1_BIT_STRING_free)
get_bit = backend._lib.ASN1_BIT_STRING_get_bit
digital_signature = get_bit(bit_string, 0) == 1
content_commitment = get_bit(bit_string, 1) == 1
key_encipherment = get_bit(bit_string, 2) == 1
data_encipherment = get_bit(bit_string, 3) == 1
key_agreement = get_bit(bit_string, 4) == 1
key_cert_sign = get_bit(bit_string, 5) == 1
crl_sign = get_bit(bit_string, 6) == 1
encipher_only = get_bit(bit_string, 7) == 1
decipher_only = get_bit(bit_string, 8) == 1
return x509.KeyUsage(
digital_signature,
content_commitment,
key_encipherment,
data_encipherment,
key_agreement,
key_cert_sign,
crl_sign,
encipher_only,
decipher_only
)
def _decode_general_names_extension(backend, gns):
gns = backend._ffi.cast("GENERAL_NAMES *", gns)
gns = backend._ffi.gc(gns, backend._lib.GENERAL_NAMES_free)
general_names = _decode_general_names(backend, gns)
return general_names
def _decode_subject_alt_name(backend, ext):
return x509.SubjectAlternativeName(
_decode_general_names_extension(backend, ext)
)
def _decode_issuer_alt_name(backend, ext):
return x509.IssuerAlternativeName(
_decode_general_names_extension(backend, ext)
)
def _decode_name_constraints(backend, nc):
nc = backend._ffi.cast("NAME_CONSTRAINTS *", nc)
nc = backend._ffi.gc(nc, backend._lib.NAME_CONSTRAINTS_free)
permitted = _decode_general_subtrees(backend, nc.permittedSubtrees)
excluded = _decode_general_subtrees(backend, nc.excludedSubtrees)
return x509.NameConstraints(
permitted_subtrees=permitted, excluded_subtrees=excluded
)
def _decode_general_subtrees(backend, stack_subtrees):
if stack_subtrees == backend._ffi.NULL:
return None
num = backend._lib.sk_GENERAL_SUBTREE_num(stack_subtrees)
subtrees = []
for i in range(num):
obj = backend._lib.sk_GENERAL_SUBTREE_value(stack_subtrees, i)
backend.openssl_assert(obj != backend._ffi.NULL)
name = _decode_general_name(backend, obj.base)
subtrees.append(name)
return subtrees
def _decode_policy_constraints(backend, pc):
pc = backend._ffi.cast("POLICY_CONSTRAINTS *", pc)
pc = backend._ffi.gc(pc, backend._lib.POLICY_CONSTRAINTS_free)
require_explicit_policy = _asn1_integer_to_int_or_none(
backend, pc.requireExplicitPolicy
)
inhibit_policy_mapping = _asn1_integer_to_int_or_none(
backend, pc.inhibitPolicyMapping
)
return x509.PolicyConstraints(
require_explicit_policy, inhibit_policy_mapping
)
def _decode_extended_key_usage(backend, sk):
sk = backend._ffi.cast("Cryptography_STACK_OF_ASN1_OBJECT *", sk)
sk = backend._ffi.gc(sk, backend._lib.sk_ASN1_OBJECT_free)
num = backend._lib.sk_ASN1_OBJECT_num(sk)
ekus = []
for i in range(num):
obj = backend._lib.sk_ASN1_OBJECT_value(sk, i)
backend.openssl_assert(obj != backend._ffi.NULL)
oid = x509.ObjectIdentifier(_obj2txt(backend, obj))
ekus.append(oid)
return x509.ExtendedKeyUsage(ekus)
_DISTPOINT_TYPE_FULLNAME = 0
_DISTPOINT_TYPE_RELATIVENAME = 1
def _decode_dist_points(backend, cdps):
cdps = backend._ffi.cast("Cryptography_STACK_OF_DIST_POINT *", cdps)
cdps = backend._ffi.gc(cdps, backend._lib.CRL_DIST_POINTS_free)
num = backend._lib.sk_DIST_POINT_num(cdps)
dist_points = []
for i in range(num):
full_name = None
relative_name = None
crl_issuer = None
reasons = None
cdp = backend._lib.sk_DIST_POINT_value(cdps, i)
if cdp.reasons != backend._ffi.NULL:
reasons = _decode_reasons(backend, cdp.reasons)
if cdp.CRLissuer != backend._ffi.NULL:
crl_issuer = _decode_general_names(backend, cdp.CRLissuer)
# Certificates may have a crl_issuer/reasons and no distribution
# point so make sure it's not null.
if cdp.distpoint != backend._ffi.NULL:
full_name, relative_name = _decode_distpoint(
backend, cdp.distpoint
)
dist_points.append(
x509.DistributionPoint(
full_name, relative_name, reasons, crl_issuer
)
)
return dist_points
# ReasonFlags ::= BIT STRING {
# unused (0),
# keyCompromise (1),
# cACompromise (2),
# affiliationChanged (3),
# superseded (4),
# cessationOfOperation (5),
# certificateHold (6),
# privilegeWithdrawn (7),
# aACompromise (8) }
_REASON_BIT_MAPPING = {
1: x509.ReasonFlags.key_compromise,
2: x509.ReasonFlags.ca_compromise,
3: x509.ReasonFlags.affiliation_changed,
4: x509.ReasonFlags.superseded,
5: x509.ReasonFlags.cessation_of_operation,
6: x509.ReasonFlags.certificate_hold,
7: x509.ReasonFlags.privilege_withdrawn,
8: x509.ReasonFlags.aa_compromise,
}
def _decode_reasons(backend, reasons):
# We will check each bit from RFC 5280
enum_reasons = []
for bit_position, reason in six.iteritems(_REASON_BIT_MAPPING):
if backend._lib.ASN1_BIT_STRING_get_bit(reasons, bit_position):
enum_reasons.append(reason)
return frozenset(enum_reasons)
def _decode_distpoint(backend, distpoint):
if distpoint.type == _DISTPOINT_TYPE_FULLNAME:
full_name = _decode_general_names(backend, distpoint.name.fullname)
return full_name, None
# OpenSSL code doesn't test for a specific type for
# relativename, everything that isn't fullname is considered
# relativename. Per RFC 5280:
#
# DistributionPointName ::= CHOICE {
# fullName [0] GeneralNames,
# nameRelativeToCRLIssuer [1] RelativeDistinguishedName }
rns = distpoint.name.relativename
rnum = backend._lib.sk_X509_NAME_ENTRY_num(rns)
attributes = set()
for i in range(rnum):
rn = backend._lib.sk_X509_NAME_ENTRY_value(
rns, i
)
backend.openssl_assert(rn != backend._ffi.NULL)
attributes.add(
_decode_x509_name_entry(backend, rn)
)
relative_name = x509.RelativeDistinguishedName(attributes)
return None, relative_name
def _decode_crl_distribution_points(backend, cdps):
dist_points = _decode_dist_points(backend, cdps)
return x509.CRLDistributionPoints(dist_points)
def _decode_freshest_crl(backend, cdps):
dist_points = _decode_dist_points(backend, cdps)
return x509.FreshestCRL(dist_points)
def _decode_inhibit_any_policy(backend, asn1_int):
asn1_int = backend._ffi.cast("ASN1_INTEGER *", asn1_int)
asn1_int = backend._ffi.gc(asn1_int, backend._lib.ASN1_INTEGER_free)
skip_certs = _asn1_integer_to_int(backend, asn1_int)
return x509.InhibitAnyPolicy(skip_certs)
def _decode_precert_signed_certificate_timestamps(backend, asn1_scts):
from cryptography.hazmat.backends.openssl.x509 import (
_SignedCertificateTimestamp
)
asn1_scts = backend._ffi.cast("Cryptography_STACK_OF_SCT *", asn1_scts)
asn1_scts = backend._ffi.gc(asn1_scts, backend._lib.SCT_LIST_free)
scts = []
for i in range(backend._lib.sk_SCT_num(asn1_scts)):
sct = backend._lib.sk_SCT_value(asn1_scts, i)
scts.append(_SignedCertificateTimestamp(backend, asn1_scts, sct))
return x509.PrecertificateSignedCertificateTimestamps(scts)
# CRLReason ::= ENUMERATED {
# unspecified (0),
# keyCompromise (1),
# cACompromise (2),
# affiliationChanged (3),
# superseded (4),
# cessationOfOperation (5),
# certificateHold (6),
# -- value 7 is not used
# removeFromCRL (8),
# privilegeWithdrawn (9),
# aACompromise (10) }
_CRL_ENTRY_REASON_CODE_TO_ENUM = {
0: x509.ReasonFlags.unspecified,
1: x509.ReasonFlags.key_compromise,
2: x509.ReasonFlags.ca_compromise,
3: x509.ReasonFlags.affiliation_changed,
4: x509.ReasonFlags.superseded,
5: x509.ReasonFlags.cessation_of_operation,
6: x509.ReasonFlags.certificate_hold,
8: x509.ReasonFlags.remove_from_crl,
9: x509.ReasonFlags.privilege_withdrawn,
10: x509.ReasonFlags.aa_compromise,
}
_CRL_ENTRY_REASON_ENUM_TO_CODE = {
x509.ReasonFlags.unspecified: 0,
x509.ReasonFlags.key_compromise: 1,
x509.ReasonFlags.ca_compromise: 2,
x509.ReasonFlags.affiliation_changed: 3,
x509.ReasonFlags.superseded: 4,
x509.ReasonFlags.cessation_of_operation: 5,
x509.ReasonFlags.certificate_hold: 6,
x509.ReasonFlags.remove_from_crl: 8,
x509.ReasonFlags.privilege_withdrawn: 9,
x509.ReasonFlags.aa_compromise: 10
}
def _decode_crl_reason(backend, enum):
enum = backend._ffi.cast("ASN1_ENUMERATED *", enum)
enum = backend._ffi.gc(enum, backend._lib.ASN1_ENUMERATED_free)
code = backend._lib.ASN1_ENUMERATED_get(enum)
try:
return x509.CRLReason(_CRL_ENTRY_REASON_CODE_TO_ENUM[code])
except KeyError:
raise ValueError("Unsupported reason code: {0}".format(code))
def _decode_invalidity_date(backend, inv_date):
generalized_time = backend._ffi.cast(
"ASN1_GENERALIZEDTIME *", inv_date
)
generalized_time = backend._ffi.gc(
generalized_time, backend._lib.ASN1_GENERALIZEDTIME_free
)
return x509.InvalidityDate(
_parse_asn1_generalized_time(backend, generalized_time)
)
def _decode_cert_issuer(backend, gns):
gns = backend._ffi.cast("GENERAL_NAMES *", gns)
gns = backend._ffi.gc(gns, backend._lib.GENERAL_NAMES_free)
general_names = _decode_general_names(backend, gns)
return x509.CertificateIssuer(general_names)
def _asn1_to_der(backend, asn1_type):
buf = backend._ffi.new("unsigned char **")
res = backend._lib.i2d_ASN1_TYPE(asn1_type, buf)
backend.openssl_assert(res >= 0)
backend.openssl_assert(buf[0] != backend._ffi.NULL)
buf = backend._ffi.gc(
buf, lambda buffer: backend._lib.OPENSSL_free(buffer[0])
)
return backend._ffi.buffer(buf[0], res)[:]
def _asn1_integer_to_int(backend, asn1_int):
bn = backend._lib.ASN1_INTEGER_to_BN(asn1_int, backend._ffi.NULL)
backend.openssl_assert(bn != backend._ffi.NULL)
bn = backend._ffi.gc(bn, backend._lib.BN_free)
return backend._bn_to_int(bn)
def _asn1_integer_to_int_or_none(backend, asn1_int):
if asn1_int == backend._ffi.NULL:
return None
else:
return _asn1_integer_to_int(backend, asn1_int)
def _asn1_string_to_bytes(backend, asn1_string):
return backend._ffi.buffer(asn1_string.data, asn1_string.length)[:]
def _asn1_string_to_ascii(backend, asn1_string):
return _asn1_string_to_bytes(backend, asn1_string).decode("ascii")
def _asn1_string_to_utf8(backend, asn1_string):
buf = backend._ffi.new("unsigned char **")
res = backend._lib.ASN1_STRING_to_UTF8(buf, asn1_string)
if res == -1:
raise ValueError(
"Unsupported ASN1 string type. Type: {0}".format(asn1_string.type)
)
backend.openssl_assert(buf[0] != backend._ffi.NULL)
buf = backend._ffi.gc(
buf, lambda buffer: backend._lib.OPENSSL_free(buffer[0])
)
return backend._ffi.buffer(buf[0], res)[:].decode('utf8')
def _parse_asn1_time(backend, asn1_time):
backend.openssl_assert(asn1_time != backend._ffi.NULL)
generalized_time = backend._lib.ASN1_TIME_to_generalizedtime(
asn1_time, backend._ffi.NULL
)
if generalized_time == backend._ffi.NULL:
raise ValueError(
"Couldn't parse ASN.1 time as generalizedtime {!r}".format(
_asn1_string_to_bytes(backend, asn1_time)
)
)
generalized_time = backend._ffi.gc(
generalized_time, backend._lib.ASN1_GENERALIZEDTIME_free
)
return _parse_asn1_generalized_time(backend, generalized_time)
def _parse_asn1_generalized_time(backend, generalized_time):
time = _asn1_string_to_ascii(
backend, backend._ffi.cast("ASN1_STRING *", generalized_time)
)
return datetime.datetime.strptime(time, "%Y%m%d%H%M%SZ")
def _decode_nonce(backend, nonce):
nonce = backend._ffi.cast("ASN1_OCTET_STRING *", nonce)
nonce = backend._ffi.gc(nonce, backend._lib.ASN1_OCTET_STRING_free)
return x509.OCSPNonce(_asn1_string_to_bytes(backend, nonce))
_EXTENSION_HANDLERS_NO_SCT = {
ExtensionOID.BASIC_CONSTRAINTS: _decode_basic_constraints,
ExtensionOID.SUBJECT_KEY_IDENTIFIER: _decode_subject_key_identifier,
ExtensionOID.KEY_USAGE: _decode_key_usage,
ExtensionOID.SUBJECT_ALTERNATIVE_NAME: _decode_subject_alt_name,
ExtensionOID.EXTENDED_KEY_USAGE: _decode_extended_key_usage,
ExtensionOID.AUTHORITY_KEY_IDENTIFIER: _decode_authority_key_identifier,
ExtensionOID.AUTHORITY_INFORMATION_ACCESS: (
_decode_authority_information_access
),
ExtensionOID.CERTIFICATE_POLICIES: _decode_certificate_policies,
ExtensionOID.CRL_DISTRIBUTION_POINTS: _decode_crl_distribution_points,
ExtensionOID.FRESHEST_CRL: _decode_freshest_crl,
ExtensionOID.OCSP_NO_CHECK: _decode_ocsp_no_check,
ExtensionOID.INHIBIT_ANY_POLICY: _decode_inhibit_any_policy,
ExtensionOID.ISSUER_ALTERNATIVE_NAME: _decode_issuer_alt_name,
ExtensionOID.NAME_CONSTRAINTS: _decode_name_constraints,
ExtensionOID.POLICY_CONSTRAINTS: _decode_policy_constraints,
}
_EXTENSION_HANDLERS = _EXTENSION_HANDLERS_NO_SCT.copy()
_EXTENSION_HANDLERS[
ExtensionOID.PRECERT_SIGNED_CERTIFICATE_TIMESTAMPS
] = _decode_precert_signed_certificate_timestamps
_REVOKED_EXTENSION_HANDLERS = {
CRLEntryExtensionOID.CRL_REASON: _decode_crl_reason,
CRLEntryExtensionOID.INVALIDITY_DATE: _decode_invalidity_date,
CRLEntryExtensionOID.CERTIFICATE_ISSUER: _decode_cert_issuer,
}
_CRL_EXTENSION_HANDLERS = {
ExtensionOID.CRL_NUMBER: _decode_crl_number,
ExtensionOID.DELTA_CRL_INDICATOR: _decode_delta_crl_indicator,
ExtensionOID.AUTHORITY_KEY_IDENTIFIER: _decode_authority_key_identifier,
ExtensionOID.ISSUER_ALTERNATIVE_NAME: _decode_issuer_alt_name,
ExtensionOID.AUTHORITY_INFORMATION_ACCESS: (
_decode_authority_information_access
),
}
_OCSP_REQ_EXTENSION_HANDLERS = {
OCSPExtensionOID.NONCE: _decode_nonce,
}
_OCSP_BASICRESP_EXTENSION_HANDLERS = {
OCSPExtensionOID.NONCE: _decode_nonce,
}
_CERTIFICATE_EXTENSION_PARSER_NO_SCT = _X509ExtensionParser(
ext_count=lambda backend, x: backend._lib.X509_get_ext_count(x),
get_ext=lambda backend, x, i: backend._lib.X509_get_ext(x, i),
handlers=_EXTENSION_HANDLERS_NO_SCT
)
_CERTIFICATE_EXTENSION_PARSER = _X509ExtensionParser(
ext_count=lambda backend, x: backend._lib.X509_get_ext_count(x),
get_ext=lambda backend, x, i: backend._lib.X509_get_ext(x, i),
handlers=_EXTENSION_HANDLERS
)
_CSR_EXTENSION_PARSER = _X509ExtensionParser(
ext_count=lambda backend, x: backend._lib.sk_X509_EXTENSION_num(x),
get_ext=lambda backend, x, i: backend._lib.sk_X509_EXTENSION_value(x, i),
handlers=_EXTENSION_HANDLERS
)
_REVOKED_CERTIFICATE_EXTENSION_PARSER = _X509ExtensionParser(
ext_count=lambda backend, x: backend._lib.X509_REVOKED_get_ext_count(x),
get_ext=lambda backend, x, i: backend._lib.X509_REVOKED_get_ext(x, i),
handlers=_REVOKED_EXTENSION_HANDLERS,
)
_CRL_EXTENSION_PARSER = _X509ExtensionParser(
ext_count=lambda backend, x: backend._lib.X509_CRL_get_ext_count(x),
get_ext=lambda backend, x, i: backend._lib.X509_CRL_get_ext(x, i),
handlers=_CRL_EXTENSION_HANDLERS,
)
_OCSP_REQ_EXT_PARSER = _X509ExtensionParser(
ext_count=lambda backend, x: backend._lib.OCSP_REQUEST_get_ext_count(x),
get_ext=lambda backend, x, i: backend._lib.OCSP_REQUEST_get_ext(x, i),
handlers=_OCSP_REQ_EXTENSION_HANDLERS,
)
_OCSP_BASICRESP_EXT_PARSER = _X509ExtensionParser(
ext_count=lambda backend, x: backend._lib.OCSP_BASICRESP_get_ext_count(x),
get_ext=lambda backend, x, i: backend._lib.OCSP_BASICRESP_get_ext(x, i),
handlers=_OCSP_BASICRESP_EXTENSION_HANDLERS,
)

View file

@ -0,0 +1,280 @@
# 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 UnsupportedAlgorithm, _Reasons
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import dh
def _dh_params_dup(dh_cdata, backend):
lib = backend._lib
ffi = backend._ffi
param_cdata = lib.DHparams_dup(dh_cdata)
backend.openssl_assert(param_cdata != ffi.NULL)
param_cdata = ffi.gc(param_cdata, lib.DH_free)
if lib.CRYPTOGRAPHY_OPENSSL_LESS_THAN_102:
# In OpenSSL versions < 1.0.2 or libressl DHparams_dup don't copy q
q = ffi.new("BIGNUM **")
lib.DH_get0_pqg(dh_cdata, ffi.NULL, q, ffi.NULL)
q_dup = lib.BN_dup(q[0])
res = lib.DH_set0_pqg(param_cdata, ffi.NULL, q_dup, ffi.NULL)
backend.openssl_assert(res == 1)
return param_cdata
def _dh_cdata_to_parameters(dh_cdata, backend):
param_cdata = _dh_params_dup(dh_cdata, backend)
return _DHParameters(backend, param_cdata)
@utils.register_interface(dh.DHParametersWithSerialization)
class _DHParameters(object):
def __init__(self, backend, dh_cdata):
self._backend = backend
self._dh_cdata = dh_cdata
def parameter_numbers(self):
p = self._backend._ffi.new("BIGNUM **")
g = self._backend._ffi.new("BIGNUM **")
q = self._backend._ffi.new("BIGNUM **")
self._backend._lib.DH_get0_pqg(self._dh_cdata, p, q, g)
self._backend.openssl_assert(p[0] != self._backend._ffi.NULL)
self._backend.openssl_assert(g[0] != self._backend._ffi.NULL)
if q[0] == self._backend._ffi.NULL:
q_val = None
else:
q_val = self._backend._bn_to_int(q[0])
return dh.DHParameterNumbers(
p=self._backend._bn_to_int(p[0]),
g=self._backend._bn_to_int(g[0]),
q=q_val
)
def generate_private_key(self):
return self._backend.generate_dh_private_key(self)
def parameter_bytes(self, encoding, format):
if format is not serialization.ParameterFormat.PKCS3:
raise ValueError(
"Only PKCS3 serialization is supported"
)
if not self._backend._lib.Cryptography_HAS_EVP_PKEY_DHX:
q = self._backend._ffi.new("BIGNUM **")
self._backend._lib.DH_get0_pqg(self._dh_cdata,
self._backend._ffi.NULL,
q,
self._backend._ffi.NULL)
if q[0] != self._backend._ffi.NULL:
raise UnsupportedAlgorithm(
"DH X9.42 serialization is not supported",
_Reasons.UNSUPPORTED_SERIALIZATION)
return self._backend._parameter_bytes(
encoding,
format,
self._dh_cdata
)
def _handle_dh_compute_key_error(errors, backend):
lib = backend._lib
backend.openssl_assert(
errors[0]._lib_reason_match(
lib.ERR_LIB_DH, lib.DH_R_INVALID_PUBKEY
)
)
raise ValueError("Public key value is invalid for this exchange.")
def _get_dh_num_bits(backend, dh_cdata):
p = backend._ffi.new("BIGNUM **")
backend._lib.DH_get0_pqg(dh_cdata, p,
backend._ffi.NULL,
backend._ffi.NULL)
backend.openssl_assert(p[0] != backend._ffi.NULL)
return backend._lib.BN_num_bits(p[0])
@utils.register_interface(dh.DHPrivateKeyWithSerialization)
class _DHPrivateKey(object):
def __init__(self, backend, dh_cdata, evp_pkey):
self._backend = backend
self._dh_cdata = dh_cdata
self._evp_pkey = evp_pkey
self._key_size_bytes = self._backend._lib.DH_size(dh_cdata)
@property
def key_size(self):
return _get_dh_num_bits(self._backend, self._dh_cdata)
def private_numbers(self):
p = self._backend._ffi.new("BIGNUM **")
g = self._backend._ffi.new("BIGNUM **")
q = self._backend._ffi.new("BIGNUM **")
self._backend._lib.DH_get0_pqg(self._dh_cdata, p, q, g)
self._backend.openssl_assert(p[0] != self._backend._ffi.NULL)
self._backend.openssl_assert(g[0] != self._backend._ffi.NULL)
if q[0] == self._backend._ffi.NULL:
q_val = None
else:
q_val = self._backend._bn_to_int(q[0])
pub_key = self._backend._ffi.new("BIGNUM **")
priv_key = self._backend._ffi.new("BIGNUM **")
self._backend._lib.DH_get0_key(self._dh_cdata, pub_key, priv_key)
self._backend.openssl_assert(pub_key[0] != self._backend._ffi.NULL)
self._backend.openssl_assert(priv_key[0] != self._backend._ffi.NULL)
return dh.DHPrivateNumbers(
public_numbers=dh.DHPublicNumbers(
parameter_numbers=dh.DHParameterNumbers(
p=self._backend._bn_to_int(p[0]),
g=self._backend._bn_to_int(g[0]),
q=q_val
),
y=self._backend._bn_to_int(pub_key[0])
),
x=self._backend._bn_to_int(priv_key[0])
)
def exchange(self, peer_public_key):
buf = self._backend._ffi.new("unsigned char[]", self._key_size_bytes)
pub_key = self._backend._ffi.new("BIGNUM **")
self._backend._lib.DH_get0_key(peer_public_key._dh_cdata, pub_key,
self._backend._ffi.NULL)
self._backend.openssl_assert(pub_key[0] != self._backend._ffi.NULL)
res = self._backend._lib.DH_compute_key(
buf,
pub_key[0],
self._dh_cdata
)
if res == -1:
errors = self._backend._consume_errors()
return _handle_dh_compute_key_error(errors, self._backend)
else:
self._backend.openssl_assert(res >= 1)
key = self._backend._ffi.buffer(buf)[:res]
pad = self._key_size_bytes - len(key)
if pad > 0:
key = (b"\x00" * pad) + key
return key
def public_key(self):
dh_cdata = _dh_params_dup(self._dh_cdata, self._backend)
pub_key = self._backend._ffi.new("BIGNUM **")
self._backend._lib.DH_get0_key(self._dh_cdata,
pub_key, self._backend._ffi.NULL)
self._backend.openssl_assert(pub_key[0] != self._backend._ffi.NULL)
pub_key_dup = self._backend._lib.BN_dup(pub_key[0])
self._backend.openssl_assert(pub_key_dup != self._backend._ffi.NULL)
res = self._backend._lib.DH_set0_key(dh_cdata,
pub_key_dup,
self._backend._ffi.NULL)
self._backend.openssl_assert(res == 1)
evp_pkey = self._backend._dh_cdata_to_evp_pkey(dh_cdata)
return _DHPublicKey(self._backend, dh_cdata, evp_pkey)
def parameters(self):
return _dh_cdata_to_parameters(self._dh_cdata, self._backend)
def private_bytes(self, encoding, format, encryption_algorithm):
if format is not serialization.PrivateFormat.PKCS8:
raise ValueError(
"DH private keys support only PKCS8 serialization"
)
if not self._backend._lib.Cryptography_HAS_EVP_PKEY_DHX:
q = self._backend._ffi.new("BIGNUM **")
self._backend._lib.DH_get0_pqg(self._dh_cdata,
self._backend._ffi.NULL,
q,
self._backend._ffi.NULL)
if q[0] != self._backend._ffi.NULL:
raise UnsupportedAlgorithm(
"DH X9.42 serialization is not supported",
_Reasons.UNSUPPORTED_SERIALIZATION)
return self._backend._private_key_bytes(
encoding,
format,
encryption_algorithm,
self._evp_pkey,
self._dh_cdata
)
@utils.register_interface(dh.DHPublicKeyWithSerialization)
class _DHPublicKey(object):
def __init__(self, backend, dh_cdata, evp_pkey):
self._backend = backend
self._dh_cdata = dh_cdata
self._evp_pkey = evp_pkey
self._key_size_bits = _get_dh_num_bits(self._backend, self._dh_cdata)
@property
def key_size(self):
return self._key_size_bits
def public_numbers(self):
p = self._backend._ffi.new("BIGNUM **")
g = self._backend._ffi.new("BIGNUM **")
q = self._backend._ffi.new("BIGNUM **")
self._backend._lib.DH_get0_pqg(self._dh_cdata, p, q, g)
self._backend.openssl_assert(p[0] != self._backend._ffi.NULL)
self._backend.openssl_assert(g[0] != self._backend._ffi.NULL)
if q[0] == self._backend._ffi.NULL:
q_val = None
else:
q_val = self._backend._bn_to_int(q[0])
pub_key = self._backend._ffi.new("BIGNUM **")
self._backend._lib.DH_get0_key(self._dh_cdata,
pub_key, self._backend._ffi.NULL)
self._backend.openssl_assert(pub_key[0] != self._backend._ffi.NULL)
return dh.DHPublicNumbers(
parameter_numbers=dh.DHParameterNumbers(
p=self._backend._bn_to_int(p[0]),
g=self._backend._bn_to_int(g[0]),
q=q_val
),
y=self._backend._bn_to_int(pub_key[0])
)
def parameters(self):
return _dh_cdata_to_parameters(self._dh_cdata, self._backend)
def public_bytes(self, encoding, format):
if format is not serialization.PublicFormat.SubjectPublicKeyInfo:
raise ValueError(
"DH public keys support only "
"SubjectPublicKeyInfo serialization"
)
if not self._backend._lib.Cryptography_HAS_EVP_PKEY_DHX:
q = self._backend._ffi.new("BIGNUM **")
self._backend._lib.DH_get0_pqg(self._dh_cdata,
self._backend._ffi.NULL,
q,
self._backend._ffi.NULL)
if q[0] != self._backend._ffi.NULL:
raise UnsupportedAlgorithm(
"DH X9.42 serialization is not supported",
_Reasons.UNSUPPORTED_SERIALIZATION)
return self._backend._public_key_bytes(
encoding,
format,
self,
self._evp_pkey,
None
)

View file

@ -6,24 +6,42 @@ from __future__ import absolute_import, division, print_function
from cryptography import utils
from cryptography.exceptions import InvalidSignature
from cryptography.hazmat.backends.openssl.utils import _truncate_digest
from cryptography.hazmat.backends.openssl.utils import (
_calculate_digest_and_algorithm, _check_not_prehashed,
_warn_sign_verify_deprecated
)
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import (
AsymmetricSignatureContext, AsymmetricVerificationContext, dsa
)
def _truncate_digest_for_dsa(dsa_cdata, digest, backend):
"""
This function truncates digests that are longer than a given DS
key's length so they can be signed. OpenSSL does this for us in
1.0.0c+ and it isn't needed in 0.9.8, but that leaves us with three
releases (1.0.0, 1.0.0a, and 1.0.0b) where this is a problem. This
truncation is not required in 0.9.8 because DSA is limited to SHA-1.
"""
def _dsa_sig_sign(backend, private_key, data):
sig_buf_len = backend._lib.DSA_size(private_key._dsa_cdata)
sig_buf = backend._ffi.new("unsigned char[]", sig_buf_len)
buflen = backend._ffi.new("unsigned int *")
order_bits = backend._lib.BN_num_bits(dsa_cdata.q)
return _truncate_digest(digest, order_bits)
# The first parameter passed to DSA_sign is unused by OpenSSL but
# must be an integer.
res = backend._lib.DSA_sign(
0, data, len(data), sig_buf, buflen, private_key._dsa_cdata
)
backend.openssl_assert(res == 1)
backend.openssl_assert(buflen[0])
return backend._ffi.buffer(sig_buf)[:buflen[0]]
def _dsa_sig_verify(backend, public_key, signature, data):
# The first parameter passed to DSA_verify is unused by OpenSSL but
# must be an integer.
res = backend._lib.DSA_verify(
0, data, len(data), signature, len(signature), public_key._dsa_cdata
)
if res != 1:
backend._consume_errors()
raise InvalidSignature
@utils.register_interface(AsymmetricVerificationContext)
@ -42,20 +60,10 @@ class _DSAVerificationContext(object):
def verify(self):
data_to_verify = self._hash_ctx.finalize()
data_to_verify = _truncate_digest_for_dsa(
self._public_key._dsa_cdata, data_to_verify, self._backend
_dsa_sig_verify(
self._backend, self._public_key, self._signature, data_to_verify
)
# The first parameter passed to DSA_verify is unused by OpenSSL but
# must be an integer.
res = self._backend._lib.DSA_verify(
0, data_to_verify, len(data_to_verify), self._signature,
len(self._signature), self._public_key._dsa_cdata)
if res != 1:
self._backend._consume_errors()
raise InvalidSignature
@utils.register_interface(AsymmetricSignatureContext)
class _DSASignatureContext(object):
@ -70,22 +78,7 @@ class _DSASignatureContext(object):
def finalize(self):
data_to_sign = self._hash_ctx.finalize()
data_to_sign = _truncate_digest_for_dsa(
self._private_key._dsa_cdata, data_to_sign, self._backend
)
sig_buf_len = self._backend._lib.DSA_size(self._private_key._dsa_cdata)
sig_buf = self._backend._ffi.new("unsigned char[]", sig_buf_len)
buflen = self._backend._ffi.new("unsigned int *")
# The first parameter passed to DSA_sign is unused by OpenSSL but
# must be an integer.
res = self._backend._lib.DSA_sign(
0, data_to_sign, len(data_to_sign), sig_buf,
buflen, self._private_key._dsa_cdata)
self._backend.openssl_assert(res == 1)
self._backend.openssl_assert(buflen[0])
return self._backend._ffi.buffer(sig_buf)[:buflen[0]]
return _dsa_sig_sign(self._backend, self._private_key, data_to_sign)
@utils.register_interface(dsa.DSAParametersWithNumbers)
@ -95,10 +88,17 @@ class _DSAParameters(object):
self._dsa_cdata = dsa_cdata
def parameter_numbers(self):
p = self._backend._ffi.new("BIGNUM **")
q = self._backend._ffi.new("BIGNUM **")
g = self._backend._ffi.new("BIGNUM **")
self._backend._lib.DSA_get0_pqg(self._dsa_cdata, p, q, g)
self._backend.openssl_assert(p[0] != self._backend._ffi.NULL)
self._backend.openssl_assert(q[0] != self._backend._ffi.NULL)
self._backend.openssl_assert(g[0] != self._backend._ffi.NULL)
return dsa.DSAParameterNumbers(
p=self._backend._bn_to_int(self._dsa_cdata.p),
q=self._backend._bn_to_int(self._dsa_cdata.q),
g=self._backend._bn_to_int(self._dsa_cdata.g)
p=self._backend._bn_to_int(p[0]),
q=self._backend._bn_to_int(q[0]),
g=self._backend._bn_to_int(g[0])
)
def generate_private_key(self):
@ -111,48 +111,71 @@ class _DSAPrivateKey(object):
self._backend = backend
self._dsa_cdata = dsa_cdata
self._evp_pkey = evp_pkey
self._key_size = self._backend._lib.BN_num_bits(self._dsa_cdata.p)
p = self._backend._ffi.new("BIGNUM **")
self._backend._lib.DSA_get0_pqg(
dsa_cdata, p, self._backend._ffi.NULL, self._backend._ffi.NULL
)
self._backend.openssl_assert(p[0] != backend._ffi.NULL)
self._key_size = self._backend._lib.BN_num_bits(p[0])
key_size = utils.read_only_property("_key_size")
def signer(self, signature_algorithm):
_warn_sign_verify_deprecated()
_check_not_prehashed(signature_algorithm)
return _DSASignatureContext(self._backend, self, signature_algorithm)
def private_numbers(self):
p = self._backend._ffi.new("BIGNUM **")
q = self._backend._ffi.new("BIGNUM **")
g = self._backend._ffi.new("BIGNUM **")
pub_key = self._backend._ffi.new("BIGNUM **")
priv_key = self._backend._ffi.new("BIGNUM **")
self._backend._lib.DSA_get0_pqg(self._dsa_cdata, p, q, g)
self._backend.openssl_assert(p[0] != self._backend._ffi.NULL)
self._backend.openssl_assert(q[0] != self._backend._ffi.NULL)
self._backend.openssl_assert(g[0] != self._backend._ffi.NULL)
self._backend._lib.DSA_get0_key(self._dsa_cdata, pub_key, priv_key)
self._backend.openssl_assert(pub_key[0] != self._backend._ffi.NULL)
self._backend.openssl_assert(priv_key[0] != self._backend._ffi.NULL)
return dsa.DSAPrivateNumbers(
public_numbers=dsa.DSAPublicNumbers(
parameter_numbers=dsa.DSAParameterNumbers(
p=self._backend._bn_to_int(self._dsa_cdata.p),
q=self._backend._bn_to_int(self._dsa_cdata.q),
g=self._backend._bn_to_int(self._dsa_cdata.g)
p=self._backend._bn_to_int(p[0]),
q=self._backend._bn_to_int(q[0]),
g=self._backend._bn_to_int(g[0])
),
y=self._backend._bn_to_int(self._dsa_cdata.pub_key)
y=self._backend._bn_to_int(pub_key[0])
),
x=self._backend._bn_to_int(self._dsa_cdata.priv_key)
x=self._backend._bn_to_int(priv_key[0])
)
def public_key(self):
dsa_cdata = self._backend._lib.DSA_new()
dsa_cdata = self._backend._lib.DSAparams_dup(self._dsa_cdata)
self._backend.openssl_assert(dsa_cdata != self._backend._ffi.NULL)
dsa_cdata = self._backend._ffi.gc(
dsa_cdata, self._backend._lib.DSA_free
)
dsa_cdata.p = self._backend._lib.BN_dup(self._dsa_cdata.p)
dsa_cdata.q = self._backend._lib.BN_dup(self._dsa_cdata.q)
dsa_cdata.g = self._backend._lib.BN_dup(self._dsa_cdata.g)
dsa_cdata.pub_key = self._backend._lib.BN_dup(self._dsa_cdata.pub_key)
pub_key = self._backend._ffi.new("BIGNUM **")
self._backend._lib.DSA_get0_key(
self._dsa_cdata, pub_key, self._backend._ffi.NULL
)
self._backend.openssl_assert(pub_key[0] != self._backend._ffi.NULL)
pub_key_dup = self._backend._lib.BN_dup(pub_key[0])
res = self._backend._lib.DSA_set0_key(
dsa_cdata, pub_key_dup, self._backend._ffi.NULL
)
self._backend.openssl_assert(res == 1)
evp_pkey = self._backend._dsa_cdata_to_evp_pkey(dsa_cdata)
return _DSAPublicKey(self._backend, dsa_cdata, evp_pkey)
def parameters(self):
dsa_cdata = self._backend._lib.DSA_new()
dsa_cdata = self._backend._lib.DSAparams_dup(self._dsa_cdata)
self._backend.openssl_assert(dsa_cdata != self._backend._ffi.NULL)
dsa_cdata = self._backend._ffi.gc(
dsa_cdata, self._backend._lib.DSA_free
)
dsa_cdata.p = self._backend._lib.BN_dup(self._dsa_cdata.p)
dsa_cdata.q = self._backend._lib.BN_dup(self._dsa_cdata.q)
dsa_cdata.g = self._backend._lib.BN_dup(self._dsa_cdata.g)
return _DSAParameters(self._backend, dsa_cdata)
def private_bytes(self, encoding, format, encryption_algorithm):
@ -164,6 +187,12 @@ class _DSAPrivateKey(object):
self._dsa_cdata
)
def sign(self, data, algorithm):
data, algorithm = _calculate_digest_and_algorithm(
self._backend, data, algorithm
)
return _dsa_sig_sign(self._backend, self, data)
@utils.register_interface(dsa.DSAPublicKeyWithSerialization)
class _DSAPublicKey(object):
@ -171,37 +200,52 @@ class _DSAPublicKey(object):
self._backend = backend
self._dsa_cdata = dsa_cdata
self._evp_pkey = evp_pkey
self._key_size = self._backend._lib.BN_num_bits(self._dsa_cdata.p)
p = self._backend._ffi.new("BIGNUM **")
self._backend._lib.DSA_get0_pqg(
dsa_cdata, p, self._backend._ffi.NULL, self._backend._ffi.NULL
)
self._backend.openssl_assert(p[0] != backend._ffi.NULL)
self._key_size = self._backend._lib.BN_num_bits(p[0])
key_size = utils.read_only_property("_key_size")
def verifier(self, signature, signature_algorithm):
_warn_sign_verify_deprecated()
if not isinstance(signature, bytes):
raise TypeError("signature must be bytes.")
_check_not_prehashed(signature_algorithm)
return _DSAVerificationContext(
self._backend, self, signature, signature_algorithm
)
def public_numbers(self):
p = self._backend._ffi.new("BIGNUM **")
q = self._backend._ffi.new("BIGNUM **")
g = self._backend._ffi.new("BIGNUM **")
pub_key = self._backend._ffi.new("BIGNUM **")
self._backend._lib.DSA_get0_pqg(self._dsa_cdata, p, q, g)
self._backend.openssl_assert(p[0] != self._backend._ffi.NULL)
self._backend.openssl_assert(q[0] != self._backend._ffi.NULL)
self._backend.openssl_assert(g[0] != self._backend._ffi.NULL)
self._backend._lib.DSA_get0_key(
self._dsa_cdata, pub_key, self._backend._ffi.NULL
)
self._backend.openssl_assert(pub_key[0] != self._backend._ffi.NULL)
return dsa.DSAPublicNumbers(
parameter_numbers=dsa.DSAParameterNumbers(
p=self._backend._bn_to_int(self._dsa_cdata.p),
q=self._backend._bn_to_int(self._dsa_cdata.q),
g=self._backend._bn_to_int(self._dsa_cdata.g)
p=self._backend._bn_to_int(p[0]),
q=self._backend._bn_to_int(q[0]),
g=self._backend._bn_to_int(g[0])
),
y=self._backend._bn_to_int(self._dsa_cdata.pub_key)
y=self._backend._bn_to_int(pub_key[0])
)
def parameters(self):
dsa_cdata = self._backend._lib.DSA_new()
self._backend.openssl_assert(dsa_cdata != self._backend._ffi.NULL)
dsa_cdata = self._backend._lib.DSAparams_dup(self._dsa_cdata)
dsa_cdata = self._backend._ffi.gc(
dsa_cdata, self._backend._lib.DSA_free
)
dsa_cdata.p = self._backend._lib.BN_dup(self._dsa_cdata.p)
dsa_cdata.q = self._backend._lib.BN_dup(self._dsa_cdata.q)
dsa_cdata.g = self._backend._lib.BN_dup(self._dsa_cdata.g)
return _DSAParameters(self._backend, dsa_cdata)
def public_bytes(self, encoding, format):
@ -213,6 +257,13 @@ class _DSAPublicKey(object):
return self._backend._public_key_bytes(
encoding,
format,
self,
self._evp_pkey,
None
)
def verify(self, signature, data, algorithm):
data, algorithm = _calculate_digest_and_algorithm(
self._backend, data, algorithm
)
return _dsa_sig_verify(self._backend, self, signature, data)

View file

@ -8,36 +8,21 @@ from cryptography import utils
from cryptography.exceptions import (
InvalidSignature, UnsupportedAlgorithm, _Reasons
)
from cryptography.hazmat.backends.openssl.utils import _truncate_digest
from cryptography.hazmat.backends.openssl.utils import (
_calculate_digest_and_algorithm, _check_not_prehashed,
_warn_sign_verify_deprecated
)
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import (
AsymmetricSignatureContext, AsymmetricVerificationContext, ec
)
def _truncate_digest_for_ecdsa(ec_key_cdata, digest, backend):
"""
This function truncates digests that are longer than a given elliptic
curve key's length so they can be signed. Since elliptic curve keys are
much shorter than RSA keys many digests (e.g. SHA-512) may require
truncation.
"""
_lib = backend._lib
_ffi = backend._ffi
group = _lib.EC_KEY_get0_group(ec_key_cdata)
with backend._tmp_bn_ctx() as bn_ctx:
order = _lib.BN_CTX_get(bn_ctx)
backend.openssl_assert(order != _ffi.NULL)
res = _lib.EC_GROUP_get_order(group, order, bn_ctx)
backend.openssl_assert(res == 1)
order_bits = _lib.BN_num_bits(order)
return _truncate_digest(digest, order_bits)
def _check_signature_algorithm(signature_algorithm):
if not isinstance(signature_algorithm, ec.ECDSA):
raise UnsupportedAlgorithm(
"Unsupported elliptic curve signature algorithm.",
_Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
def _ec_key_curve_sn(backend, ec_key):
@ -82,6 +67,28 @@ def _sn_to_elliptic_curve(backend, sn):
)
def _ecdsa_sig_sign(backend, private_key, data):
max_size = backend._lib.ECDSA_size(private_key._ec_key)
backend.openssl_assert(max_size > 0)
sigbuf = backend._ffi.new("unsigned char[]", max_size)
siglen_ptr = backend._ffi.new("unsigned int[]", 1)
res = backend._lib.ECDSA_sign(
0, data, len(data), sigbuf, siglen_ptr, private_key._ec_key
)
backend.openssl_assert(res == 1)
return backend._ffi.buffer(sigbuf)[:siglen_ptr[0]]
def _ecdsa_sig_verify(backend, public_key, signature, data):
res = backend._lib.ECDSA_verify(
0, data, len(data), signature, len(signature), public_key._ec_key
)
if res != 1:
backend._consume_errors()
raise InvalidSignature
@utils.register_interface(AsymmetricSignatureContext)
class _ECDSASignatureContext(object):
def __init__(self, backend, private_key, algorithm):
@ -93,27 +100,9 @@ class _ECDSASignatureContext(object):
self._digest.update(data)
def finalize(self):
ec_key = self._private_key._ec_key
digest = self._digest.finalize()
digest = _truncate_digest_for_ecdsa(ec_key, digest, self._backend)
max_size = self._backend._lib.ECDSA_size(ec_key)
self._backend.openssl_assert(max_size > 0)
sigbuf = self._backend._ffi.new("char[]", max_size)
siglen_ptr = self._backend._ffi.new("unsigned int[]", 1)
res = self._backend._lib.ECDSA_sign(
0,
digest,
len(digest),
sigbuf,
siglen_ptr,
ec_key
)
self._backend.openssl_assert(res == 1)
return self._backend._ffi.buffer(sigbuf)[:siglen_ptr[0]]
return _ecdsa_sig_sign(self._backend, self._private_key, digest)
@utils.register_interface(AsymmetricVerificationContext)
@ -128,24 +117,10 @@ class _ECDSAVerificationContext(object):
self._digest.update(data)
def verify(self):
ec_key = self._public_key._ec_key
digest = self._digest.finalize()
digest = _truncate_digest_for_ecdsa(ec_key, digest, self._backend)
res = self._backend._lib.ECDSA_verify(
0,
digest,
len(digest),
self._signature,
len(self._signature),
ec_key
_ecdsa_sig_verify(
self._backend, self._public_key, self._signature, digest
)
if res != 1:
self._backend._consume_errors()
raise InvalidSignature
return True
@utils.register_interface(ec.EllipticCurvePrivateKeyWithSerialization)
@ -161,15 +136,17 @@ class _EllipticCurvePrivateKey(object):
curve = utils.read_only_property("_curve")
@property
def key_size(self):
return self.curve.key_size
def signer(self, signature_algorithm):
if isinstance(signature_algorithm, ec.ECDSA):
return _ECDSASignatureContext(
self._backend, self, signature_algorithm.algorithm
)
else:
raise UnsupportedAlgorithm(
"Unsupported elliptic curve signature algorithm.",
_Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
_warn_sign_verify_deprecated()
_check_signature_algorithm(signature_algorithm)
_check_not_prehashed(signature_algorithm.algorithm)
return _ECDSASignatureContext(
self._backend, self, signature_algorithm.algorithm
)
def exchange(self, algorithm, peer_public_key):
if not (
@ -240,6 +217,13 @@ class _EllipticCurvePrivateKey(object):
self._ec_key
)
def sign(self, data, signature_algorithm):
_check_signature_algorithm(signature_algorithm)
data, algorithm = _calculate_digest_and_algorithm(
self._backend, data, signature_algorithm._algorithm
)
return _ecdsa_sig_sign(self._backend, self, data)
@utils.register_interface(ec.EllipticCurvePublicKeyWithSerialization)
class _EllipticCurvePublicKey(object):
@ -254,22 +238,24 @@ class _EllipticCurvePublicKey(object):
curve = utils.read_only_property("_curve")
@property
def key_size(self):
return self.curve.key_size
def verifier(self, signature, signature_algorithm):
_warn_sign_verify_deprecated()
if not isinstance(signature, bytes):
raise TypeError("signature must be bytes.")
if isinstance(signature_algorithm, ec.ECDSA):
return _ECDSAVerificationContext(
self._backend, self, signature, signature_algorithm.algorithm
)
else:
raise UnsupportedAlgorithm(
"Unsupported elliptic curve signature algorithm.",
_Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM)
_check_signature_algorithm(signature_algorithm)
_check_not_prehashed(signature_algorithm.algorithm)
return _ECDSAVerificationContext(
self._backend, self, signature, signature_algorithm.algorithm
)
def public_numbers(self):
set_func, get_func, group = (
self._backend._ec_key_determine_group_get_set_funcs(self._ec_key)
get_func, group = (
self._backend._ec_key_determine_group_get_func(self._ec_key)
)
point = self._backend._lib.EC_KEY_get0_public_key(self._ec_key)
self._backend.openssl_assert(point != self._backend._ffi.NULL)
@ -299,6 +285,14 @@ class _EllipticCurvePublicKey(object):
return self._backend._public_key_bytes(
encoding,
format,
self,
self._evp_pkey,
None
)
def verify(self, signature, data, signature_algorithm):
_check_signature_algorithm(signature_algorithm)
data, algorithm = _calculate_digest_and_algorithm(
self._backend, data, signature_algorithm._algorithm
)
_ecdsa_sig_verify(self._backend, self, signature, data)

View file

@ -0,0 +1,620 @@
# 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 calendar
import ipaddress
import six
from cryptography import utils, x509
from cryptography.hazmat.backends.openssl.decode_asn1 import (
_CRL_ENTRY_REASON_ENUM_TO_CODE, _DISTPOINT_TYPE_FULLNAME,
_DISTPOINT_TYPE_RELATIVENAME
)
from cryptography.x509.name import _ASN1Type
from cryptography.x509.oid import (
CRLEntryExtensionOID, ExtensionOID, OCSPExtensionOID,
)
def _encode_asn1_int(backend, x):
"""
Converts a python integer to an ASN1_INTEGER. The returned ASN1_INTEGER
will not be garbage collected (to support adding them to structs that take
ownership of the object). Be sure to register it for GC if it will be
discarded after use.
"""
# Convert Python integer to OpenSSL "bignum" in case value exceeds
# machine's native integer limits (note: `int_to_bn` doesn't automatically
# GC).
i = backend._int_to_bn(x)
i = backend._ffi.gc(i, backend._lib.BN_free)
# Wrap in an ASN.1 integer. Don't GC -- as documented.
i = backend._lib.BN_to_ASN1_INTEGER(i, backend._ffi.NULL)
backend.openssl_assert(i != backend._ffi.NULL)
return i
def _encode_asn1_int_gc(backend, x):
i = _encode_asn1_int(backend, x)
i = backend._ffi.gc(i, backend._lib.ASN1_INTEGER_free)
return i
def _encode_asn1_str(backend, data):
"""
Create an ASN1_OCTET_STRING from a Python byte string.
"""
s = backend._lib.ASN1_OCTET_STRING_new()
res = backend._lib.ASN1_OCTET_STRING_set(s, data, len(data))
backend.openssl_assert(res == 1)
return s
def _encode_asn1_utf8_str(backend, string):
"""
Create an ASN1_UTF8STRING from a Python unicode string.
This object will be an ASN1_STRING with UTF8 type in OpenSSL and
can be decoded with ASN1_STRING_to_UTF8.
"""
s = backend._lib.ASN1_UTF8STRING_new()
res = backend._lib.ASN1_STRING_set(
s, string.encode("utf8"), len(string.encode("utf8"))
)
backend.openssl_assert(res == 1)
return s
def _encode_asn1_str_gc(backend, data):
s = _encode_asn1_str(backend, data)
s = backend._ffi.gc(s, backend._lib.ASN1_OCTET_STRING_free)
return s
def _encode_inhibit_any_policy(backend, inhibit_any_policy):
return _encode_asn1_int_gc(backend, inhibit_any_policy.skip_certs)
def _encode_name(backend, name):
"""
The X509_NAME created will not be gc'd. Use _encode_name_gc if needed.
"""
subject = backend._lib.X509_NAME_new()
for rdn in name.rdns:
set_flag = 0 # indicate whether to add to last RDN or create new RDN
for attribute in rdn:
name_entry = _encode_name_entry(backend, attribute)
# X509_NAME_add_entry dups the object so we need to gc this copy
name_entry = backend._ffi.gc(
name_entry, backend._lib.X509_NAME_ENTRY_free
)
res = backend._lib.X509_NAME_add_entry(
subject, name_entry, -1, set_flag)
backend.openssl_assert(res == 1)
set_flag = -1
return subject
def _encode_name_gc(backend, attributes):
subject = _encode_name(backend, attributes)
subject = backend._ffi.gc(subject, backend._lib.X509_NAME_free)
return subject
def _encode_sk_name_entry(backend, attributes):
"""
The sk_X509_NAME_ENTRY created will not be gc'd.
"""
stack = backend._lib.sk_X509_NAME_ENTRY_new_null()
for attribute in attributes:
name_entry = _encode_name_entry(backend, attribute)
res = backend._lib.sk_X509_NAME_ENTRY_push(stack, name_entry)
backend.openssl_assert(res == 1)
return stack
def _encode_name_entry(backend, attribute):
if attribute._type is _ASN1Type.BMPString:
value = attribute.value.encode('utf_16_be')
else:
value = attribute.value.encode('utf8')
obj = _txt2obj_gc(backend, attribute.oid.dotted_string)
name_entry = backend._lib.X509_NAME_ENTRY_create_by_OBJ(
backend._ffi.NULL, obj, attribute._type.value, value, len(value)
)
return name_entry
def _encode_crl_number_delta_crl_indicator(backend, ext):
return _encode_asn1_int_gc(backend, ext.crl_number)
def _encode_crl_reason(backend, crl_reason):
asn1enum = backend._lib.ASN1_ENUMERATED_new()
backend.openssl_assert(asn1enum != backend._ffi.NULL)
asn1enum = backend._ffi.gc(asn1enum, backend._lib.ASN1_ENUMERATED_free)
res = backend._lib.ASN1_ENUMERATED_set(
asn1enum, _CRL_ENTRY_REASON_ENUM_TO_CODE[crl_reason.reason]
)
backend.openssl_assert(res == 1)
return asn1enum
def _encode_invalidity_date(backend, invalidity_date):
time = backend._lib.ASN1_GENERALIZEDTIME_set(
backend._ffi.NULL, calendar.timegm(
invalidity_date.invalidity_date.timetuple()
)
)
backend.openssl_assert(time != backend._ffi.NULL)
time = backend._ffi.gc(time, backend._lib.ASN1_GENERALIZEDTIME_free)
return time
def _encode_certificate_policies(backend, certificate_policies):
cp = backend._lib.sk_POLICYINFO_new_null()
backend.openssl_assert(cp != backend._ffi.NULL)
cp = backend._ffi.gc(cp, backend._lib.sk_POLICYINFO_free)
for policy_info in certificate_policies:
pi = backend._lib.POLICYINFO_new()
backend.openssl_assert(pi != backend._ffi.NULL)
res = backend._lib.sk_POLICYINFO_push(cp, pi)
backend.openssl_assert(res >= 1)
oid = _txt2obj(backend, policy_info.policy_identifier.dotted_string)
pi.policyid = oid
if policy_info.policy_qualifiers:
pqis = backend._lib.sk_POLICYQUALINFO_new_null()
backend.openssl_assert(pqis != backend._ffi.NULL)
for qualifier in policy_info.policy_qualifiers:
pqi = backend._lib.POLICYQUALINFO_new()
backend.openssl_assert(pqi != backend._ffi.NULL)
res = backend._lib.sk_POLICYQUALINFO_push(pqis, pqi)
backend.openssl_assert(res >= 1)
if isinstance(qualifier, six.text_type):
pqi.pqualid = _txt2obj(
backend, x509.OID_CPS_QUALIFIER.dotted_string
)
pqi.d.cpsuri = _encode_asn1_str(
backend,
qualifier.encode("ascii"),
)
else:
assert isinstance(qualifier, x509.UserNotice)
pqi.pqualid = _txt2obj(
backend, x509.OID_CPS_USER_NOTICE.dotted_string
)
un = backend._lib.USERNOTICE_new()
backend.openssl_assert(un != backend._ffi.NULL)
pqi.d.usernotice = un
if qualifier.explicit_text:
un.exptext = _encode_asn1_utf8_str(
backend, qualifier.explicit_text
)
un.noticeref = _encode_notice_reference(
backend, qualifier.notice_reference
)
pi.qualifiers = pqis
return cp
def _encode_notice_reference(backend, notice):
if notice is None:
return backend._ffi.NULL
else:
nr = backend._lib.NOTICEREF_new()
backend.openssl_assert(nr != backend._ffi.NULL)
# organization is a required field
nr.organization = _encode_asn1_utf8_str(backend, notice.organization)
notice_stack = backend._lib.sk_ASN1_INTEGER_new_null()
nr.noticenos = notice_stack
for number in notice.notice_numbers:
num = _encode_asn1_int(backend, number)
res = backend._lib.sk_ASN1_INTEGER_push(notice_stack, num)
backend.openssl_assert(res >= 1)
return nr
def _txt2obj(backend, name):
"""
Converts a Python string with an ASN.1 object ID in dotted form to a
ASN1_OBJECT.
"""
name = name.encode('ascii')
obj = backend._lib.OBJ_txt2obj(name, 1)
backend.openssl_assert(obj != backend._ffi.NULL)
return obj
def _txt2obj_gc(backend, name):
obj = _txt2obj(backend, name)
obj = backend._ffi.gc(obj, backend._lib.ASN1_OBJECT_free)
return obj
def _encode_ocsp_nocheck(backend, ext):
# Doesn't need to be GC'd
return backend._lib.ASN1_NULL_new()
def _encode_key_usage(backend, key_usage):
set_bit = backend._lib.ASN1_BIT_STRING_set_bit
ku = backend._lib.ASN1_BIT_STRING_new()
ku = backend._ffi.gc(ku, backend._lib.ASN1_BIT_STRING_free)
res = set_bit(ku, 0, key_usage.digital_signature)
backend.openssl_assert(res == 1)
res = set_bit(ku, 1, key_usage.content_commitment)
backend.openssl_assert(res == 1)
res = set_bit(ku, 2, key_usage.key_encipherment)
backend.openssl_assert(res == 1)
res = set_bit(ku, 3, key_usage.data_encipherment)
backend.openssl_assert(res == 1)
res = set_bit(ku, 4, key_usage.key_agreement)
backend.openssl_assert(res == 1)
res = set_bit(ku, 5, key_usage.key_cert_sign)
backend.openssl_assert(res == 1)
res = set_bit(ku, 6, key_usage.crl_sign)
backend.openssl_assert(res == 1)
if key_usage.key_agreement:
res = set_bit(ku, 7, key_usage.encipher_only)
backend.openssl_assert(res == 1)
res = set_bit(ku, 8, key_usage.decipher_only)
backend.openssl_assert(res == 1)
else:
res = set_bit(ku, 7, 0)
backend.openssl_assert(res == 1)
res = set_bit(ku, 8, 0)
backend.openssl_assert(res == 1)
return ku
def _encode_authority_key_identifier(backend, authority_keyid):
akid = backend._lib.AUTHORITY_KEYID_new()
backend.openssl_assert(akid != backend._ffi.NULL)
akid = backend._ffi.gc(akid, backend._lib.AUTHORITY_KEYID_free)
if authority_keyid.key_identifier is not None:
akid.keyid = _encode_asn1_str(
backend,
authority_keyid.key_identifier,
)
if authority_keyid.authority_cert_issuer is not None:
akid.issuer = _encode_general_names(
backend, authority_keyid.authority_cert_issuer
)
if authority_keyid.authority_cert_serial_number is not None:
akid.serial = _encode_asn1_int(
backend, authority_keyid.authority_cert_serial_number
)
return akid
def _encode_basic_constraints(backend, basic_constraints):
constraints = backend._lib.BASIC_CONSTRAINTS_new()
constraints = backend._ffi.gc(
constraints, backend._lib.BASIC_CONSTRAINTS_free
)
constraints.ca = 255 if basic_constraints.ca else 0
if basic_constraints.ca and basic_constraints.path_length is not None:
constraints.pathlen = _encode_asn1_int(
backend, basic_constraints.path_length
)
return constraints
def _encode_authority_information_access(backend, authority_info_access):
aia = backend._lib.sk_ACCESS_DESCRIPTION_new_null()
backend.openssl_assert(aia != backend._ffi.NULL)
aia = backend._ffi.gc(
aia, backend._lib.sk_ACCESS_DESCRIPTION_free
)
for access_description in authority_info_access:
ad = backend._lib.ACCESS_DESCRIPTION_new()
method = _txt2obj(
backend, access_description.access_method.dotted_string
)
gn = _encode_general_name(backend, access_description.access_location)
ad.method = method
ad.location = gn
res = backend._lib.sk_ACCESS_DESCRIPTION_push(aia, ad)
backend.openssl_assert(res >= 1)
return aia
def _encode_general_names(backend, names):
general_names = backend._lib.GENERAL_NAMES_new()
backend.openssl_assert(general_names != backend._ffi.NULL)
for name in names:
gn = _encode_general_name(backend, name)
res = backend._lib.sk_GENERAL_NAME_push(general_names, gn)
backend.openssl_assert(res != 0)
return general_names
def _encode_alt_name(backend, san):
general_names = _encode_general_names(backend, san)
general_names = backend._ffi.gc(
general_names, backend._lib.GENERAL_NAMES_free
)
return general_names
def _encode_subject_key_identifier(backend, ski):
return _encode_asn1_str_gc(backend, ski.digest)
def _encode_general_name(backend, name):
if isinstance(name, x509.DNSName):
gn = backend._lib.GENERAL_NAME_new()
backend.openssl_assert(gn != backend._ffi.NULL)
gn.type = backend._lib.GEN_DNS
ia5 = backend._lib.ASN1_IA5STRING_new()
backend.openssl_assert(ia5 != backend._ffi.NULL)
# ia5strings are supposed to be ITU T.50 but to allow round-tripping
# of broken certs that encode utf8 we'll encode utf8 here too.
value = name.value.encode("utf8")
res = backend._lib.ASN1_STRING_set(ia5, value, len(value))
backend.openssl_assert(res == 1)
gn.d.dNSName = ia5
elif isinstance(name, x509.RegisteredID):
gn = backend._lib.GENERAL_NAME_new()
backend.openssl_assert(gn != backend._ffi.NULL)
gn.type = backend._lib.GEN_RID
obj = backend._lib.OBJ_txt2obj(
name.value.dotted_string.encode('ascii'), 1
)
backend.openssl_assert(obj != backend._ffi.NULL)
gn.d.registeredID = obj
elif isinstance(name, x509.DirectoryName):
gn = backend._lib.GENERAL_NAME_new()
backend.openssl_assert(gn != backend._ffi.NULL)
dir_name = _encode_name(backend, name.value)
gn.type = backend._lib.GEN_DIRNAME
gn.d.directoryName = dir_name
elif isinstance(name, x509.IPAddress):
gn = backend._lib.GENERAL_NAME_new()
backend.openssl_assert(gn != backend._ffi.NULL)
if isinstance(name.value, ipaddress.IPv4Network):
packed = (
name.value.network_address.packed +
utils.int_to_bytes(((1 << 32) - name.value.num_addresses), 4)
)
elif isinstance(name.value, ipaddress.IPv6Network):
packed = (
name.value.network_address.packed +
utils.int_to_bytes((1 << 128) - name.value.num_addresses, 16)
)
else:
packed = name.value.packed
ipaddr = _encode_asn1_str(backend, packed)
gn.type = backend._lib.GEN_IPADD
gn.d.iPAddress = ipaddr
elif isinstance(name, x509.OtherName):
gn = backend._lib.GENERAL_NAME_new()
backend.openssl_assert(gn != backend._ffi.NULL)
other_name = backend._lib.OTHERNAME_new()
backend.openssl_assert(other_name != backend._ffi.NULL)
type_id = backend._lib.OBJ_txt2obj(
name.type_id.dotted_string.encode('ascii'), 1
)
backend.openssl_assert(type_id != backend._ffi.NULL)
data = backend._ffi.new("unsigned char[]", name.value)
data_ptr_ptr = backend._ffi.new("unsigned char **")
data_ptr_ptr[0] = data
value = backend._lib.d2i_ASN1_TYPE(
backend._ffi.NULL, data_ptr_ptr, len(name.value)
)
if value == backend._ffi.NULL:
backend._consume_errors()
raise ValueError("Invalid ASN.1 data")
other_name.type_id = type_id
other_name.value = value
gn.type = backend._lib.GEN_OTHERNAME
gn.d.otherName = other_name
elif isinstance(name, x509.RFC822Name):
gn = backend._lib.GENERAL_NAME_new()
backend.openssl_assert(gn != backend._ffi.NULL)
# ia5strings are supposed to be ITU T.50 but to allow round-tripping
# of broken certs that encode utf8 we'll encode utf8 here too.
data = name.value.encode("utf8")
asn1_str = _encode_asn1_str(backend, data)
gn.type = backend._lib.GEN_EMAIL
gn.d.rfc822Name = asn1_str
elif isinstance(name, x509.UniformResourceIdentifier):
gn = backend._lib.GENERAL_NAME_new()
backend.openssl_assert(gn != backend._ffi.NULL)
# ia5strings are supposed to be ITU T.50 but to allow round-tripping
# of broken certs that encode utf8 we'll encode utf8 here too.
data = name.value.encode("utf8")
asn1_str = _encode_asn1_str(backend, data)
gn.type = backend._lib.GEN_URI
gn.d.uniformResourceIdentifier = asn1_str
else:
raise ValueError(
"{0} is an unknown GeneralName type".format(name)
)
return gn
def _encode_extended_key_usage(backend, extended_key_usage):
eku = backend._lib.sk_ASN1_OBJECT_new_null()
eku = backend._ffi.gc(eku, backend._lib.sk_ASN1_OBJECT_free)
for oid in extended_key_usage:
obj = _txt2obj(backend, oid.dotted_string)
res = backend._lib.sk_ASN1_OBJECT_push(eku, obj)
backend.openssl_assert(res >= 1)
return eku
_CRLREASONFLAGS = {
x509.ReasonFlags.key_compromise: 1,
x509.ReasonFlags.ca_compromise: 2,
x509.ReasonFlags.affiliation_changed: 3,
x509.ReasonFlags.superseded: 4,
x509.ReasonFlags.cessation_of_operation: 5,
x509.ReasonFlags.certificate_hold: 6,
x509.ReasonFlags.privilege_withdrawn: 7,
x509.ReasonFlags.aa_compromise: 8,
}
def _encode_cdps_freshest_crl(backend, cdps):
cdp = backend._lib.sk_DIST_POINT_new_null()
cdp = backend._ffi.gc(cdp, backend._lib.sk_DIST_POINT_free)
for point in cdps:
dp = backend._lib.DIST_POINT_new()
backend.openssl_assert(dp != backend._ffi.NULL)
if point.reasons:
bitmask = backend._lib.ASN1_BIT_STRING_new()
backend.openssl_assert(bitmask != backend._ffi.NULL)
dp.reasons = bitmask
for reason in point.reasons:
res = backend._lib.ASN1_BIT_STRING_set_bit(
bitmask, _CRLREASONFLAGS[reason], 1
)
backend.openssl_assert(res == 1)
if point.full_name:
dpn = backend._lib.DIST_POINT_NAME_new()
backend.openssl_assert(dpn != backend._ffi.NULL)
dpn.type = _DISTPOINT_TYPE_FULLNAME
dpn.name.fullname = _encode_general_names(backend, point.full_name)
dp.distpoint = dpn
if point.relative_name:
dpn = backend._lib.DIST_POINT_NAME_new()
backend.openssl_assert(dpn != backend._ffi.NULL)
dpn.type = _DISTPOINT_TYPE_RELATIVENAME
relativename = _encode_sk_name_entry(backend, point.relative_name)
backend.openssl_assert(relativename != backend._ffi.NULL)
dpn.name.relativename = relativename
dp.distpoint = dpn
if point.crl_issuer:
dp.CRLissuer = _encode_general_names(backend, point.crl_issuer)
res = backend._lib.sk_DIST_POINT_push(cdp, dp)
backend.openssl_assert(res >= 1)
return cdp
def _encode_name_constraints(backend, name_constraints):
nc = backend._lib.NAME_CONSTRAINTS_new()
backend.openssl_assert(nc != backend._ffi.NULL)
nc = backend._ffi.gc(nc, backend._lib.NAME_CONSTRAINTS_free)
permitted = _encode_general_subtree(
backend, name_constraints.permitted_subtrees
)
nc.permittedSubtrees = permitted
excluded = _encode_general_subtree(
backend, name_constraints.excluded_subtrees
)
nc.excludedSubtrees = excluded
return nc
def _encode_policy_constraints(backend, policy_constraints):
pc = backend._lib.POLICY_CONSTRAINTS_new()
backend.openssl_assert(pc != backend._ffi.NULL)
pc = backend._ffi.gc(pc, backend._lib.POLICY_CONSTRAINTS_free)
if policy_constraints.require_explicit_policy is not None:
pc.requireExplicitPolicy = _encode_asn1_int(
backend, policy_constraints.require_explicit_policy
)
if policy_constraints.inhibit_policy_mapping is not None:
pc.inhibitPolicyMapping = _encode_asn1_int(
backend, policy_constraints.inhibit_policy_mapping
)
return pc
def _encode_general_subtree(backend, subtrees):
if subtrees is None:
return backend._ffi.NULL
else:
general_subtrees = backend._lib.sk_GENERAL_SUBTREE_new_null()
for name in subtrees:
gs = backend._lib.GENERAL_SUBTREE_new()
gs.base = _encode_general_name(backend, name)
res = backend._lib.sk_GENERAL_SUBTREE_push(general_subtrees, gs)
assert res >= 1
return general_subtrees
def _encode_nonce(backend, nonce):
return _encode_asn1_str_gc(backend, nonce.nonce)
_EXTENSION_ENCODE_HANDLERS = {
ExtensionOID.BASIC_CONSTRAINTS: _encode_basic_constraints,
ExtensionOID.SUBJECT_KEY_IDENTIFIER: _encode_subject_key_identifier,
ExtensionOID.KEY_USAGE: _encode_key_usage,
ExtensionOID.SUBJECT_ALTERNATIVE_NAME: _encode_alt_name,
ExtensionOID.ISSUER_ALTERNATIVE_NAME: _encode_alt_name,
ExtensionOID.EXTENDED_KEY_USAGE: _encode_extended_key_usage,
ExtensionOID.AUTHORITY_KEY_IDENTIFIER: _encode_authority_key_identifier,
ExtensionOID.CERTIFICATE_POLICIES: _encode_certificate_policies,
ExtensionOID.AUTHORITY_INFORMATION_ACCESS: (
_encode_authority_information_access
),
ExtensionOID.CRL_DISTRIBUTION_POINTS: _encode_cdps_freshest_crl,
ExtensionOID.FRESHEST_CRL: _encode_cdps_freshest_crl,
ExtensionOID.INHIBIT_ANY_POLICY: _encode_inhibit_any_policy,
ExtensionOID.OCSP_NO_CHECK: _encode_ocsp_nocheck,
ExtensionOID.NAME_CONSTRAINTS: _encode_name_constraints,
ExtensionOID.POLICY_CONSTRAINTS: _encode_policy_constraints,
}
_CRL_EXTENSION_ENCODE_HANDLERS = {
ExtensionOID.ISSUER_ALTERNATIVE_NAME: _encode_alt_name,
ExtensionOID.AUTHORITY_KEY_IDENTIFIER: _encode_authority_key_identifier,
ExtensionOID.AUTHORITY_INFORMATION_ACCESS: (
_encode_authority_information_access
),
ExtensionOID.CRL_NUMBER: _encode_crl_number_delta_crl_indicator,
ExtensionOID.DELTA_CRL_INDICATOR: _encode_crl_number_delta_crl_indicator,
}
_CRL_ENTRY_EXTENSION_ENCODE_HANDLERS = {
CRLEntryExtensionOID.CERTIFICATE_ISSUER: _encode_alt_name,
CRLEntryExtensionOID.CRL_REASON: _encode_crl_reason,
CRLEntryExtensionOID.INVALIDITY_DATE: _encode_invalidity_date,
}
_OCSP_REQUEST_EXTENSION_ENCODE_HANDLERS = {
OCSPExtensionOID.NONCE: _encode_nonce,
}
_OCSP_BASICRESP_EXTENSION_ENCODE_HANDLERS = {
OCSPExtensionOID.NONCE: _encode_nonce,
}

View file

@ -18,11 +18,11 @@ class _HashContext(object):
self._backend = backend
if ctx is None:
ctx = self._backend._lib.EVP_MD_CTX_create()
ctx = self._backend._ffi.gc(ctx,
self._backend._lib.EVP_MD_CTX_destroy)
evp_md = self._backend._lib.EVP_get_digestbyname(
algorithm.name.encode("ascii"))
ctx = self._backend._lib.Cryptography_EVP_MD_CTX_new()
ctx = self._backend._ffi.gc(
ctx, self._backend._lib.Cryptography_EVP_MD_CTX_free
)
evp_md = self._backend._evp_md_from_algorithm(algorithm)
if evp_md == self._backend._ffi.NULL:
raise UnsupportedAlgorithm(
"{0} is not a supported hash on this backend.".format(
@ -38,9 +38,9 @@ class _HashContext(object):
algorithm = utils.read_only_property("_algorithm")
def copy(self):
copied_ctx = self._backend._lib.EVP_MD_CTX_create()
copied_ctx = self._backend._lib.Cryptography_EVP_MD_CTX_new()
copied_ctx = self._backend._ffi.gc(
copied_ctx, self._backend._lib.EVP_MD_CTX_destroy
copied_ctx, self._backend._lib.Cryptography_EVP_MD_CTX_free
)
res = self._backend._lib.EVP_MD_CTX_copy_ex(copied_ctx, self._ctx)
self._backend.openssl_assert(res != 0)
@ -57,6 +57,4 @@ class _HashContext(object):
res = self._backend._lib.EVP_DigestFinal_ex(self._ctx, buf, outlen)
self._backend.openssl_assert(res != 0)
self._backend.openssl_assert(outlen[0] == self.algorithm.digest_size)
res = self._backend._lib.EVP_MD_CTX_cleanup(self._ctx)
self._backend.openssl_assert(res == 1)
return self._backend._ffi.buffer(buf)[:outlen[0]]

View file

@ -9,10 +9,10 @@ from cryptography import utils
from cryptography.exceptions import (
InvalidSignature, UnsupportedAlgorithm, _Reasons
)
from cryptography.hazmat.primitives import constant_time, hashes, interfaces
from cryptography.hazmat.primitives import constant_time, hashes, mac
@utils.register_interface(interfaces.MACContext)
@utils.register_interface(mac.MACContext)
@utils.register_interface(hashes.HashContext)
class _HMACContext(object):
def __init__(self, backend, key, algorithm, ctx=None):
@ -20,20 +20,19 @@ class _HMACContext(object):
self._backend = backend
if ctx is None:
ctx = self._backend._ffi.new("HMAC_CTX *")
self._backend._lib.HMAC_CTX_init(ctx)
ctx = self._backend._lib.Cryptography_HMAC_CTX_new()
self._backend.openssl_assert(ctx != self._backend._ffi.NULL)
ctx = self._backend._ffi.gc(
ctx, self._backend._lib.HMAC_CTX_cleanup
ctx, self._backend._lib.Cryptography_HMAC_CTX_free
)
evp_md = self._backend._lib.EVP_get_digestbyname(
algorithm.name.encode('ascii'))
evp_md = self._backend._evp_md_from_algorithm(algorithm)
if evp_md == self._backend._ffi.NULL:
raise UnsupportedAlgorithm(
"{0} is not a supported hash on this backend.".format(
"{0} is not a supported hash on this backend".format(
algorithm.name),
_Reasons.UNSUPPORTED_HASH
)
res = self._backend._lib.Cryptography_HMAC_Init_ex(
res = self._backend._lib.HMAC_Init_ex(
ctx, key, len(key), evp_md, self._backend._ffi.NULL
)
self._backend.openssl_assert(res != 0)
@ -44,35 +43,28 @@ class _HMACContext(object):
algorithm = utils.read_only_property("_algorithm")
def copy(self):
copied_ctx = self._backend._ffi.new("HMAC_CTX *")
self._backend._lib.HMAC_CTX_init(copied_ctx)
copied_ctx = self._backend._lib.Cryptography_HMAC_CTX_new()
self._backend.openssl_assert(copied_ctx != self._backend._ffi.NULL)
copied_ctx = self._backend._ffi.gc(
copied_ctx, self._backend._lib.HMAC_CTX_cleanup
)
res = self._backend._lib.Cryptography_HMAC_CTX_copy(
copied_ctx, self._ctx
copied_ctx, self._backend._lib.Cryptography_HMAC_CTX_free
)
res = self._backend._lib.HMAC_CTX_copy(copied_ctx, self._ctx)
self._backend.openssl_assert(res != 0)
return _HMACContext(
self._backend, self._key, self.algorithm, ctx=copied_ctx
)
def update(self, data):
res = self._backend._lib.Cryptography_HMAC_Update(
self._ctx, data, len(data)
)
res = self._backend._lib.HMAC_Update(self._ctx, data, len(data))
self._backend.openssl_assert(res != 0)
def finalize(self):
buf = self._backend._ffi.new("unsigned char[]",
self._backend._lib.EVP_MAX_MD_SIZE)
outlen = self._backend._ffi.new("unsigned int *")
res = self._backend._lib.Cryptography_HMAC_Final(
self._ctx, buf, outlen
)
res = self._backend._lib.HMAC_Final(self._ctx, buf, outlen)
self._backend.openssl_assert(res != 0)
self._backend.openssl_assert(outlen[0] == self.algorithm.digest_size)
self._backend._lib.HMAC_CTX_cleanup(self._ctx)
return self._backend._ffi.buffer(buf)[:outlen[0]]
def verify(self, signature):

View file

@ -0,0 +1,370 @@
# 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 functools
from cryptography import utils, x509
from cryptography.exceptions import UnsupportedAlgorithm
from cryptography.hazmat.backends.openssl.decode_asn1 import (
_CRL_ENTRY_REASON_CODE_TO_ENUM, _OCSP_BASICRESP_EXT_PARSER,
_OCSP_REQ_EXT_PARSER, _asn1_integer_to_int,
_asn1_string_to_bytes, _decode_x509_name, _obj2txt,
_parse_asn1_generalized_time,
)
from cryptography.hazmat.backends.openssl.x509 import _Certificate
from cryptography.hazmat.primitives import serialization
from cryptography.x509.ocsp import (
OCSPCertStatus, OCSPRequest, OCSPResponse, OCSPResponseStatus,
_CERT_STATUS_TO_ENUM, _OIDS_TO_HASH, _RESPONSE_STATUS_TO_ENUM,
)
def _requires_successful_response(func):
@functools.wraps(func)
def wrapper(self, *args):
if self.response_status != OCSPResponseStatus.SUCCESSFUL:
raise ValueError(
"OCSP response status is not successful so the property "
"has no value"
)
else:
return func(self, *args)
return wrapper
def _issuer_key_hash(backend, cert_id):
key_hash = backend._ffi.new("ASN1_OCTET_STRING **")
res = backend._lib.OCSP_id_get0_info(
backend._ffi.NULL, backend._ffi.NULL,
key_hash, backend._ffi.NULL, cert_id
)
backend.openssl_assert(res == 1)
backend.openssl_assert(key_hash[0] != backend._ffi.NULL)
return _asn1_string_to_bytes(backend, key_hash[0])
def _issuer_name_hash(backend, cert_id):
name_hash = backend._ffi.new("ASN1_OCTET_STRING **")
res = backend._lib.OCSP_id_get0_info(
name_hash, backend._ffi.NULL,
backend._ffi.NULL, backend._ffi.NULL, cert_id
)
backend.openssl_assert(res == 1)
backend.openssl_assert(name_hash[0] != backend._ffi.NULL)
return _asn1_string_to_bytes(backend, name_hash[0])
def _serial_number(backend, cert_id):
num = backend._ffi.new("ASN1_INTEGER **")
res = backend._lib.OCSP_id_get0_info(
backend._ffi.NULL, backend._ffi.NULL,
backend._ffi.NULL, num, cert_id
)
backend.openssl_assert(res == 1)
backend.openssl_assert(num[0] != backend._ffi.NULL)
return _asn1_integer_to_int(backend, num[0])
def _hash_algorithm(backend, cert_id):
asn1obj = backend._ffi.new("ASN1_OBJECT **")
res = backend._lib.OCSP_id_get0_info(
backend._ffi.NULL, asn1obj,
backend._ffi.NULL, backend._ffi.NULL, cert_id
)
backend.openssl_assert(res == 1)
backend.openssl_assert(asn1obj[0] != backend._ffi.NULL)
oid = _obj2txt(backend, asn1obj[0])
try:
return _OIDS_TO_HASH[oid]
except KeyError:
raise UnsupportedAlgorithm(
"Signature algorithm OID: {0} not recognized".format(oid)
)
@utils.register_interface(OCSPResponse)
class _OCSPResponse(object):
def __init__(self, backend, ocsp_response):
self._backend = backend
self._ocsp_response = ocsp_response
status = self._backend._lib.OCSP_response_status(self._ocsp_response)
self._backend.openssl_assert(status in _RESPONSE_STATUS_TO_ENUM)
self._status = _RESPONSE_STATUS_TO_ENUM[status]
if self._status is OCSPResponseStatus.SUCCESSFUL:
basic = self._backend._lib.OCSP_response_get1_basic(
self._ocsp_response
)
self._backend.openssl_assert(basic != self._backend._ffi.NULL)
self._basic = self._backend._ffi.gc(
basic, self._backend._lib.OCSP_BASICRESP_free
)
self._backend.openssl_assert(
self._backend._lib.OCSP_resp_count(self._basic) == 1
)
self._single = self._backend._lib.OCSP_resp_get0(self._basic, 0)
self._backend.openssl_assert(
self._single != self._backend._ffi.NULL
)
self._cert_id = self._backend._lib.OCSP_SINGLERESP_get0_id(
self._single
)
self._backend.openssl_assert(
self._cert_id != self._backend._ffi.NULL
)
response_status = utils.read_only_property("_status")
@property
@_requires_successful_response
def signature_algorithm_oid(self):
alg = self._backend._lib.OCSP_resp_get0_tbs_sigalg(self._basic)
self._backend.openssl_assert(alg != self._backend._ffi.NULL)
oid = _obj2txt(self._backend, alg.algorithm)
return x509.ObjectIdentifier(oid)
@property
@_requires_successful_response
def signature(self):
sig = self._backend._lib.OCSP_resp_get0_signature(self._basic)
self._backend.openssl_assert(sig != self._backend._ffi.NULL)
return _asn1_string_to_bytes(self._backend, sig)
@property
@_requires_successful_response
def tbs_response_bytes(self):
respdata = self._backend._lib.OCSP_resp_get0_respdata(self._basic)
self._backend.openssl_assert(respdata != self._backend._ffi.NULL)
pp = self._backend._ffi.new("unsigned char **")
res = self._backend._lib.i2d_OCSP_RESPDATA(respdata, pp)
self._backend.openssl_assert(pp[0] != self._backend._ffi.NULL)
pp = self._backend._ffi.gc(
pp, lambda pointer: self._backend._lib.OPENSSL_free(pointer[0])
)
self._backend.openssl_assert(res > 0)
return self._backend._ffi.buffer(pp[0], res)[:]
@property
@_requires_successful_response
def certificates(self):
sk_x509 = self._backend._lib.OCSP_resp_get0_certs(self._basic)
num = self._backend._lib.sk_X509_num(sk_x509)
certs = []
for i in range(num):
x509 = self._backend._lib.sk_X509_value(sk_x509, i)
self._backend.openssl_assert(x509 != self._backend._ffi.NULL)
cert = _Certificate(self._backend, x509)
# We need to keep the OCSP response that the certificate came from
# alive until the Certificate object itself goes out of scope, so
# we give it a private reference.
cert._ocsp_resp = self
certs.append(cert)
return certs
@property
@_requires_successful_response
def responder_key_hash(self):
_, asn1_string = self._responder_key_name()
if asn1_string == self._backend._ffi.NULL:
return None
else:
return _asn1_string_to_bytes(self._backend, asn1_string)
@property
@_requires_successful_response
def responder_name(self):
x509_name, _ = self._responder_key_name()
if x509_name == self._backend._ffi.NULL:
return None
else:
return _decode_x509_name(self._backend, x509_name)
def _responder_key_name(self):
asn1_string = self._backend._ffi.new("ASN1_OCTET_STRING **")
x509_name = self._backend._ffi.new("X509_NAME **")
res = self._backend._lib.OCSP_resp_get0_id(
self._basic, asn1_string, x509_name
)
self._backend.openssl_assert(res == 1)
return x509_name[0], asn1_string[0]
@property
@_requires_successful_response
def produced_at(self):
produced_at = self._backend._lib.OCSP_resp_get0_produced_at(
self._basic
)
return _parse_asn1_generalized_time(self._backend, produced_at)
@property
@_requires_successful_response
def certificate_status(self):
status = self._backend._lib.OCSP_single_get0_status(
self._single,
self._backend._ffi.NULL,
self._backend._ffi.NULL,
self._backend._ffi.NULL,
self._backend._ffi.NULL,
)
self._backend.openssl_assert(status in _CERT_STATUS_TO_ENUM)
return _CERT_STATUS_TO_ENUM[status]
@property
@_requires_successful_response
def revocation_time(self):
if self.certificate_status is not OCSPCertStatus.REVOKED:
return None
asn1_time = self._backend._ffi.new("ASN1_GENERALIZEDTIME **")
self._backend._lib.OCSP_single_get0_status(
self._single,
self._backend._ffi.NULL,
asn1_time,
self._backend._ffi.NULL,
self._backend._ffi.NULL,
)
self._backend.openssl_assert(asn1_time[0] != self._backend._ffi.NULL)
return _parse_asn1_generalized_time(self._backend, asn1_time[0])
@property
@_requires_successful_response
def revocation_reason(self):
if self.certificate_status is not OCSPCertStatus.REVOKED:
return None
reason_ptr = self._backend._ffi.new("int *")
self._backend._lib.OCSP_single_get0_status(
self._single,
reason_ptr,
self._backend._ffi.NULL,
self._backend._ffi.NULL,
self._backend._ffi.NULL,
)
# If no reason is encoded OpenSSL returns -1
if reason_ptr[0] == -1:
return None
else:
self._backend.openssl_assert(
reason_ptr[0] in _CRL_ENTRY_REASON_CODE_TO_ENUM
)
return _CRL_ENTRY_REASON_CODE_TO_ENUM[reason_ptr[0]]
@property
@_requires_successful_response
def this_update(self):
asn1_time = self._backend._ffi.new("ASN1_GENERALIZEDTIME **")
self._backend._lib.OCSP_single_get0_status(
self._single,
self._backend._ffi.NULL,
self._backend._ffi.NULL,
asn1_time,
self._backend._ffi.NULL,
)
self._backend.openssl_assert(asn1_time[0] != self._backend._ffi.NULL)
return _parse_asn1_generalized_time(self._backend, asn1_time[0])
@property
@_requires_successful_response
def next_update(self):
asn1_time = self._backend._ffi.new("ASN1_GENERALIZEDTIME **")
self._backend._lib.OCSP_single_get0_status(
self._single,
self._backend._ffi.NULL,
self._backend._ffi.NULL,
self._backend._ffi.NULL,
asn1_time,
)
if asn1_time[0] != self._backend._ffi.NULL:
return _parse_asn1_generalized_time(self._backend, asn1_time[0])
else:
return None
@property
@_requires_successful_response
def issuer_key_hash(self):
return _issuer_key_hash(self._backend, self._cert_id)
@property
@_requires_successful_response
def issuer_name_hash(self):
return _issuer_name_hash(self._backend, self._cert_id)
@property
@_requires_successful_response
def hash_algorithm(self):
return _hash_algorithm(self._backend, self._cert_id)
@property
@_requires_successful_response
def serial_number(self):
return _serial_number(self._backend, self._cert_id)
@utils.cached_property
@_requires_successful_response
def extensions(self):
return _OCSP_BASICRESP_EXT_PARSER.parse(self._backend, self._basic)
def public_bytes(self, encoding):
if encoding is not serialization.Encoding.DER:
raise ValueError(
"The only allowed encoding value is Encoding.DER"
)
bio = self._backend._create_mem_bio_gc()
res = self._backend._lib.i2d_OCSP_RESPONSE_bio(
bio, self._ocsp_response
)
self._backend.openssl_assert(res > 0)
return self._backend._read_mem_bio(bio)
@utils.register_interface(OCSPRequest)
class _OCSPRequest(object):
def __init__(self, backend, ocsp_request):
if backend._lib.OCSP_request_onereq_count(ocsp_request) > 1:
raise NotImplementedError(
'OCSP request contains more than one request'
)
self._backend = backend
self._ocsp_request = ocsp_request
self._request = self._backend._lib.OCSP_request_onereq_get0(
self._ocsp_request, 0
)
self._backend.openssl_assert(self._request != self._backend._ffi.NULL)
self._cert_id = self._backend._lib.OCSP_onereq_get0_id(self._request)
self._backend.openssl_assert(self._cert_id != self._backend._ffi.NULL)
@property
def issuer_key_hash(self):
return _issuer_key_hash(self._backend, self._cert_id)
@property
def issuer_name_hash(self):
return _issuer_name_hash(self._backend, self._cert_id)
@property
def serial_number(self):
return _serial_number(self._backend, self._cert_id)
@property
def hash_algorithm(self):
return _hash_algorithm(self._backend, self._cert_id)
@utils.cached_property
def extensions(self):
return _OCSP_REQ_EXT_PARSER.parse(self._backend, self._ocsp_request)
def public_bytes(self, encoding):
if encoding is not serialization.Encoding.DER:
raise ValueError(
"The only allowed encoding value is Encoding.DER"
)
bio = self._backend._create_mem_bio_gc()
res = self._backend._lib.i2d_OCSP_REQUEST_bio(bio, self._ocsp_request)
self._backend.openssl_assert(res > 0)
return self._backend._read_mem_bio(bio)

View file

@ -8,29 +8,29 @@ import math
from cryptography import utils
from cryptography.exceptions import (
AlreadyFinalized, InvalidSignature, UnsupportedAlgorithm, _Reasons
InvalidSignature, UnsupportedAlgorithm, _Reasons
)
from cryptography.hazmat.backends.openssl.utils import (
_calculate_digest_and_algorithm, _check_not_prehashed,
_warn_sign_verify_deprecated
)
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import (
AsymmetricSignatureContext, AsymmetricVerificationContext, rsa
)
from cryptography.hazmat.primitives.asymmetric.padding import (
AsymmetricPadding, MGF1, OAEP, PKCS1v15, PSS
AsymmetricPadding, MGF1, OAEP, PKCS1v15, PSS, calculate_max_pss_salt_length
)
from cryptography.hazmat.primitives.asymmetric.rsa import (
RSAPrivateKeyWithSerialization, RSAPublicKeyWithSerialization
)
def _get_rsa_pss_salt_length(pss, key_size, digest_size):
def _get_rsa_pss_salt_length(pss, key, hash_algorithm):
salt = pss._salt_length
if salt is MGF1.MAX_LENGTH or salt is PSS.MAX_LENGTH:
# bit length - 1 per RFC 3447
emlen = int(math.ceil((key_size - 1) / 8.0))
salt_length = emlen - digest_size - 2
assert salt_length >= 0
return salt_length
return calculate_max_pss_salt_length(key, hash_algorithm)
else:
return salt
@ -43,27 +43,20 @@ def _enc_dec_rsa(backend, key, data, padding):
padding_enum = backend._lib.RSA_PKCS1_PADDING
elif isinstance(padding, OAEP):
padding_enum = backend._lib.RSA_PKCS1_OAEP_PADDING
if not isinstance(padding._mgf, MGF1):
raise UnsupportedAlgorithm(
"Only MGF1 is supported by this backend.",
_Reasons.UNSUPPORTED_MGF
)
if not isinstance(padding._mgf._algorithm, hashes.SHA1):
if not backend.rsa_padding_supported(padding):
raise UnsupportedAlgorithm(
"This backend supports only SHA1 inside MGF1 when "
"using OAEP.",
_Reasons.UNSUPPORTED_HASH
"This combination of padding and hash algorithm is not "
"supported by this backend.",
_Reasons.UNSUPPORTED_PADDING
)
if padding._label is not None and padding._label != b"":
raise ValueError("This backend does not support OAEP labels.")
if not isinstance(padding._algorithm, hashes.SHA1):
raise UnsupportedAlgorithm(
"This backend only supports SHA1 when using OAEP.",
_Reasons.UNSUPPORTED_HASH
)
else:
raise UnsupportedAlgorithm(
"{0} is not supported by this backend.".format(
@ -72,19 +65,16 @@ def _enc_dec_rsa(backend, key, data, padding):
_Reasons.UNSUPPORTED_PADDING
)
if backend._lib.Cryptography_HAS_PKEY_CTX:
return _enc_dec_rsa_pkey_ctx(backend, key, data, padding_enum)
else:
return _enc_dec_rsa_098(backend, key, data, padding_enum)
return _enc_dec_rsa_pkey_ctx(backend, key, data, padding_enum, padding)
def _enc_dec_rsa_pkey_ctx(backend, key, data, padding_enum):
def _enc_dec_rsa_pkey_ctx(backend, key, data, padding_enum, padding):
if isinstance(key, _RSAPublicKey):
init = backend._lib.EVP_PKEY_encrypt_init
crypt = backend._lib.Cryptography_EVP_PKEY_encrypt
crypt = backend._lib.EVP_PKEY_encrypt
else:
init = backend._lib.EVP_PKEY_decrypt_init
crypt = backend._lib.Cryptography_EVP_PKEY_decrypt
crypt = backend._lib.EVP_PKEY_decrypt
pkey_ctx = backend._lib.EVP_PKEY_CTX_new(
key._evp_pkey, backend._ffi.NULL
@ -98,8 +88,35 @@ def _enc_dec_rsa_pkey_ctx(backend, key, data, padding_enum):
backend.openssl_assert(res > 0)
buf_size = backend._lib.EVP_PKEY_size(key._evp_pkey)
backend.openssl_assert(buf_size > 0)
if (
isinstance(padding, OAEP) and
backend._lib.Cryptography_HAS_RSA_OAEP_MD
):
mgf1_md = backend._evp_md_non_null_from_algorithm(
padding._mgf._algorithm)
res = backend._lib.EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, mgf1_md)
backend.openssl_assert(res > 0)
oaep_md = backend._evp_md_non_null_from_algorithm(padding._algorithm)
res = backend._lib.EVP_PKEY_CTX_set_rsa_oaep_md(pkey_ctx, oaep_md)
backend.openssl_assert(res > 0)
if (
isinstance(padding, OAEP) and
padding._label is not None and
len(padding._label) > 0
):
# set0_rsa_oaep_label takes ownership of the char * so we need to
# copy it into some new memory
labelptr = backend._lib.OPENSSL_malloc(len(padding._label))
backend.openssl_assert(labelptr != backend._ffi.NULL)
backend._ffi.memmove(labelptr, padding._label, len(padding._label))
res = backend._lib.EVP_PKEY_CTX_set0_rsa_oaep_label(
pkey_ctx, labelptr, len(padding._label)
)
backend.openssl_assert(res == 1)
outlen = backend._ffi.new("size_t *", buf_size)
buf = backend._ffi.new("char[]", buf_size)
buf = backend._ffi.new("unsigned char[]", buf_size)
res = crypt(pkey_ctx, buf, outlen, data, len(data))
if res <= 0:
_handle_rsa_enc_dec_error(backend, key)
@ -107,25 +124,9 @@ def _enc_dec_rsa_pkey_ctx(backend, key, data, padding_enum):
return backend._ffi.buffer(buf)[:outlen[0]]
def _enc_dec_rsa_098(backend, key, data, padding_enum):
if isinstance(key, _RSAPublicKey):
crypt = backend._lib.RSA_public_encrypt
else:
crypt = backend._lib.RSA_private_decrypt
key_size = backend._lib.RSA_size(key._rsa_cdata)
backend.openssl_assert(key_size > 0)
buf = backend._ffi.new("unsigned char[]", key_size)
res = crypt(len(data), data, buf, key._rsa_cdata, padding_enum)
if res < 0:
_handle_rsa_enc_dec_error(backend, key)
return backend._ffi.buffer(buf)[:res]
def _handle_rsa_enc_dec_error(backend, key):
errors = backend._consume_errors()
assert errors
backend.openssl_assert(errors)
assert errors[0].lib == backend._lib.ERR_LIB_RSA
if isinstance(key, _RSAPublicKey):
assert (errors[0].reason ==
@ -139,6 +140,10 @@ def _handle_rsa_enc_dec_error(backend, key):
backend._lib.RSA_R_BLOCK_TYPE_IS_NOT_01,
backend._lib.RSA_R_BLOCK_TYPE_IS_NOT_02,
backend._lib.RSA_R_OAEP_DECODING_ERROR,
# Though this error looks similar to the
# RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE, this occurs on decrypts,
# rather than on encrypts
backend._lib.RSA_R_DATA_TOO_LARGE_FOR_MODULUS,
]
if backend._lib.Cryptography_HAS_RSA_R_PKCS_DECODING_ERROR:
decoding_errors.append(backend._lib.RSA_R_PKCS_DECODING_ERROR)
@ -147,57 +152,133 @@ def _handle_rsa_enc_dec_error(backend, key):
raise ValueError("Decryption failed.")
def _rsa_sig_determine_padding(backend, key, padding, algorithm):
if not isinstance(padding, AsymmetricPadding):
raise TypeError("Expected provider of AsymmetricPadding.")
pkey_size = backend._lib.EVP_PKEY_size(key._evp_pkey)
backend.openssl_assert(pkey_size > 0)
if isinstance(padding, PKCS1v15):
padding_enum = backend._lib.RSA_PKCS1_PADDING
elif isinstance(padding, PSS):
if not isinstance(padding._mgf, MGF1):
raise UnsupportedAlgorithm(
"Only MGF1 is supported by this backend.",
_Reasons.UNSUPPORTED_MGF
)
# Size of key in bytes - 2 is the maximum
# PSS signature length (salt length is checked later)
if pkey_size - algorithm.digest_size - 2 < 0:
raise ValueError("Digest too large for key size. Use a larger "
"key or different digest.")
padding_enum = backend._lib.RSA_PKCS1_PSS_PADDING
else:
raise UnsupportedAlgorithm(
"{0} is not supported by this backend.".format(padding.name),
_Reasons.UNSUPPORTED_PADDING
)
return padding_enum
def _rsa_sig_setup(backend, padding, algorithm, key, data, init_func):
padding_enum = _rsa_sig_determine_padding(backend, key, padding, algorithm)
evp_md = backend._evp_md_non_null_from_algorithm(algorithm)
pkey_ctx = backend._lib.EVP_PKEY_CTX_new(key._evp_pkey, backend._ffi.NULL)
backend.openssl_assert(pkey_ctx != backend._ffi.NULL)
pkey_ctx = backend._ffi.gc(pkey_ctx, backend._lib.EVP_PKEY_CTX_free)
res = init_func(pkey_ctx)
backend.openssl_assert(res == 1)
res = backend._lib.EVP_PKEY_CTX_set_signature_md(pkey_ctx, evp_md)
if res == 0:
backend._consume_errors()
raise UnsupportedAlgorithm(
"{0} is not supported by this backend for RSA signing.".format(
algorithm.name
),
_Reasons.UNSUPPORTED_HASH
)
res = backend._lib.EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, padding_enum)
backend.openssl_assert(res > 0)
if isinstance(padding, PSS):
res = backend._lib.EVP_PKEY_CTX_set_rsa_pss_saltlen(
pkey_ctx, _get_rsa_pss_salt_length(padding, key, algorithm)
)
backend.openssl_assert(res > 0)
mgf1_md = backend._evp_md_non_null_from_algorithm(
padding._mgf._algorithm)
res = backend._lib.EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, mgf1_md)
backend.openssl_assert(res > 0)
return pkey_ctx
def _rsa_sig_sign(backend, padding, algorithm, private_key, data):
pkey_ctx = _rsa_sig_setup(
backend, padding, algorithm, private_key, data,
backend._lib.EVP_PKEY_sign_init
)
buflen = backend._ffi.new("size_t *")
res = backend._lib.EVP_PKEY_sign(
pkey_ctx,
backend._ffi.NULL,
buflen,
data,
len(data)
)
backend.openssl_assert(res == 1)
buf = backend._ffi.new("unsigned char[]", buflen[0])
res = backend._lib.EVP_PKEY_sign(
pkey_ctx, buf, buflen, data, len(data))
if res != 1:
errors = backend._consume_errors()
assert errors[0].lib == backend._lib.ERR_LIB_RSA
reason = None
if (errors[0].reason ==
backend._lib.RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE):
reason = ("Salt length too long for key size. Try using "
"MAX_LENGTH instead.")
else:
assert (errors[0].reason ==
backend._lib.RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY)
reason = "Digest too large for key size. Use a larger key."
assert reason is not None
raise ValueError(reason)
return backend._ffi.buffer(buf)[:]
def _rsa_sig_verify(backend, padding, algorithm, public_key, signature, data):
pkey_ctx = _rsa_sig_setup(
backend, padding, algorithm, public_key, data,
backend._lib.EVP_PKEY_verify_init
)
res = backend._lib.EVP_PKEY_verify(
pkey_ctx, signature, len(signature), data, len(data)
)
# The previous call can return negative numbers in the event of an
# error. This is not a signature failure but we need to fail if it
# occurs.
backend.openssl_assert(res >= 0)
if res == 0:
backend._consume_errors()
raise InvalidSignature
@utils.register_interface(AsymmetricSignatureContext)
class _RSASignatureContext(object):
def __init__(self, backend, private_key, padding, algorithm):
self._backend = backend
self._private_key = private_key
if not isinstance(padding, AsymmetricPadding):
raise TypeError("Expected provider of AsymmetricPadding.")
self._pkey_size = self._backend._lib.EVP_PKEY_size(
self._private_key._evp_pkey
)
self._backend.openssl_assert(self._pkey_size > 0)
if isinstance(padding, PKCS1v15):
if self._backend._lib.Cryptography_HAS_PKEY_CTX:
self._finalize_method = self._finalize_pkey_ctx
self._padding_enum = self._backend._lib.RSA_PKCS1_PADDING
else:
self._finalize_method = self._finalize_pkcs1
elif isinstance(padding, PSS):
if not isinstance(padding._mgf, MGF1):
raise UnsupportedAlgorithm(
"Only MGF1 is supported by this backend.",
_Reasons.UNSUPPORTED_MGF
)
# Size of key in bytes - 2 is the maximum
# PSS signature length (salt length is checked later)
if self._pkey_size - algorithm.digest_size - 2 < 0:
raise ValueError("Digest too large for key size. Use a larger "
"key.")
if not self._backend._mgf1_hash_supported(padding._mgf._algorithm):
raise UnsupportedAlgorithm(
"When OpenSSL is older than 1.0.1 then only SHA1 is "
"supported with MGF1.",
_Reasons.UNSUPPORTED_HASH
)
if self._backend._lib.Cryptography_HAS_PKEY_CTX:
self._finalize_method = self._finalize_pkey_ctx
self._padding_enum = self._backend._lib.RSA_PKCS1_PSS_PADDING
else:
self._finalize_method = self._finalize_pss
else:
raise UnsupportedAlgorithm(
"{0} is not supported by this backend.".format(padding.name),
_Reasons.UNSUPPORTED_PADDING
)
# We now call _rsa_sig_determine_padding in _rsa_sig_setup. However
# we need to make a pointless call to it here so we maintain the
# API of erroring on init with this context if the values are invalid.
_rsa_sig_determine_padding(backend, private_key, padding, algorithm)
self._padding = padding
self._algorithm = algorithm
self._hash_ctx = hashes.Hash(self._algorithm, self._backend)
@ -206,135 +287,13 @@ class _RSASignatureContext(object):
self._hash_ctx.update(data)
def finalize(self):
evp_md = self._backend._lib.EVP_get_digestbyname(
self._algorithm.name.encode("ascii"))
self._backend.openssl_assert(evp_md != self._backend._ffi.NULL)
return self._finalize_method(evp_md)
def _finalize_pkey_ctx(self, evp_md):
pkey_ctx = self._backend._lib.EVP_PKEY_CTX_new(
self._private_key._evp_pkey, self._backend._ffi.NULL
return _rsa_sig_sign(
self._backend,
self._padding,
self._algorithm,
self._private_key,
self._hash_ctx.finalize()
)
self._backend.openssl_assert(pkey_ctx != self._backend._ffi.NULL)
pkey_ctx = self._backend._ffi.gc(pkey_ctx,
self._backend._lib.EVP_PKEY_CTX_free)
res = self._backend._lib.EVP_PKEY_sign_init(pkey_ctx)
self._backend.openssl_assert(res == 1)
res = self._backend._lib.EVP_PKEY_CTX_set_signature_md(
pkey_ctx, evp_md)
self._backend.openssl_assert(res > 0)
res = self._backend._lib.EVP_PKEY_CTX_set_rsa_padding(
pkey_ctx, self._padding_enum)
self._backend.openssl_assert(res > 0)
if isinstance(self._padding, PSS):
res = self._backend._lib.EVP_PKEY_CTX_set_rsa_pss_saltlen(
pkey_ctx,
_get_rsa_pss_salt_length(
self._padding,
self._private_key.key_size,
self._hash_ctx.algorithm.digest_size
)
)
self._backend.openssl_assert(res > 0)
if self._backend._lib.Cryptography_HAS_MGF1_MD:
# MGF1 MD is configurable in OpenSSL 1.0.1+
mgf1_md = self._backend._lib.EVP_get_digestbyname(
self._padding._mgf._algorithm.name.encode("ascii"))
self._backend.openssl_assert(
mgf1_md != self._backend._ffi.NULL
)
res = self._backend._lib.EVP_PKEY_CTX_set_rsa_mgf1_md(
pkey_ctx, mgf1_md
)
self._backend.openssl_assert(res > 0)
data_to_sign = self._hash_ctx.finalize()
buflen = self._backend._ffi.new("size_t *")
res = self._backend._lib.EVP_PKEY_sign(
pkey_ctx,
self._backend._ffi.NULL,
buflen,
data_to_sign,
len(data_to_sign)
)
self._backend.openssl_assert(res == 1)
buf = self._backend._ffi.new("unsigned char[]", buflen[0])
res = self._backend._lib.EVP_PKEY_sign(
pkey_ctx, buf, buflen, data_to_sign, len(data_to_sign))
if res != 1:
errors = self._backend._consume_errors()
assert errors[0].lib == self._backend._lib.ERR_LIB_RSA
reason = None
if (errors[0].reason ==
self._backend._lib.RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE):
reason = ("Salt length too long for key size. Try using "
"MAX_LENGTH instead.")
else:
assert (errors[0].reason ==
self._backend._lib.RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY)
reason = "Digest too large for key size. Use a larger key."
assert reason is not None
raise ValueError(reason)
return self._backend._ffi.buffer(buf)[:]
def _finalize_pkcs1(self, evp_md):
if self._hash_ctx._ctx is None:
raise AlreadyFinalized("Context has already been finalized.")
sig_buf = self._backend._ffi.new("char[]", self._pkey_size)
sig_len = self._backend._ffi.new("unsigned int *")
res = self._backend._lib.EVP_SignFinal(
self._hash_ctx._ctx._ctx,
sig_buf,
sig_len,
self._private_key._evp_pkey
)
self._hash_ctx.finalize()
if res == 0:
errors = self._backend._consume_errors()
assert errors[0].lib == self._backend._lib.ERR_LIB_RSA
assert (errors[0].reason ==
self._backend._lib.RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY)
raise ValueError("Digest too large for key size. Use a larger "
"key.")
return self._backend._ffi.buffer(sig_buf)[:sig_len[0]]
def _finalize_pss(self, evp_md):
data_to_sign = self._hash_ctx.finalize()
padded = self._backend._ffi.new("unsigned char[]", self._pkey_size)
res = self._backend._lib.RSA_padding_add_PKCS1_PSS(
self._private_key._rsa_cdata,
padded,
data_to_sign,
evp_md,
_get_rsa_pss_salt_length(
self._padding,
self._private_key.key_size,
len(data_to_sign)
)
)
if res != 1:
errors = self._backend._consume_errors()
assert errors[0].lib == self._backend._lib.ERR_LIB_RSA
assert (errors[0].reason ==
self._backend._lib.RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE)
raise ValueError("Salt length too long for key size. Try using "
"MAX_LENGTH instead.")
sig_buf = self._backend._ffi.new("char[]", self._pkey_size)
sig_len = self._backend._lib.RSA_private_encrypt(
self._pkey_size,
padded,
sig_buf,
self._private_key._rsa_cdata,
self._backend._lib.RSA_NO_PADDING
)
self._backend.openssl_assert(sig_len != -1)
return self._backend._ffi.buffer(sig_buf)[:sig_len]
@utils.register_interface(AsymmetricVerificationContext)
@ -343,55 +302,13 @@ class _RSAVerificationContext(object):
self._backend = backend
self._public_key = public_key
self._signature = signature
if not isinstance(padding, AsymmetricPadding):
raise TypeError("Expected provider of AsymmetricPadding.")
self._pkey_size = self._backend._lib.EVP_PKEY_size(
self._public_key._evp_pkey
)
self._backend.openssl_assert(self._pkey_size > 0)
if isinstance(padding, PKCS1v15):
if self._backend._lib.Cryptography_HAS_PKEY_CTX:
self._verify_method = self._verify_pkey_ctx
self._padding_enum = self._backend._lib.RSA_PKCS1_PADDING
else:
self._verify_method = self._verify_pkcs1
elif isinstance(padding, PSS):
if not isinstance(padding._mgf, MGF1):
raise UnsupportedAlgorithm(
"Only MGF1 is supported by this backend.",
_Reasons.UNSUPPORTED_MGF
)
# Size of key in bytes - 2 is the maximum
# PSS signature length (salt length is checked later)
if self._pkey_size - algorithm.digest_size - 2 < 0:
raise ValueError(
"Digest too large for key size. Check that you have the "
"correct key and digest algorithm."
)
if not self._backend._mgf1_hash_supported(padding._mgf._algorithm):
raise UnsupportedAlgorithm(
"When OpenSSL is older than 1.0.1 then only SHA1 is "
"supported with MGF1.",
_Reasons.UNSUPPORTED_HASH
)
if self._backend._lib.Cryptography_HAS_PKEY_CTX:
self._verify_method = self._verify_pkey_ctx
self._padding_enum = self._backend._lib.RSA_PKCS1_PSS_PADDING
else:
self._verify_method = self._verify_pss
else:
raise UnsupportedAlgorithm(
"{0} is not supported by this backend.".format(padding.name),
_Reasons.UNSUPPORTED_PADDING
)
self._padding = padding
# We now call _rsa_sig_determine_padding in _rsa_sig_setup. However
# we need to make a pointless call to it here so we maintain the
# API of erroring on init with this context if the values are invalid.
_rsa_sig_determine_padding(backend, public_key, padding, algorithm)
padding = padding
self._algorithm = algorithm
self._hash_ctx = hashes.Hash(self._algorithm, self._backend)
@ -399,117 +316,14 @@ class _RSAVerificationContext(object):
self._hash_ctx.update(data)
def verify(self):
evp_md = self._backend._lib.EVP_get_digestbyname(
self._algorithm.name.encode("ascii"))
self._backend.openssl_assert(evp_md != self._backend._ffi.NULL)
self._verify_method(evp_md)
def _verify_pkey_ctx(self, evp_md):
pkey_ctx = self._backend._lib.EVP_PKEY_CTX_new(
self._public_key._evp_pkey, self._backend._ffi.NULL
)
self._backend.openssl_assert(pkey_ctx != self._backend._ffi.NULL)
pkey_ctx = self._backend._ffi.gc(pkey_ctx,
self._backend._lib.EVP_PKEY_CTX_free)
res = self._backend._lib.EVP_PKEY_verify_init(pkey_ctx)
self._backend.openssl_assert(res == 1)
res = self._backend._lib.EVP_PKEY_CTX_set_signature_md(
pkey_ctx, evp_md)
self._backend.openssl_assert(res > 0)
res = self._backend._lib.EVP_PKEY_CTX_set_rsa_padding(
pkey_ctx, self._padding_enum)
self._backend.openssl_assert(res > 0)
if isinstance(self._padding, PSS):
res = self._backend._lib.EVP_PKEY_CTX_set_rsa_pss_saltlen(
pkey_ctx,
_get_rsa_pss_salt_length(
self._padding,
self._public_key.key_size,
self._hash_ctx.algorithm.digest_size
)
)
self._backend.openssl_assert(res > 0)
if self._backend._lib.Cryptography_HAS_MGF1_MD:
# MGF1 MD is configurable in OpenSSL 1.0.1+
mgf1_md = self._backend._lib.EVP_get_digestbyname(
self._padding._mgf._algorithm.name.encode("ascii"))
self._backend.openssl_assert(
mgf1_md != self._backend._ffi.NULL
)
res = self._backend._lib.EVP_PKEY_CTX_set_rsa_mgf1_md(
pkey_ctx, mgf1_md
)
self._backend.openssl_assert(res > 0)
data_to_verify = self._hash_ctx.finalize()
res = self._backend._lib.EVP_PKEY_verify(
pkey_ctx,
return _rsa_sig_verify(
self._backend,
self._padding,
self._algorithm,
self._public_key,
self._signature,
len(self._signature),
data_to_verify,
len(data_to_verify)
self._hash_ctx.finalize()
)
# The previous call can return negative numbers in the event of an
# error. This is not a signature failure but we need to fail if it
# occurs.
self._backend.openssl_assert(res >= 0)
if res == 0:
errors = self._backend._consume_errors()
assert errors
raise InvalidSignature
def _verify_pkcs1(self, evp_md):
if self._hash_ctx._ctx is None:
raise AlreadyFinalized("Context has already been finalized.")
res = self._backend._lib.EVP_VerifyFinal(
self._hash_ctx._ctx._ctx,
self._signature,
len(self._signature),
self._public_key._evp_pkey
)
self._hash_ctx.finalize()
# The previous call can return negative numbers in the event of an
# error. This is not a signature failure but we need to fail if it
# occurs.
self._backend.openssl_assert(res >= 0)
if res == 0:
errors = self._backend._consume_errors()
assert errors
raise InvalidSignature
def _verify_pss(self, evp_md):
buf = self._backend._ffi.new("unsigned char[]", self._pkey_size)
res = self._backend._lib.RSA_public_decrypt(
len(self._signature),
self._signature,
buf,
self._public_key._rsa_cdata,
self._backend._lib.RSA_NO_PADDING
)
if res != self._pkey_size:
errors = self._backend._consume_errors()
assert errors
raise InvalidSignature
data_to_verify = self._hash_ctx.finalize()
res = self._backend._lib.RSA_verify_PKCS1_PSS(
self._public_key._rsa_cdata,
data_to_verify,
evp_md,
buf,
_get_rsa_pss_salt_length(
self._padding,
self._public_key.key_size,
len(data_to_verify)
)
)
if res != 1:
errors = self._backend._consume_errors()
assert errors
raise InvalidSignature
@utils.register_interface(RSAPrivateKeyWithSerialization)
@ -519,11 +333,19 @@ class _RSAPrivateKey(object):
self._rsa_cdata = rsa_cdata
self._evp_pkey = evp_pkey
self._key_size = self._backend._lib.BN_num_bits(self._rsa_cdata.n)
n = self._backend._ffi.new("BIGNUM **")
self._backend._lib.RSA_get0_key(
self._rsa_cdata, n, self._backend._ffi.NULL,
self._backend._ffi.NULL
)
self._backend.openssl_assert(n[0] != self._backend._ffi.NULL)
self._key_size = self._backend._lib.BN_num_bits(n[0])
key_size = utils.read_only_property("_key_size")
def signer(self, padding, algorithm):
_warn_sign_verify_deprecated()
_check_not_prehashed(algorithm)
return _RSASignatureContext(self._backend, self, padding, algorithm)
def decrypt(self, ciphertext, padding):
@ -534,27 +356,46 @@ class _RSAPrivateKey(object):
return _enc_dec_rsa(self._backend, self, ciphertext, padding)
def public_key(self):
ctx = self._backend._lib.RSA_new()
ctx = self._backend._lib.RSAPublicKey_dup(self._rsa_cdata)
self._backend.openssl_assert(ctx != self._backend._ffi.NULL)
ctx = self._backend._ffi.gc(ctx, self._backend._lib.RSA_free)
ctx.e = self._backend._lib.BN_dup(self._rsa_cdata.e)
ctx.n = self._backend._lib.BN_dup(self._rsa_cdata.n)
res = self._backend._lib.RSA_blinding_on(ctx, self._backend._ffi.NULL)
self._backend.openssl_assert(res == 1)
evp_pkey = self._backend._rsa_cdata_to_evp_pkey(ctx)
return _RSAPublicKey(self._backend, ctx, evp_pkey)
def private_numbers(self):
n = self._backend._ffi.new("BIGNUM **")
e = self._backend._ffi.new("BIGNUM **")
d = self._backend._ffi.new("BIGNUM **")
p = self._backend._ffi.new("BIGNUM **")
q = self._backend._ffi.new("BIGNUM **")
dmp1 = self._backend._ffi.new("BIGNUM **")
dmq1 = self._backend._ffi.new("BIGNUM **")
iqmp = self._backend._ffi.new("BIGNUM **")
self._backend._lib.RSA_get0_key(self._rsa_cdata, n, e, d)
self._backend.openssl_assert(n[0] != self._backend._ffi.NULL)
self._backend.openssl_assert(e[0] != self._backend._ffi.NULL)
self._backend.openssl_assert(d[0] != self._backend._ffi.NULL)
self._backend._lib.RSA_get0_factors(self._rsa_cdata, p, q)
self._backend.openssl_assert(p[0] != self._backend._ffi.NULL)
self._backend.openssl_assert(q[0] != self._backend._ffi.NULL)
self._backend._lib.RSA_get0_crt_params(
self._rsa_cdata, dmp1, dmq1, iqmp
)
self._backend.openssl_assert(dmp1[0] != self._backend._ffi.NULL)
self._backend.openssl_assert(dmq1[0] != self._backend._ffi.NULL)
self._backend.openssl_assert(iqmp[0] != self._backend._ffi.NULL)
return rsa.RSAPrivateNumbers(
p=self._backend._bn_to_int(self._rsa_cdata.p),
q=self._backend._bn_to_int(self._rsa_cdata.q),
d=self._backend._bn_to_int(self._rsa_cdata.d),
dmp1=self._backend._bn_to_int(self._rsa_cdata.dmp1),
dmq1=self._backend._bn_to_int(self._rsa_cdata.dmq1),
iqmp=self._backend._bn_to_int(self._rsa_cdata.iqmp),
p=self._backend._bn_to_int(p[0]),
q=self._backend._bn_to_int(q[0]),
d=self._backend._bn_to_int(d[0]),
dmp1=self._backend._bn_to_int(dmp1[0]),
dmq1=self._backend._bn_to_int(dmq1[0]),
iqmp=self._backend._bn_to_int(iqmp[0]),
public_numbers=rsa.RSAPublicNumbers(
e=self._backend._bn_to_int(self._rsa_cdata.e),
n=self._backend._bn_to_int(self._rsa_cdata.n),
e=self._backend._bn_to_int(e[0]),
n=self._backend._bn_to_int(n[0]),
)
)
@ -567,6 +408,12 @@ class _RSAPrivateKey(object):
self._rsa_cdata
)
def sign(self, data, padding, algorithm):
data, algorithm = _calculate_digest_and_algorithm(
self._backend, data, algorithm
)
return _rsa_sig_sign(self._backend, padding, algorithm, self, data)
@utils.register_interface(RSAPublicKeyWithSerialization)
class _RSAPublicKey(object):
@ -575,14 +422,22 @@ class _RSAPublicKey(object):
self._rsa_cdata = rsa_cdata
self._evp_pkey = evp_pkey
self._key_size = self._backend._lib.BN_num_bits(self._rsa_cdata.n)
n = self._backend._ffi.new("BIGNUM **")
self._backend._lib.RSA_get0_key(
self._rsa_cdata, n, self._backend._ffi.NULL,
self._backend._ffi.NULL
)
self._backend.openssl_assert(n[0] != self._backend._ffi.NULL)
self._key_size = self._backend._lib.BN_num_bits(n[0])
key_size = utils.read_only_property("_key_size")
def verifier(self, signature, padding, algorithm):
_warn_sign_verify_deprecated()
if not isinstance(signature, bytes):
raise TypeError("signature must be bytes.")
_check_not_prehashed(algorithm)
return _RSAVerificationContext(
self._backend, self, signature, padding, algorithm
)
@ -591,15 +446,31 @@ class _RSAPublicKey(object):
return _enc_dec_rsa(self._backend, self, plaintext, padding)
def public_numbers(self):
n = self._backend._ffi.new("BIGNUM **")
e = self._backend._ffi.new("BIGNUM **")
self._backend._lib.RSA_get0_key(
self._rsa_cdata, n, e, self._backend._ffi.NULL
)
self._backend.openssl_assert(n[0] != self._backend._ffi.NULL)
self._backend.openssl_assert(e[0] != self._backend._ffi.NULL)
return rsa.RSAPublicNumbers(
e=self._backend._bn_to_int(self._rsa_cdata.e),
n=self._backend._bn_to_int(self._rsa_cdata.n),
e=self._backend._bn_to_int(e[0]),
n=self._backend._bn_to_int(n[0]),
)
def public_bytes(self, encoding, format):
return self._backend._public_key_bytes(
encoding,
format,
self,
self._evp_pkey,
self._rsa_cdata
)
def verify(self, signature, data, padding, algorithm):
data, algorithm = _calculate_digest_and_algorithm(
self._backend, data, algorithm
)
return _rsa_sig_verify(
self._backend, padding, algorithm, self, signature, data
)

View file

@ -4,23 +4,42 @@
from __future__ import absolute_import, division, print_function
import six
import warnings
from cryptography import utils
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric.utils import Prehashed
def _truncate_digest(digest, order_bits):
digest_len = len(digest)
def _calculate_digest_and_algorithm(backend, data, algorithm):
if not isinstance(algorithm, Prehashed):
hash_ctx = hashes.Hash(algorithm, backend)
hash_ctx.update(data)
data = hash_ctx.finalize()
else:
algorithm = algorithm._algorithm
if 8 * digest_len > order_bits:
digest_len = (order_bits + 7) // 8
digest = digest[:digest_len]
if len(data) != algorithm.digest_size:
raise ValueError(
"The provided data must be the same length as the hash "
"algorithm's digest size."
)
if 8 * digest_len > order_bits:
rshift = 8 - (order_bits & 0x7)
assert 0 < rshift < 8
return (data, algorithm)
mask = 0xFF >> rshift << rshift
# Set the bottom rshift bits to 0
digest = digest[:-1] + six.int2byte(six.indexbytes(digest, -1) & mask)
def _check_not_prehashed(signature_algorithm):
if isinstance(signature_algorithm, Prehashed):
raise TypeError(
"Prehashed is only supported in the sign and verify methods. "
"It cannot be used with signer or verifier."
)
return digest
def _warn_sign_verify_deprecated():
warnings.warn(
"signer and verifier have been deprecated. Please use sign "
"and verify instead.",
utils.PersistentlyDeprecated,
stacklevel=3
)

View file

@ -0,0 +1,79 @@
# 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.hazmat.primitives.asymmetric.x25519 import (
X25519PrivateKey, X25519PublicKey
)
@utils.register_interface(X25519PublicKey)
class _X25519PublicKey(object):
def __init__(self, backend, evp_pkey):
self._backend = backend
self._evp_pkey = evp_pkey
def public_bytes(self):
ucharpp = self._backend._ffi.new("unsigned char **")
res = self._backend._lib.EVP_PKEY_get1_tls_encodedpoint(
self._evp_pkey, ucharpp
)
self._backend.openssl_assert(res == 32)
self._backend.openssl_assert(ucharpp[0] != self._backend._ffi.NULL)
data = self._backend._ffi.gc(
ucharpp[0], self._backend._lib.OPENSSL_free
)
return self._backend._ffi.buffer(data, res)[:]
@utils.register_interface(X25519PrivateKey)
class _X25519PrivateKey(object):
def __init__(self, backend, evp_pkey):
self._backend = backend
self._evp_pkey = evp_pkey
def public_key(self):
bio = self._backend._create_mem_bio_gc()
res = self._backend._lib.i2d_PUBKEY_bio(bio, self._evp_pkey)
self._backend.openssl_assert(res == 1)
evp_pkey = self._backend._lib.d2i_PUBKEY_bio(
bio, self._backend._ffi.NULL
)
self._backend.openssl_assert(evp_pkey != self._backend._ffi.NULL)
evp_pkey = self._backend._ffi.gc(
evp_pkey, self._backend._lib.EVP_PKEY_free
)
return _X25519PublicKey(self._backend, evp_pkey)
def exchange(self, peer_public_key):
if not isinstance(peer_public_key, X25519PublicKey):
raise TypeError("peer_public_key must be X25519PublicKey.")
ctx = self._backend._lib.EVP_PKEY_CTX_new(
self._evp_pkey, self._backend._ffi.NULL
)
self._backend.openssl_assert(ctx != self._backend._ffi.NULL)
ctx = self._backend._ffi.gc(ctx, self._backend._lib.EVP_PKEY_CTX_free)
res = self._backend._lib.EVP_PKEY_derive_init(ctx)
self._backend.openssl_assert(res == 1)
res = self._backend._lib.EVP_PKEY_derive_set_peer(
ctx, peer_public_key._evp_pkey
)
self._backend.openssl_assert(res == 1)
keylen = self._backend._ffi.new("size_t *")
res = self._backend._lib.EVP_PKEY_derive(
ctx, self._backend._ffi.NULL, keylen
)
self._backend.openssl_assert(res == 1)
self._backend.openssl_assert(keylen[0] > 0)
buf = self._backend._ffi.new("unsigned char[]", keylen[0])
res = self._backend._lib.EVP_PKEY_derive(ctx, buf, keylen)
if res != 1:
raise ValueError(
"Null shared key derived from public/private pair."
)
return self._backend._ffi.buffer(buf, keylen[0])[:]

View file

@ -1,5 +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

View file

@ -1,15 +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
from cryptography.hazmat.bindings._commoncrypto import ffi, lib
class Binding(object):
"""
CommonCrypto API wrapper.
"""
lib = lib
ffi = ffi

View file

@ -4,363 +4,78 @@
from __future__ import absolute_import, division, print_function
# This is a temporary copy of all the CONDITIONAL_NAMES from _cffi_src so
# we can loop over them and delete them at runtime. It will be removed when
# cffi supports #if in cdef
CONDITIONAL_NAMES = {
"Cryptography_HAS_AES_WRAP": [
"AES_wrap_key",
"AES_unwrap_key",
],
"Cryptography_HAS_CMAC": [
"CMAC_CTX_new",
"CMAC_Init",
"CMAC_Update",
"CMAC_Final",
"CMAC_CTX_copy",
"CMAC_CTX_free",
],
"Cryptography_HAS_CMS": [
"BIO_new_CMS",
"i2d_CMS_bio_stream",
"PEM_write_bio_CMS_stream",
"CMS_final",
"CMS_sign",
"CMS_verify",
"CMS_encrypt",
"CMS_decrypt",
"CMS_add1_signer",
"CMS_TEXT",
"CMS_NOCERTS",
"CMS_NO_CONTENT_VERIFY",
"CMS_NO_ATTR_VERIFY",
"CMS_NOSIGS",
"CMS_NOINTERN",
"CMS_NO_SIGNER_CERT_VERIFY",
"CMS_NOVERIFY",
"CMS_DETACHED",
"CMS_BINARY",
"CMS_NOATTR",
"CMS_NOSMIMECAP",
"CMS_NOOLDMIMETYPE",
"CMS_CRLFEOL",
"CMS_STREAM",
"CMS_NOCRL",
"CMS_PARTIAL",
"CMS_REUSE_DIGEST",
"CMS_USE_KEYID",
"CMS_DEBUG_DECRYPT",
],
"Cryptography_HAS_CMS_BIO_FUNCTIONS": [
"BIO_new_CMS",
"i2d_CMS_bio_stream",
"PEM_write_bio_CMS_stream",
],
"Cryptography_HAS_EC": [
"OPENSSL_EC_NAMED_CURVE",
"EC_GROUP_new",
"EC_GROUP_free",
"EC_GROUP_clear_free",
"EC_GROUP_new_curve_GFp",
"EC_GROUP_new_by_curve_name",
"EC_GROUP_set_curve_GFp",
"EC_GROUP_get_curve_GFp",
"EC_GROUP_method_of",
"EC_GROUP_get0_generator",
"EC_GROUP_get_curve_name",
"EC_GROUP_get_degree",
"EC_GROUP_set_asn1_flag",
"EC_GROUP_set_point_conversion_form",
"EC_KEY_new",
"EC_KEY_free",
"EC_get_builtin_curves",
"EC_KEY_new_by_curve_name",
"EC_KEY_copy",
"EC_KEY_dup",
"EC_KEY_up_ref",
"EC_KEY_set_group",
"EC_KEY_get0_private_key",
"EC_KEY_set_private_key",
"EC_KEY_set_public_key",
"EC_KEY_get_enc_flags",
"EC_KEY_set_enc_flags",
"EC_KEY_set_conv_form",
"EC_KEY_get_key_method_data",
"EC_KEY_insert_key_method_data",
"EC_KEY_set_asn1_flag",
"EC_KEY_precompute_mult",
"EC_KEY_generate_key",
"EC_KEY_check_key",
"EC_POINT_new",
"EC_POINT_free",
"EC_POINT_clear_free",
"EC_POINT_copy",
"EC_POINT_dup",
"EC_POINT_method_of",
"EC_POINT_set_to_infinity",
"EC_POINT_set_Jprojective_coordinates_GFp",
"EC_POINT_get_Jprojective_coordinates_GFp",
"EC_POINT_set_affine_coordinates_GFp",
"EC_POINT_get_affine_coordinates_GFp",
"EC_POINT_set_compressed_coordinates_GFp",
"EC_POINT_point2oct",
"EC_POINT_oct2point",
"EC_POINT_point2bn",
"EC_POINT_bn2point",
"EC_POINT_point2hex",
"EC_POINT_hex2point",
"EC_POINT_add",
"EC_POINT_dbl",
"EC_POINT_invert",
"EC_POINT_is_at_infinity",
"EC_POINT_is_on_curve",
"EC_POINT_cmp",
"EC_POINT_make_affine",
"EC_POINTs_make_affine",
"EC_POINTs_mul",
"EC_POINT_mul",
"EC_GROUP_precompute_mult",
"EC_GROUP_have_precompute_mult",
"EC_GFp_simple_method",
"EC_GFp_mont_method",
"EC_GFp_nist_method",
"EC_METHOD_get_field_type",
"EVP_PKEY_assign_EC_KEY",
"EVP_PKEY_get1_EC_KEY",
"EVP_PKEY_set1_EC_KEY",
"PEM_write_bio_ECPrivateKey",
"i2d_EC_PUBKEY",
"d2i_EC_PUBKEY",
"d2i_EC_PUBKEY_bio",
"i2d_EC_PUBKEY_bio",
"d2i_ECPrivateKey",
"d2i_ECPrivateKey_bio",
"i2d_ECPrivateKey",
"i2d_ECPrivateKey_bio",
"i2o_ECPublicKey",
"o2i_ECPublicKey",
"SSL_CTX_set_tmp_ecdh",
"POINT_CONVERSION_COMPRESSED",
"POINT_CONVERSION_UNCOMPRESSED",
"POINT_CONVERSION_HYBRID",
],
"Cryptography_HAS_EC_1_0_1": [
"EC_KEY_get_flags",
"EC_KEY_set_flags",
"EC_KEY_clear_flags",
"EC_KEY_set_public_key_affine_coordinates",
],
"Cryptography_HAS_EC2M": [
"EC_GF2m_simple_method",
def cryptography_has_ec2m():
return [
"EC_POINT_set_affine_coordinates_GF2m",
"EC_POINT_get_affine_coordinates_GF2m",
"EC_POINT_set_compressed_coordinates_GF2m",
"EC_GROUP_set_curve_GF2m",
"EC_GROUP_get_curve_GF2m",
"EC_GROUP_new_curve_GF2m",
],
]
"Cryptography_HAS_EC_1_0_2": [
def cryptography_has_ec_1_0_2():
return [
"EC_curve_nid2nist",
],
"Cryptography_HAS_ECDH": [
"ECDH_compute_key",
"ECDH_get_ex_new_index",
"ECDH_set_ex_data",
"ECDH_get_ex_data",
],
"Cryptography_HAS_ECDSA": [
"ECDSA_SIG_new",
"ECDSA_SIG_free",
"i2d_ECDSA_SIG",
"d2i_ECDSA_SIG",
"ECDSA_do_sign",
"ECDSA_do_sign_ex",
"ECDSA_do_verify",
"ECDSA_sign_setup",
"ECDSA_sign",
"ECDSA_sign_ex",
"ECDSA_verify",
"ECDSA_size",
"ECDSA_OpenSSL",
"ECDSA_set_default_method",
"ECDSA_get_default_method",
"ECDSA_set_method",
"ECDSA_get_ex_new_index",
"ECDSA_set_ex_data",
"ECDSA_get_ex_data",
],
"Cryptography_HAS_ENGINE_CRYPTODEV": [
"ENGINE_load_cryptodev"
],
"Cryptography_HAS_REMOVE_THREAD_STATE": [
"ERR_remove_thread_state"
],
"Cryptography_HAS_098H_ERROR_CODES": [
"ASN1_F_B64_READ_ASN1",
"ASN1_F_B64_WRITE_ASN1",
"ASN1_F_SMIME_READ_ASN1",
"ASN1_F_SMIME_TEXT",
"ASN1_R_NO_CONTENT_TYPE",
"ASN1_R_NO_MULTIPART_BODY_FAILURE",
"ASN1_R_NO_MULTIPART_BOUNDARY",
],
"Cryptography_HAS_098C_CAMELLIA_CODES": [
"EVP_F_CAMELLIA_INIT_KEY",
"EVP_R_CAMELLIA_KEY_SETUP_FAILED"
],
"Cryptography_HAS_EC_CODES": [
"EC_R_UNKNOWN_GROUP",
"EC_F_EC_GROUP_NEW_BY_CURVE_NAME"
],
"Cryptography_HAS_RSA_R_PKCS_DECODING_ERROR": [
]
def cryptography_has_set_ecdh_auto():
return [
"SSL_CTX_set_ecdh_auto",
]
def cryptography_has_rsa_r_pkcs_decoding_error():
return [
"RSA_R_PKCS_DECODING_ERROR"
],
"Cryptography_HAS_GCM": [
"EVP_CTRL_GCM_GET_TAG",
"EVP_CTRL_GCM_SET_TAG",
"EVP_CTRL_GCM_SET_IVLEN",
],
"Cryptography_HAS_PBKDF2_HMAC": [
"PKCS5_PBKDF2_HMAC"
],
"Cryptography_HAS_PKEY_CTX": [
"EVP_PKEY_CTX_new",
"EVP_PKEY_CTX_new_id",
"EVP_PKEY_CTX_dup",
"EVP_PKEY_CTX_free",
"EVP_PKEY_sign",
"EVP_PKEY_sign_init",
"EVP_PKEY_verify",
"EVP_PKEY_verify_init",
"Cryptography_EVP_PKEY_encrypt",
"EVP_PKEY_encrypt_init",
"Cryptography_EVP_PKEY_decrypt",
"EVP_PKEY_decrypt_init",
"EVP_PKEY_CTX_set_signature_md",
"EVP_PKEY_id",
"EVP_PKEY_CTX_set_rsa_padding",
"EVP_PKEY_CTX_set_rsa_pss_saltlen",
],
"Cryptography_HAS_ECDSA_SHA2_NIDS": [
"NID_ecdsa_with_SHA224",
"NID_ecdsa_with_SHA256",
"NID_ecdsa_with_SHA384",
"NID_ecdsa_with_SHA512",
],
"Cryptography_HAS_EGD": [
"RAND_egd",
"RAND_egd_bytes",
"RAND_query_egd_bytes",
],
"Cryptography_HAS_PSS_PADDING": [
"RSA_PKCS1_PSS_PADDING",
],
"Cryptography_HAS_MGF1_MD": [
"EVP_PKEY_CTX_set_rsa_mgf1_md",
],
"Cryptography_HAS_TLSv1_1": [
"SSL_OP_NO_TLSv1_1",
"TLSv1_1_method",
"TLSv1_1_server_method",
"TLSv1_1_client_method",
],
]
"Cryptography_HAS_TLSv1_2": [
"SSL_OP_NO_TLSv1_2",
"TLSv1_2_method",
"TLSv1_2_server_method",
"TLSv1_2_client_method",
],
"Cryptography_HAS_SSL3_METHOD": [
def cryptography_has_rsa_oaep_md():
return [
"EVP_PKEY_CTX_set_rsa_oaep_md",
]
def cryptography_has_rsa_oaep_label():
return [
"EVP_PKEY_CTX_set0_rsa_oaep_label",
]
def cryptography_has_ssl3_method():
return [
"SSLv3_method",
"SSLv3_client_method",
"SSLv3_server_method",
],
]
"Cryptography_HAS_TLSEXT_HOSTNAME": [
"SSL_set_tlsext_host_name",
"SSL_get_servername",
"SSL_CTX_set_tlsext_servername_callback",
],
"Cryptography_HAS_TLSEXT_STATUS_REQ_CB": [
"SSL_CTX_set_tlsext_status_cb",
"SSL_CTX_set_tlsext_status_arg"
],
"Cryptography_HAS_STATUS_REQ_OCSP_RESP": [
"SSL_set_tlsext_status_ocsp_resp",
"SSL_get_tlsext_status_ocsp_resp",
],
"Cryptography_HAS_TLSEXT_STATUS_REQ_TYPE": [
"SSL_set_tlsext_status_type",
],
"Cryptography_HAS_RELEASE_BUFFERS": [
"SSL_MODE_RELEASE_BUFFERS",
],
"Cryptography_HAS_OP_NO_COMPRESSION": [
"SSL_OP_NO_COMPRESSION",
],
"Cryptography_HAS_SSL_OP_MSIE_SSLV2_RSA_PADDING": [
"SSL_OP_MSIE_SSLV2_RSA_PADDING",
],
"Cryptography_HAS_SSL_OP_NO_TICKET": [
"SSL_OP_NO_TICKET",
],
"Cryptography_HAS_SSL_SET_SSL_CTX": [
"SSL_set_SSL_CTX",
"TLSEXT_NAMETYPE_host_name",
],
"Cryptography_HAS_NETBSD_D1_METH": [
"DTLSv1_method",
],
"Cryptography_HAS_NEXTPROTONEG": [
"SSL_CTX_set_next_protos_advertised_cb",
"SSL_CTX_set_next_proto_select_cb",
"SSL_select_next_proto",
"SSL_get0_next_proto_negotiated",
],
"Cryptography_HAS_SECURE_RENEGOTIATION": [
"SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION",
"SSL_OP_LEGACY_SERVER_CONNECT",
"SSL_get_secure_renegotiation_support",
],
"Cryptography_HAS_ALPN": [
def cryptography_has_alpn():
return [
"SSL_CTX_set_alpn_protos",
"SSL_set_alpn_protos",
"SSL_CTX_set_alpn_select_cb",
"SSL_get0_alpn_selected",
],
]
"Cryptography_HAS_COMPRESSION": [
def cryptography_has_compression():
return [
"SSL_get_current_compression",
"SSL_get_current_expansion",
"SSL_COMP_get_name",
],
]
"Cryptography_HAS_GET_SERVER_TMP_KEY": [
def cryptography_has_get_server_tmp_key():
return [
"SSL_get_server_tmp_key",
],
]
"Cryptography_HAS_SSL_CTX_SET_CLIENT_CERT_ENGINE": [
"SSL_CTX_set_client_cert_engine",
],
"Cryptography_HAS_102_VERIFICATION_ERROR_CODES": [
def cryptography_has_102_verification_error_codes():
return [
'X509_V_ERR_SUITE_B_INVALID_VERSION',
'X509_V_ERR_SUITE_B_INVALID_ALGORITHM',
'X509_V_ERR_SUITE_B_INVALID_CURVE',
@ -370,8 +85,11 @@ CONDITIONAL_NAMES = {
'X509_V_ERR_HOSTNAME_MISMATCH',
'X509_V_ERR_EMAIL_MISMATCH',
'X509_V_ERR_IP_ADDRESS_MISMATCH'
],
"Cryptography_HAS_102_VERIFICATION_PARAMS": [
]
def cryptography_has_102_verification_params():
return [
"X509_V_FLAG_SUITEB_128_LOS_ONLY",
"X509_V_FLAG_SUITEB_192_LOS",
"X509_V_FLAG_SUITEB_128_LOS",
@ -380,35 +98,269 @@ CONDITIONAL_NAMES = {
"X509_VERIFY_PARAM_set1_ip",
"X509_VERIFY_PARAM_set1_ip_asc",
"X509_VERIFY_PARAM_set_hostflags",
],
"Cryptography_HAS_X509_V_FLAG_TRUSTED_FIRST": [
"SSL_get0_param",
"X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT",
"X509_CHECK_FLAG_NO_WILDCARDS",
"X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS",
"X509_CHECK_FLAG_MULTI_LABEL_WILDCARDS",
"X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS"
]
def cryptography_has_110_verification_params():
return [
"X509_CHECK_FLAG_NEVER_CHECK_SUBJECT"
]
def cryptography_has_x509_v_flag_trusted_first():
return [
"X509_V_FLAG_TRUSTED_FIRST",
],
"Cryptography_HAS_X509_V_FLAG_PARTIAL_CHAIN": [
]
def cryptography_has_x509_v_flag_partial_chain():
return [
"X509_V_FLAG_PARTIAL_CHAIN",
],
"Cryptography_HAS_100_VERIFICATION_ERROR_CODES": [
'X509_V_ERR_DIFFERENT_CRL_SCOPE',
'X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE',
'X509_V_ERR_UNNESTED_RESOURCE',
'X509_V_ERR_PERMITTED_VIOLATION',
'X509_V_ERR_EXCLUDED_VIOLATION',
'X509_V_ERR_SUBTREE_MINMAX',
'X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE',
'X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX',
'X509_V_ERR_UNSUPPORTED_NAME_SYNTAX',
'X509_V_ERR_CRL_PATH_VALIDATION_ERROR',
],
"Cryptography_HAS_100_VERIFICATION_PARAMS": [
"Cryptography_HAS_100_VERIFICATION_PARAMS",
"X509_V_FLAG_EXTENDED_CRL_SUPPORT",
"X509_V_FLAG_USE_DELTAS",
],
"Cryptography_HAS_X509_V_FLAG_CHECK_SS_SIGNATURE": [
"X509_V_FLAG_CHECK_SS_SIGNATURE",
],
"Cryptography_HAS_SET_CERT_CB": [
]
def cryptography_has_set_cert_cb():
return [
"SSL_CTX_set_cert_cb",
"SSL_set_cert_cb",
],
]
def cryptography_has_ssl_st():
return [
"SSL_ST_BEFORE",
"SSL_ST_OK",
"SSL_ST_INIT",
"SSL_ST_RENEGOTIATE",
]
def cryptography_has_tls_st():
return [
"TLS_ST_BEFORE",
"TLS_ST_OK",
]
def cryptography_has_locking_callbacks():
return [
"CRYPTO_LOCK",
"CRYPTO_UNLOCK",
"CRYPTO_READ",
"CRYPTO_LOCK_SSL",
"CRYPTO_lock",
"Cryptography_setup_ssl_threads",
]
def cryptography_has_scrypt():
return [
"EVP_PBE_scrypt",
]
def cryptography_has_generic_dtls_method():
return [
"DTLS_method",
"DTLS_server_method",
"DTLS_client_method",
"SSL_OP_NO_DTLSv1",
"SSL_OP_NO_DTLSv1_2",
"DTLS_set_link_mtu",
"DTLS_get_link_min_mtu",
]
def cryptography_has_evp_pkey_dhx():
return [
"EVP_PKEY_DHX",
]
def cryptography_has_mem_functions():
return [
"Cryptography_CRYPTO_set_mem_functions",
]
def cryptography_has_sct():
return [
"SCT_get_version",
"SCT_get_log_entry_type",
"SCT_get0_log_id",
"SCT_get0_signature",
"SCT_get_timestamp",
"SCT_set_source",
"sk_SCT_num",
"sk_SCT_value",
"SCT_LIST_free",
"sk_SCT_push",
"sk_SCT_new_null",
"SCT_new",
"SCT_set1_log_id",
"SCT_set_timestamp",
"SCT_set_version",
"SCT_set_log_entry_type",
]
def cryptography_has_x509_store_ctx_get_issuer():
return [
"X509_STORE_get_get_issuer",
"X509_STORE_set_get_issuer",
]
def cryptography_has_x25519():
return [
"EVP_PKEY_X25519",
"NID_X25519",
]
def cryptography_has_ed25519():
return [
"NID_ED25519",
]
def cryptography_has_oneshot_evp_digest_sign_verify():
return [
"EVP_DigestSign",
"EVP_DigestVerify",
]
def cryptography_has_evp_pkey_get_set_tls_encodedpoint():
return [
"EVP_PKEY_get1_tls_encodedpoint",
"EVP_PKEY_set1_tls_encodedpoint",
]
def cryptography_has_fips():
return [
"FIPS_set_mode",
"FIPS_mode",
]
def cryptography_has_ssl_sigalgs():
return [
"SSL_CTX_set1_sigalgs_list",
"SSL_get_sigalgs",
]
def cryptography_has_psk():
return [
"SSL_CTX_use_psk_identity_hint",
"SSL_CTX_set_psk_server_callback",
"SSL_CTX_set_psk_client_callback",
]
def cryptography_has_custom_ext():
return [
"SSL_CTX_add_client_custom_ext",
"SSL_CTX_add_server_custom_ext",
"SSL_extension_supported",
]
def cryptography_has_openssl_cleanup():
return [
"OPENSSL_cleanup",
]
def cryptography_has_cipher_details():
return [
"SSL_CIPHER_is_aead",
"SSL_CIPHER_get_cipher_nid",
"SSL_CIPHER_get_digest_nid",
"SSL_CIPHER_get_kx_nid",
"SSL_CIPHER_get_auth_nid",
]
def cryptography_has_tlsv13():
return [
"SSL_OP_NO_TLSv1_3",
"SSL_VERIFY_POST_HANDSHAKE",
"SSL_CTX_set_ciphersuites",
"SSL_verify_client_post_handshake",
"SSL_CTX_set_post_handshake_auth",
"SSL_set_post_handshake_auth",
]
# This is a mapping of
# {condition: function-returning-names-dependent-on-that-condition} so we can
# loop over them and delete unsupported names at runtime. It will be removed
# when cffi supports #if in cdef. We use functions instead of just a dict of
# lists so we can use coverage to measure which are used.
CONDITIONAL_NAMES = {
"Cryptography_HAS_EC2M": cryptography_has_ec2m,
"Cryptography_HAS_EC_1_0_2": cryptography_has_ec_1_0_2,
"Cryptography_HAS_SET_ECDH_AUTO": cryptography_has_set_ecdh_auto,
"Cryptography_HAS_RSA_R_PKCS_DECODING_ERROR": (
cryptography_has_rsa_r_pkcs_decoding_error
),
"Cryptography_HAS_RSA_OAEP_MD": cryptography_has_rsa_oaep_md,
"Cryptography_HAS_RSA_OAEP_LABEL": cryptography_has_rsa_oaep_label,
"Cryptography_HAS_SSL3_METHOD": cryptography_has_ssl3_method,
"Cryptography_HAS_ALPN": cryptography_has_alpn,
"Cryptography_HAS_COMPRESSION": cryptography_has_compression,
"Cryptography_HAS_GET_SERVER_TMP_KEY": cryptography_has_get_server_tmp_key,
"Cryptography_HAS_102_VERIFICATION_ERROR_CODES": (
cryptography_has_102_verification_error_codes
),
"Cryptography_HAS_102_VERIFICATION_PARAMS": (
cryptography_has_102_verification_params
),
"Cryptography_HAS_110_VERIFICATION_PARAMS": (
cryptography_has_110_verification_params
),
"Cryptography_HAS_X509_V_FLAG_TRUSTED_FIRST": (
cryptography_has_x509_v_flag_trusted_first
),
"Cryptography_HAS_X509_V_FLAG_PARTIAL_CHAIN": (
cryptography_has_x509_v_flag_partial_chain
),
"Cryptography_HAS_SET_CERT_CB": cryptography_has_set_cert_cb,
"Cryptography_HAS_SSL_ST": cryptography_has_ssl_st,
"Cryptography_HAS_TLS_ST": cryptography_has_tls_st,
"Cryptography_HAS_LOCKING_CALLBACKS": cryptography_has_locking_callbacks,
"Cryptography_HAS_SCRYPT": cryptography_has_scrypt,
"Cryptography_HAS_GENERIC_DTLS_METHOD": (
cryptography_has_generic_dtls_method
),
"Cryptography_HAS_EVP_PKEY_DHX": cryptography_has_evp_pkey_dhx,
"Cryptography_HAS_MEM_FUNCTIONS": cryptography_has_mem_functions,
"Cryptography_HAS_SCT": cryptography_has_sct,
"Cryptography_HAS_X509_STORE_CTX_GET_ISSUER": (
cryptography_has_x509_store_ctx_get_issuer
),
"Cryptography_HAS_X25519": cryptography_has_x25519,
"Cryptography_HAS_ED25519": cryptography_has_ed25519,
"Cryptography_HAS_ONESHOT_EVP_DIGEST_SIGN_VERIFY": (
cryptography_has_oneshot_evp_digest_sign_verify
),
"Cryptography_HAS_EVP_PKEY_get_set_tls_encodedpoint": (
cryptography_has_evp_pkey_get_set_tls_encodedpoint
),
"Cryptography_HAS_FIPS": cryptography_has_fips,
"Cryptography_HAS_SIGALGS": cryptography_has_ssl_sigalgs,
"Cryptography_HAS_PSK": cryptography_has_psk,
"Cryptography_HAS_CUSTOM_EXT": cryptography_has_custom_ext,
"Cryptography_HAS_OPENSSL_CLEANUP": cryptography_has_openssl_cleanup,
"Cryptography_HAS_CIPHER_DETAILS": cryptography_has_cipher_details,
"Cryptography_HAS_TLSv1_3": cryptography_has_tlsv13,
}

View file

@ -5,17 +5,34 @@
from __future__ import absolute_import, division, print_function
import collections
import os
import threading
import types
import warnings
from cryptography import utils
from cryptography.exceptions import InternalError
from cryptography.hazmat.bindings._openssl import ffi, lib
from cryptography.hazmat.bindings.openssl._conditional import CONDITIONAL_NAMES
_OpenSSLError = collections.namedtuple("_OpenSSLError",
["code", "lib", "func", "reason"])
_OpenSSLErrorWithText = collections.namedtuple(
"_OpenSSLErrorWithText", ["code", "lib", "func", "reason", "reason_text"]
)
class _OpenSSLError(object):
def __init__(self, code, lib, func, reason):
self._code = code
self._lib = lib
self._func = func
self._reason = reason
def _lib_reason_match(self, lib, reason):
return lib == self.lib and reason == self.reason
code = utils.read_only_property("_code")
lib = utils.read_only_property("_lib")
func = utils.read_only_property("_func")
reason = utils.read_only_property("_reason")
def _consume_errors(lib):
@ -30,60 +47,44 @@ def _consume_errors(lib):
err_reason = lib.ERR_GET_REASON(code)
errors.append(_OpenSSLError(code, err_lib, err_func, err_reason))
return errors
def _openssl_assert(lib, ok):
if not ok:
errors = _consume_errors(lib)
errors_with_text = []
for err in errors:
buf = ffi.new("char[]", 256)
lib.ERR_error_string_n(err.code, buf, len(buf))
err_text_reason = ffi.string(buf)
errors_with_text.append(
_OpenSSLErrorWithText(
err.code, err.lib, err.func, err.reason, err_text_reason
)
)
raise InternalError(
"Unknown OpenSSL error. Please file an issue at https://github.com"
"/pyca/cryptography/issues with information on how to reproduce "
"this. ({0!r})".format(errors),
errors
"Unknown OpenSSL error. This error is commonly encountered when "
"another library is not cleaning up the OpenSSL error stack. If "
"you are using cryptography with another library that uses "
"OpenSSL try disabling it before reporting a bug. Otherwise "
"please file an issue at https://github.com/pyca/cryptography/"
"issues with information on how to reproduce "
"this. ({0!r})".format(errors_with_text),
errors_with_text
)
def ffi_callback(signature, name, **kwargs):
"""Callback dispatcher
The ffi_callback() dispatcher keeps callbacks compatible between dynamic
and static callbacks.
"""
def wrapper(func):
if lib.Cryptography_STATIC_CALLBACKS:
# def_extern() returns a decorator that sets the internal
# function pointer and returns the original function unmodified.
ffi.def_extern(name=name, **kwargs)(func)
callback = getattr(lib, name)
else:
# callback() wraps the function in a cdata function.
callback = ffi.callback(signature, **kwargs)(func)
return callback
return wrapper
@ffi_callback("int (*)(unsigned char *, int)",
name="Cryptography_rand_bytes",
error=-1)
def _osrandom_rand_bytes(buf, size):
signed = ffi.cast("char *", buf)
result = os.urandom(size)
signed[0:size] = result
return 1
@ffi_callback("int (*)(void)", name="Cryptography_rand_status")
def _osrandom_rand_status():
return 1
def build_conditional_library(lib, conditional_names):
conditional_lib = types.ModuleType("lib")
conditional_lib._original_lib = lib
excluded_names = set()
for condition, names in conditional_names.items():
for condition, names_cb in conditional_names.items():
if not getattr(lib, condition):
excluded_names |= set(names)
excluded_names.update(names_cb())
for attr in dir(lib):
if attr not in excluded_names:
@ -99,47 +100,24 @@ class Binding(object):
lib = None
ffi = ffi
_lib_loaded = False
_locks = None
_lock_cb_handle = None
_init_lock = threading.Lock()
_lock_init_lock = threading.Lock()
_osrandom_engine_id = ffi.new("const char[]", b"osrandom")
_osrandom_engine_name = ffi.new("const char[]", b"osrandom_engine")
_osrandom_method = ffi.new(
"RAND_METHOD *",
dict(bytes=_osrandom_rand_bytes,
pseudorand=_osrandom_rand_bytes,
status=_osrandom_rand_status)
)
def __init__(self):
self._ensure_ffi_initialized()
@classmethod
def _register_osrandom_engine(cls):
_openssl_assert(cls.lib, cls.lib.ERR_peek_error() == 0)
engine = cls.lib.ENGINE_new()
_openssl_assert(cls.lib, engine != cls.ffi.NULL)
try:
result = cls.lib.ENGINE_set_id(engine, cls._osrandom_engine_id)
_openssl_assert(cls.lib, result == 1)
result = cls.lib.ENGINE_set_name(engine, cls._osrandom_engine_name)
_openssl_assert(cls.lib, result == 1)
result = cls.lib.ENGINE_set_RAND(engine, cls._osrandom_method)
_openssl_assert(cls.lib, result == 1)
result = cls.lib.ENGINE_add(engine)
if result != 1:
errors = _consume_errors(cls.lib)
_openssl_assert(
cls.lib,
errors[0].reason == cls.lib.ENGINE_R_CONFLICTING_ENGINE_ID
)
finally:
result = cls.lib.ENGINE_free(engine)
_openssl_assert(cls.lib, result == 1)
# Clear any errors extant in the queue before we start. In many
# scenarios other things may be interacting with OpenSSL in the same
# process space and it has proven untenable to assume that they will
# reliably clear the error queue. Once we clear it here we will
# error on any subsequent unexpected item in the stack.
cls.lib.ERR_clear_error()
cls._osrandom_engine_id = cls.lib.Cryptography_osrandom_engine_id
cls._osrandom_engine_name = cls.lib.Cryptography_osrandom_engine_name
result = cls.lib.Cryptography_add_osrandom_engine()
_openssl_assert(cls.lib, result in (1, 2))
@classmethod
def _ensure_ffi_initialized(cls):
@ -159,42 +137,31 @@ class Binding(object):
def init_static_locks(cls):
with cls._lock_init_lock:
cls._ensure_ffi_initialized()
if not cls._lock_cb_handle:
wrapper = ffi_callback(
"void(int, int, const char *, int)",
name="Cryptography_locking_cb",
)
cls._lock_cb_handle = wrapper(cls._lock_cb)
# Use Python's implementation if available, importing _ssl triggers
# the setup for this.
__import__("_ssl")
if cls.lib.CRYPTO_get_locking_callback() != cls.ffi.NULL:
if (not cls.lib.Cryptography_HAS_LOCKING_CALLBACKS or
cls.lib.CRYPTO_get_locking_callback() != cls.ffi.NULL):
return
# If nothing else has setup a locking callback already, we set up
# our own
num_locks = cls.lib.CRYPTO_num_locks()
cls._locks = [threading.Lock() for n in range(num_locks)]
res = lib.Cryptography_setup_ssl_threads()
_openssl_assert(cls.lib, res == 1)
cls.lib.CRYPTO_set_locking_callback(cls._lock_cb_handle)
@classmethod
def _lock_cb(cls, mode, n, file, line):
lock = cls._locks[n]
if mode & cls.lib.CRYPTO_LOCK:
lock.acquire()
elif mode & cls.lib.CRYPTO_UNLOCK:
lock.release()
else:
raise RuntimeError(
"Unknown lock mode {0}: lock={1}, file={2}, line={3}.".format(
mode, n, file, line
)
)
def _verify_openssl_version(lib):
if (
lib.CRYPTOGRAPHY_OPENSSL_LESS_THAN_102 and
not lib.CRYPTOGRAPHY_IS_LIBRESSL
):
warnings.warn(
"OpenSSL version 1.0.1 is no longer supported by the OpenSSL "
"project, please upgrade. A future version of cryptography will "
"drop support for it.",
utils.CryptographyDeprecationWarning
)
# OpenSSL is not thread safe until the locks are initialized. We call this
@ -204,10 +171,4 @@ class Binding(object):
# is per module so this approach will not work.
Binding.init_static_locks()
if Binding.lib.SSLeay() < 0x10001000:
warnings.warn(
"OpenSSL versions less than 1.0.1 are no longer supported by the "
"OpenSSL project, please upgrade. A future version of cryptography "
"will drop support for these versions.",
DeprecationWarning
)
_verify_openssl_version(Binding.lib)

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",
]

Some files were not shown because too many files have changed in this diff Show more