diff options
Diffstat (limited to 'src')
142 files changed, 15880 insertions, 8518 deletions
diff --git a/src/_cffi_src/build_commoncrypto.py b/src/_cffi_src/build_commoncrypto.py deleted file mode 100644 index 1c2692a7..00000000 --- a/src/_cffi_src/build_commoncrypto.py +++ /dev/null @@ -1,29 +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 _cffi_src.utils import build_ffi_for_binding - - -ffi = build_ffi_for_binding( - module_name="_commoncrypto", - module_prefix="_cffi_src.commoncrypto.", - modules=[ - "cf", - "common_digest", - "common_hmac", - "common_key_derivation", - "common_cryptor", - "common_symmetric_key_wrap", - "secimport", - "secitem", - "seckey", - "seckeychain", - "sectransform", - ], - extra_link_args=[ - "-framework", "Security", "-framework", "CoreFoundation" - ], -) diff --git a/src/_cffi_src/build_constant_time.py b/src/_cffi_src/build_constant_time.py deleted file mode 100644 index 6d9a8f54..00000000 --- a/src/_cffi_src/build_constant_time.py +++ /dev/null @@ -1,28 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import absolute_import, division, print_function - -import os -import sys - -from _cffi_src.utils import build_ffi, extra_link_args - - -with open(os.path.join( - os.path.dirname(__file__), "hazmat_src/constant_time.h" -)) as f: - types = f.read() - -with open(os.path.join( - os.path.dirname(__file__), "hazmat_src/constant_time.c" -)) as f: - functions = f.read() - -ffi = build_ffi( - module_name="_constant_time", - cdef_source=types, - verify_source=functions, - extra_link_args=extra_link_args(sys.platform), -) diff --git a/src/_cffi_src/build_openssl.py b/src/_cffi_src/build_openssl.py index dac3e4d8..a09d6d8e 100644 --- a/src/_cffi_src/build_openssl.py +++ b/src/_cffi_src/build_openssl.py @@ -6,66 +6,83 @@ from __future__ import absolute_import, division, print_function import os import sys +from distutils import dist +from distutils.ccompiler import get_default_compiler +from distutils.command.config import config -from _cffi_src.utils import build_ffi_for_binding, extra_link_args +from _cffi_src.utils import ( + build_ffi_for_binding, compiler_type, extra_link_args +) def _get_openssl_libraries(platform): + if os.environ.get("CRYPTOGRAPHY_SUPPRESS_LINK_FLAGS", None): + return [] # OpenSSL goes by a different library name on different operating systems. - if platform != "win32": + if platform == "win32" and compiler_type() == "msvc": + windows_link_legacy_openssl = os.environ.get( + "CRYPTOGRAPHY_WINDOWS_LINK_LEGACY_OPENSSL", None + ) + if windows_link_legacy_openssl is None: + # Link against the 1.1.0 names + # CRYPTOGRAPHY_OPENSSL_110_OR_GREATER + libs = ["libssl", "libcrypto"] + else: + # Link against the 1.0.2 and lower names + libs = ["libeay32", "ssleay32"] + return libs + ["advapi32", "crypt32", "gdi32", "user32", "ws2_32"] + else: + # darwin, linux, mingw all use this path # In some circumstances, the order in which these libs are # specified on the linker command-line is significant; # libssl must come before libcrypto - # (http://marc.info/?l=openssl-users&m=135361825921871) - return ["ssl", "crypto"] - else: - link_type = os.environ.get("PYCA_WINDOWS_LINK_TYPE", "static") - return _get_openssl_windows_libraries(link_type) + # (https://marc.info/?l=openssl-users&m=135361825921871) + # -lpthread required due to usage of pthread an potential + # existance of a static part containing e.g. pthread_atfork + # (https://github.com/pyca/cryptography/issues/5084) + return ["ssl", "crypto", "pthread"] -def _get_openssl_windows_libraries(link_type): - if link_type == "dynamic": - return ["libeay32", "ssleay32", "advapi32"] - elif link_type == "static" or link_type == "": - return ["libeay32mt", "ssleay32mt", "advapi32", - "crypt32", "gdi32", "user32", "ws2_32"] +def _extra_compile_args(platform): + """ + We set -Wconversion args here so that we only do Wconversion checks on the + code we're compiling and not on cffi itself (as passing -Wconversion in + CFLAGS would do). We set no error on sign conversion because some + function signatures in OpenSSL have changed from long -> unsigned long + in the past. Since that isn't a precision issue we don't care. + When we drop support for CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 we can + revisit this. + """ + # make sure the compiler used supports the flags to be added + is_gcc = False + if get_default_compiler() == "unix": + d = dist.Distribution() + cmd = config(d) + cmd._check_compiler() + is_gcc = ("gcc" in cmd.compiler.compiler[0] or + "clang" in cmd.compiler.compiler[0]) + if is_gcc or not (platform in ["win32", "hp-ux11", "sunos5"] or + platform.startswith("aix")): + return ["-Wconversion", "-Wno-error=sign-conversion"] else: - raise ValueError( - "PYCA_WINDOWS_LINK_TYPE must be 'static' or 'dynamic'" - ) - - -_OSX_PRE_INCLUDE = """ -#ifdef __APPLE__ -#include <AvailabilityMacros.h> -#define __ORIG_DEPRECATED_IN_MAC_OS_X_VERSION_10_7_AND_LATER \ - DEPRECATED_IN_MAC_OS_X_VERSION_10_7_AND_LATER -#undef DEPRECATED_IN_MAC_OS_X_VERSION_10_7_AND_LATER -#define DEPRECATED_IN_MAC_OS_X_VERSION_10_7_AND_LATER -#endif -""" - -_OSX_POST_INCLUDE = """ -#ifdef __APPLE__ -#undef DEPRECATED_IN_MAC_OS_X_VERSION_10_7_AND_LATER -#define DEPRECATED_IN_MAC_OS_X_VERSION_10_7_AND_LATER \ - __ORIG_DEPRECATED_IN_MAC_OS_X_VERSION_10_7_AND_LATER -#endif -""" + return [] ffi = build_ffi_for_binding( module_name="_openssl", module_prefix="_cffi_src.openssl.", modules=[ + # This goes first so we can define some cryptography-wide symbols. + "cryptography", + "aes", "asn1", "bignum", "bio", "cmac", - "cms", "conf", "crypto", + "ct", "dh", "dsa", "ec", @@ -74,12 +91,14 @@ ffi = build_ffi_for_binding( "engine", "err", "evp", + "fips", "hmac", "nid", "objects", + "ocsp", "opensslv", + "osrandom_engine", "pem", - "pkcs7", "pkcs12", "rand", "rsa", @@ -87,10 +106,18 @@ ffi = build_ffi_for_binding( "x509", "x509name", "x509v3", - "x509_vfy" + "x509_vfy", + "pkcs7", + "callbacks", ], - pre_include=_OSX_PRE_INCLUDE, - post_include=_OSX_POST_INCLUDE, libraries=_get_openssl_libraries(sys.platform), - extra_link_args=extra_link_args(sys.platform), + # These args are passed here so that we only do Wconversion checks on the + # code we're compiling and not on cffi itself (as passing -Wconversion in + # CFLAGS would do). We set no error on sign convesrion because some + # function signatures in OpenSSL have changed from long -> unsigned long + # in the past. Since that isn't a precision issue we don't care. + # When we drop support for CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 we can + # revisit this. + extra_compile_args=_extra_compile_args(sys.platform), + extra_link_args=extra_link_args(compiler_type()), ) diff --git a/src/_cffi_src/build_padding.py b/src/_cffi_src/build_padding.py index 5df93d80..4c5096a1 100644 --- a/src/_cffi_src/build_padding.py +++ b/src/_cffi_src/build_padding.py @@ -5,9 +5,8 @@ from __future__ import absolute_import, division, print_function import os -import sys -from _cffi_src.utils import build_ffi, extra_link_args +from _cffi_src.utils import build_ffi, compiler_type, extra_link_args with open(os.path.join( @@ -24,5 +23,5 @@ ffi = build_ffi( module_name="_padding", cdef_source=types, verify_source=functions, - extra_link_args=extra_link_args(sys.platform), + extra_link_args=extra_link_args(compiler_type()), ) diff --git a/src/_cffi_src/commoncrypto/__init__.py b/src/_cffi_src/commoncrypto/__init__.py deleted file mode 100644 index 4b540884..00000000 --- a/src/_cffi_src/commoncrypto/__init__.py +++ /dev/null @@ -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 diff --git a/src/_cffi_src/commoncrypto/cf.py b/src/_cffi_src/commoncrypto/cf.py deleted file mode 100644 index 77d2d7cc..00000000 --- a/src/_cffi_src/commoncrypto/cf.py +++ /dev/null @@ -1,105 +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 - -INCLUDES = """ -#include <CoreFoundation/CoreFoundation.h> -""" - -TYPES = """ -typedef bool Boolean; -typedef signed long OSStatus; -typedef unsigned char UInt8; -typedef uint32_t UInt32; - -typedef const void * CFAllocatorRef; -const CFAllocatorRef kCFAllocatorDefault; -typedef ... *CFDataRef; -typedef signed long long CFIndex; -typedef ... *CFStringRef; -typedef ... *CFArrayRef; -typedef ... *CFBooleanRef; -typedef ... *CFErrorRef; -typedef ... *CFNumberRef; -typedef ... *CFTypeRef; -typedef ... *CFDictionaryRef; -typedef ... *CFMutableDictionaryRef; -typedef struct { - ...; -} CFDictionaryKeyCallBacks; -typedef struct { - ...; -} CFDictionaryValueCallBacks; -typedef struct { - ...; -} CFRange; - -typedef UInt32 CFStringEncoding; -enum { - kCFStringEncodingASCII = 0x0600 -}; - -enum { - kCFNumberSInt8Type = 1, - kCFNumberSInt16Type = 2, - kCFNumberSInt32Type = 3, - kCFNumberSInt64Type = 4, - kCFNumberFloat32Type = 5, - kCFNumberFloat64Type = 6, - kCFNumberCharType = 7, - kCFNumberShortType = 8, - kCFNumberIntType = 9, - kCFNumberLongType = 10, - kCFNumberLongLongType = 11, - kCFNumberFloatType = 12, - kCFNumberDoubleType = 13, - kCFNumberCFIndexType = 14, - kCFNumberNSIntegerType = 15, - kCFNumberCGFloatType = 16, - kCFNumberMaxType = 16 -}; -typedef int CFNumberType; - -const CFDictionaryKeyCallBacks kCFTypeDictionaryKeyCallBacks; -const CFDictionaryValueCallBacks kCFTypeDictionaryValueCallBacks; - -const CFBooleanRef kCFBooleanTrue; -const CFBooleanRef kCFBooleanFalse; -""" - -FUNCTIONS = """ -CFDataRef CFDataCreate(CFAllocatorRef, const UInt8 *, CFIndex); -CFStringRef CFStringCreateWithCString(CFAllocatorRef, const char *, - CFStringEncoding); -CFDictionaryRef CFDictionaryCreate(CFAllocatorRef, const void **, - const void **, CFIndex, - const CFDictionaryKeyCallBacks *, - const CFDictionaryValueCallBacks *); -CFMutableDictionaryRef CFDictionaryCreateMutable( - CFAllocatorRef, - CFIndex, - const CFDictionaryKeyCallBacks *, - const CFDictionaryValueCallBacks * -); -void CFDictionarySetValue(CFMutableDictionaryRef, const void *, const void *); -CFIndex CFArrayGetCount(CFArrayRef); -const void *CFArrayGetValueAtIndex(CFArrayRef, CFIndex); -CFIndex CFDataGetLength(CFDataRef); -void CFDataGetBytes(CFDataRef, CFRange, UInt8 *); -CFRange CFRangeMake(CFIndex, CFIndex); -void CFShow(CFTypeRef); -Boolean CFBooleanGetValue(CFBooleanRef); -CFNumberRef CFNumberCreate(CFAllocatorRef, CFNumberType, const void *); -void CFRelease(CFTypeRef); -CFTypeRef CFRetain(CFTypeRef); -""" - -MACROS = """ -""" - -CUSTOMIZATIONS = """ -""" - -CONDITIONAL_NAMES = {} diff --git a/src/_cffi_src/commoncrypto/common_cryptor.py b/src/_cffi_src/commoncrypto/common_cryptor.py deleted file mode 100644 index fc6eef91..00000000 --- a/src/_cffi_src/commoncrypto/common_cryptor.py +++ /dev/null @@ -1,101 +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 - -INCLUDES = """ -#include <CommonCrypto/CommonCryptor.h> -""" - -TYPES = """ -enum { - kCCAlgorithmAES128 = 0, - kCCAlgorithmDES, - kCCAlgorithm3DES, - kCCAlgorithmCAST, - kCCAlgorithmRC4, - kCCAlgorithmRC2, - kCCAlgorithmBlowfish -}; -typedef uint32_t CCAlgorithm; -enum { - kCCSuccess = 0, - kCCParamError = -4300, - kCCBufferTooSmall = -4301, - kCCMemoryFailure = -4302, - kCCAlignmentError = -4303, - kCCDecodeError = -4304, - kCCUnimplemented = -4305 -}; -typedef int32_t CCCryptorStatus; -typedef uint32_t CCOptions; -enum { - kCCEncrypt = 0, - kCCDecrypt, -}; -typedef uint32_t CCOperation; -typedef ... *CCCryptorRef; - -enum { - kCCModeOptionCTR_LE = 0x0001, - kCCModeOptionCTR_BE = 0x0002 -}; - -typedef uint32_t CCModeOptions; - -enum { - kCCModeECB = 1, - kCCModeCBC = 2, - kCCModeCFB = 3, - kCCModeCTR = 4, - kCCModeF8 = 5, - kCCModeLRW = 6, - kCCModeOFB = 7, - kCCModeXTS = 8, - kCCModeRC4 = 9, - kCCModeCFB8 = 10, - kCCModeGCM = 11 -}; -typedef uint32_t CCMode; -enum { - ccNoPadding = 0, - ccPKCS7Padding = 1, -}; -typedef uint32_t CCPadding; -""" - -FUNCTIONS = """ -CCCryptorStatus CCCryptorCreateWithMode(CCOperation, CCMode, CCAlgorithm, - CCPadding, const void *, const void *, - size_t, const void *, size_t, int, - CCModeOptions, CCCryptorRef *); -CCCryptorStatus CCCryptorCreate(CCOperation, CCAlgorithm, CCOptions, - const void *, size_t, const void *, - CCCryptorRef *); -CCCryptorStatus CCCryptorUpdate(CCCryptorRef, const void *, size_t, void *, - size_t, size_t *); -CCCryptorStatus CCCryptorFinal(CCCryptorRef, void *, size_t, size_t *); -CCCryptorStatus CCCryptorRelease(CCCryptorRef); - -CCCryptorStatus CCCryptorGCMAddIV(CCCryptorRef, const void *, size_t); -CCCryptorStatus CCCryptorGCMAddAAD(CCCryptorRef, const void *, size_t); -CCCryptorStatus CCCryptorGCMEncrypt(CCCryptorRef, const void *, size_t, - void *); -CCCryptorStatus CCCryptorGCMDecrypt(CCCryptorRef, const void *, size_t, - void *); -CCCryptorStatus CCCryptorGCMFinal(CCCryptorRef, const void *, size_t *); -CCCryptorStatus CCCryptorGCMReset(CCCryptorRef); -""" - -MACROS = """ -""" - -CUSTOMIZATIONS = """ -/* Not defined in the public header */ -enum { - kCCModeGCM = 11 -}; -""" - -CONDITIONAL_NAMES = {} diff --git a/src/_cffi_src/commoncrypto/common_digest.py b/src/_cffi_src/commoncrypto/common_digest.py deleted file mode 100644 index a76fc508..00000000 --- a/src/_cffi_src/commoncrypto/common_digest.py +++ /dev/null @@ -1,60 +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 - -INCLUDES = """ -#include <CommonCrypto/CommonDigest.h> -""" - -TYPES = """ -typedef uint32_t CC_LONG; -typedef uint64_t CC_LONG64; -typedef struct CC_MD5state_st { - ...; -} CC_MD5_CTX; -typedef struct CC_SHA1state_st { - ...; -} CC_SHA1_CTX; -typedef struct CC_SHA256state_st { - ...; -} CC_SHA256_CTX; -typedef struct CC_SHA512state_st { - ...; -} CC_SHA512_CTX; -""" - -FUNCTIONS = """ -int CC_MD5_Init(CC_MD5_CTX *); -int CC_MD5_Update(CC_MD5_CTX *, const void *, CC_LONG); -int CC_MD5_Final(unsigned char *, CC_MD5_CTX *); - -int CC_SHA1_Init(CC_SHA1_CTX *); -int CC_SHA1_Update(CC_SHA1_CTX *, const void *, CC_LONG); -int CC_SHA1_Final(unsigned char *, CC_SHA1_CTX *); - -int CC_SHA224_Init(CC_SHA256_CTX *); -int CC_SHA224_Update(CC_SHA256_CTX *, const void *, CC_LONG); -int CC_SHA224_Final(unsigned char *, CC_SHA256_CTX *); - -int CC_SHA256_Init(CC_SHA256_CTX *); -int CC_SHA256_Update(CC_SHA256_CTX *, const void *, CC_LONG); -int CC_SHA256_Final(unsigned char *, CC_SHA256_CTX *); - -int CC_SHA384_Init(CC_SHA512_CTX *); -int CC_SHA384_Update(CC_SHA512_CTX *, const void *, CC_LONG); -int CC_SHA384_Final(unsigned char *, CC_SHA512_CTX *); - -int CC_SHA512_Init(CC_SHA512_CTX *); -int CC_SHA512_Update(CC_SHA512_CTX *, const void *, CC_LONG); -int CC_SHA512_Final(unsigned char *, CC_SHA512_CTX *); -""" - -MACROS = """ -""" - -CUSTOMIZATIONS = """ -""" - -CONDITIONAL_NAMES = {} diff --git a/src/_cffi_src/commoncrypto/common_hmac.py b/src/_cffi_src/commoncrypto/common_hmac.py deleted file mode 100644 index fcd0c0f4..00000000 --- a/src/_cffi_src/commoncrypto/common_hmac.py +++ /dev/null @@ -1,39 +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 - -INCLUDES = """ -#include <CommonCrypto/CommonHMAC.h> -""" - -TYPES = """ -typedef struct { - ...; -} CCHmacContext; -enum { - kCCHmacAlgSHA1, - kCCHmacAlgMD5, - kCCHmacAlgSHA256, - kCCHmacAlgSHA384, - kCCHmacAlgSHA512, - kCCHmacAlgSHA224 -}; -typedef uint32_t CCHmacAlgorithm; -""" - -FUNCTIONS = """ -void CCHmacInit(CCHmacContext *, CCHmacAlgorithm, const void *, size_t); -void CCHmacUpdate(CCHmacContext *, const void *, size_t); -void CCHmacFinal(CCHmacContext *, void *); - -""" - -MACROS = """ -""" - -CUSTOMIZATIONS = """ -""" - -CONDITIONAL_NAMES = {} diff --git a/src/_cffi_src/commoncrypto/common_key_derivation.py b/src/_cffi_src/commoncrypto/common_key_derivation.py deleted file mode 100644 index 19525852..00000000 --- a/src/_cffi_src/commoncrypto/common_key_derivation.py +++ /dev/null @@ -1,41 +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 - -INCLUDES = """ -#include <CommonCrypto/CommonKeyDerivation.h> -""" - -TYPES = """ -enum { - kCCPBKDF2 = 2, -}; -typedef uint32_t CCPBKDFAlgorithm; -enum { - kCCPRFHmacAlgSHA1 = 1, - kCCPRFHmacAlgSHA224 = 2, - kCCPRFHmacAlgSHA256 = 3, - kCCPRFHmacAlgSHA384 = 4, - kCCPRFHmacAlgSHA512 = 5, -}; -typedef uint32_t CCPseudoRandomAlgorithm; -typedef unsigned int uint; -""" - -FUNCTIONS = """ -int CCKeyDerivationPBKDF(CCPBKDFAlgorithm, const char *, size_t, - const uint8_t *, size_t, CCPseudoRandomAlgorithm, - uint, uint8_t *, size_t); -uint CCCalibratePBKDF(CCPBKDFAlgorithm, size_t, size_t, - CCPseudoRandomAlgorithm, size_t, uint32_t); -""" - -MACROS = """ -""" - -CUSTOMIZATIONS = """ -""" - -CONDITIONAL_NAMES = {} diff --git a/src/_cffi_src/commoncrypto/common_symmetric_key_wrap.py b/src/_cffi_src/commoncrypto/common_symmetric_key_wrap.py deleted file mode 100644 index ea9e459d..00000000 --- a/src/_cffi_src/commoncrypto/common_symmetric_key_wrap.py +++ /dev/null @@ -1,37 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import absolute_import, division, print_function - -INCLUDES = """ -#include <CommonCrypto/CommonSymmetricKeywrap.h> -""" - -TYPES = """ -enum { - kCCWRAPAES = 1, -}; - -typedef uint32_t CCWrappingAlgorithm; -""" - -FUNCTIONS = """ -int CCSymmetricKeyWrap(CCWrappingAlgorithm, const uint8_t *, const size_t, - const uint8_t *, size_t, const uint8_t *, size_t, - uint8_t *, size_t *); -int CCSymmetricKeyUnwrap(CCWrappingAlgorithm algorithm, const uint8_t *, - const size_t, const uint8_t *, size_t, - const uint8_t *, size_t, uint8_t *, size_t *); -size_t CCSymmetricWrappedSize(CCWrappingAlgorithm, size_t); -size_t CCSymmetricUnwrappedSize(CCWrappingAlgorithm, size_t); - -""" - -MACROS = """ -""" - -CUSTOMIZATIONS = """ -""" - -CONDITIONAL_NAMES = {} diff --git a/src/_cffi_src/commoncrypto/secimport.py b/src/_cffi_src/commoncrypto/secimport.py deleted file mode 100644 index 41a799f9..00000000 --- a/src/_cffi_src/commoncrypto/secimport.py +++ /dev/null @@ -1,88 +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 - -INCLUDES = """ -#include <Security/SecImportExport.h> -""" - -TYPES = """ -typedef ... *SecAccessRef; - -CFStringRef kSecImportExportPassphrase; -CFStringRef kSecImportExportKeychain; -CFStringRef kSecImportExportAccess; - -typedef uint32_t SecExternalItemType; -enum { - kSecItemTypeUnknown, - kSecItemTypePrivateKey, - kSecItemTypePublicKey, - kSecItemTypeSessionKey, - kSecItemTypeCertificate, - kSecItemTypeAggregate -}; - - -typedef uint32_t SecExternalFormat; -enum { - kSecFormatUnknown = 0, - kSecFormatOpenSSL, - kSecFormatSSH, - kSecFormatBSAFE, - kSecFormatRawKey, - kSecFormatWrappedPKCS8, - kSecFormatWrappedOpenSSL, - kSecFormatWrappedSSH, - kSecFormatWrappedLSH, - kSecFormatX509Cert, - kSecFormatPEMSequence, - kSecFormatPKCS7, - kSecFormatPKCS12, - kSecFormatNetscapeCertSequence, - kSecFormatSSHv2 -}; - -typedef uint32_t SecItemImportExportFlags; -enum { - kSecKeyImportOnlyOne = 0x00000001, - kSecKeySecurePassphrase = 0x00000002, - kSecKeyNoAccessControl = 0x00000004 -}; -typedef uint32_t SecKeyImportExportFlags; - -typedef struct { - /* for import and export */ - uint32_t version; - SecKeyImportExportFlags flags; - CFTypeRef passphrase; - CFStringRef alertTitle; - CFStringRef alertPrompt; - - /* for import only */ - SecAccessRef accessRef; - CFArrayRef keyUsage; - - CFArrayRef keyAttributes; -} SecItemImportExportKeyParameters; -""" - -FUNCTIONS = """ -OSStatus SecItemImport(CFDataRef, CFStringRef, SecExternalFormat *, - SecExternalItemType *, SecItemImportExportFlags, - const SecItemImportExportKeyParameters *, - SecKeychainRef, CFArrayRef *); -OSStatus SecPKCS12Import(CFDataRef, CFDictionaryRef, CFArrayRef *); -OSStatus SecItemExport(CFTypeRef, SecExternalFormat, SecItemImportExportFlags, - const SecItemImportExportKeyParameters *, CFDataRef *); -""" - -MACROS = """ -""" - -CUSTOMIZATIONS = """ -""" - -CONDITIONAL_NAMES = {} diff --git a/src/_cffi_src/commoncrypto/seckey.py b/src/_cffi_src/commoncrypto/seckey.py deleted file mode 100644 index 01d42e6a..00000000 --- a/src/_cffi_src/commoncrypto/seckey.py +++ /dev/null @@ -1,26 +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 - -INCLUDES = """ -#include <Security/SecKey.h> -""" - -TYPES = """ -typedef ... *SecKeyRef; -""" - -FUNCTIONS = """ -OSStatus SecKeyGeneratePair(CFDictionaryRef, SecKeyRef *, SecKeyRef *); -size_t SecKeyGetBlockSize(SecKeyRef); -""" - -MACROS = """ -""" - -CUSTOMIZATIONS = """ -""" - -CONDITIONAL_NAMES = {} diff --git a/src/_cffi_src/commoncrypto/seckeychain.py b/src/_cffi_src/commoncrypto/seckeychain.py deleted file mode 100644 index 6a2cb4be..00000000 --- a/src/_cffi_src/commoncrypto/seckeychain.py +++ /dev/null @@ -1,27 +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 - -INCLUDES = """ -#include <Security/SecKeychain.h> -""" - -TYPES = """ -typedef ... *SecKeychainRef; -""" - -FUNCTIONS = """ -OSStatus SecKeychainCreate(const char *, UInt32, const void *, Boolean, - SecAccessRef, SecKeychainRef *); -OSStatus SecKeychainDelete(SecKeychainRef); -""" - -MACROS = """ -""" - -CUSTOMIZATIONS = """ -""" - -CONDITIONAL_NAMES = {} diff --git a/src/_cffi_src/commoncrypto/sectransform.py b/src/_cffi_src/commoncrypto/sectransform.py deleted file mode 100644 index bed94953..00000000 --- a/src/_cffi_src/commoncrypto/sectransform.py +++ /dev/null @@ -1,70 +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 - -INCLUDES = """ -#include <Security/SecDigestTransform.h> -#include <Security/SecSignVerifyTransform.h> -#include <Security/SecEncryptTransform.h> -""" - -TYPES = """ -typedef ... *SecTransformRef; - -CFStringRef kSecImportExportPassphrase; -CFStringRef kSecImportExportKeychain; -CFStringRef kSecImportExportAccess; - -CFStringRef kSecEncryptionMode; -CFStringRef kSecEncryptKey; -CFStringRef kSecIVKey; -CFStringRef kSecModeCBCKey; -CFStringRef kSecModeCFBKey; -CFStringRef kSecModeECBKey; -CFStringRef kSecModeNoneKey; -CFStringRef kSecModeOFBKey; -CFStringRef kSecOAEPEncodingParametersAttributeName; -CFStringRef kSecPaddingKey; -CFStringRef kSecPaddingNoneKey; -CFStringRef kSecPaddingOAEPKey; -CFStringRef kSecPaddingPKCS1Key; -CFStringRef kSecPaddingPKCS5Key; -CFStringRef kSecPaddingPKCS7Key; - -const CFStringRef kSecTransformInputAttributeName; -const CFStringRef kSecTransformOutputAttributeName; -const CFStringRef kSecTransformDebugAttributeName; -const CFStringRef kSecTransformTransformName; -const CFStringRef kSecTransformAbortAttributeName; - -CFStringRef kSecInputIsAttributeName; -CFStringRef kSecInputIsPlainText; -CFStringRef kSecInputIsDigest; -CFStringRef kSecInputIsRaw; - -const CFStringRef kSecDigestTypeAttribute; -const CFStringRef kSecDigestLengthAttribute; -const CFStringRef kSecDigestMD5; -const CFStringRef kSecDigestSHA1; -const CFStringRef kSecDigestSHA2; -""" - -FUNCTIONS = """ -Boolean SecTransformSetAttribute(SecTransformRef, CFStringRef, CFTypeRef, - CFErrorRef *); -SecTransformRef SecDecryptTransformCreate(SecKeyRef, CFErrorRef *); -SecTransformRef SecEncryptTransformCreate(SecKeyRef, CFErrorRef *); -SecTransformRef SecVerifyTransformCreate(SecKeyRef, CFDataRef, CFErrorRef *); -SecTransformRef SecSignTransformCreate(SecKeyRef, CFErrorRef *) ; -CFTypeRef SecTransformExecute(SecTransformRef, CFErrorRef *); -""" - -MACROS = """ -""" - -CUSTOMIZATIONS = """ -""" - -CONDITIONAL_NAMES = {} diff --git a/src/_cffi_src/hazmat_src/constant_time.c b/src/_cffi_src/hazmat_src/constant_time.c deleted file mode 100644 index 0a48fe83..00000000 --- a/src/_cffi_src/hazmat_src/constant_time.c +++ /dev/null @@ -1,22 +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. - -uint8_t Cryptography_constant_time_bytes_eq(uint8_t *a, size_t len_a, - uint8_t *b, size_t len_b) { - size_t i = 0; - uint8_t mismatch = 0; - if (len_a != len_b) { - return 0; - } - for (i = 0; i < len_a; i++) { - mismatch |= a[i] ^ b[i]; - } - - /* Make sure any bits set are copied to the lowest bit */ - mismatch |= mismatch >> 4; - mismatch |= mismatch >> 2; - mismatch |= mismatch >> 1; - /* Now check the low bit to see if it's set */ - return (mismatch & 1) == 0; -} diff --git a/src/_cffi_src/hazmat_src/constant_time.h b/src/_cffi_src/hazmat_src/constant_time.h deleted file mode 100644 index 593479f6..00000000 --- a/src/_cffi_src/hazmat_src/constant_time.h +++ /dev/null @@ -1,6 +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. - -uint8_t Cryptography_constant_time_bytes_eq(uint8_t *, size_t, uint8_t *, - size_t); diff --git a/src/_cffi_src/hazmat_src/padding.c b/src/_cffi_src/hazmat_src/padding.c index 570bad9f..a6e05dee 100644 --- a/src/_cffi_src/hazmat_src/padding.c +++ b/src/_cffi_src/hazmat_src/padding.c @@ -4,25 +4,25 @@ /* Returns the value of the input with the most-significant-bit copied to all of the bits. */ -static uint8_t Cryptography_DUPLICATE_MSB_TO_ALL(uint8_t a) { - return (1 - (a >> (sizeof(uint8_t) * 8 - 1))) - 1; +static uint16_t Cryptography_DUPLICATE_MSB_TO_ALL(uint16_t a) { + return (1 - (a >> (sizeof(uint16_t) * 8 - 1))) - 1; } -/* This returns 0xFF if a < b else 0x00, but does so in a constant time +/* This returns 0xFFFF if a < b else 0x0000, but does so in a constant time fashion */ -static uint8_t Cryptography_constant_time_lt(uint8_t a, uint8_t b) { +static uint16_t Cryptography_constant_time_lt(uint16_t a, uint16_t b) { a -= b; return Cryptography_DUPLICATE_MSB_TO_ALL(a); } uint8_t Cryptography_check_pkcs7_padding(const uint8_t *data, - uint8_t block_len) { - uint8_t i; - uint8_t pad_size = data[block_len - 1]; - uint8_t mismatch = 0; + uint16_t block_len) { + uint16_t i; + uint16_t pad_size = data[block_len - 1]; + uint16_t mismatch = 0; for (i = 0; i < block_len; i++) { unsigned int mask = Cryptography_constant_time_lt(i, pad_size); - uint8_t b = data[block_len - 1 - i]; + uint16_t b = data[block_len - 1 - i]; mismatch |= (mask & (pad_size ^ b)); } @@ -31,6 +31,32 @@ uint8_t Cryptography_check_pkcs7_padding(const uint8_t *data, mismatch |= Cryptography_constant_time_lt(block_len, pad_size); /* Make sure any bits set are copied to the lowest bit */ + mismatch |= mismatch >> 8; + mismatch |= mismatch >> 4; + mismatch |= mismatch >> 2; + mismatch |= mismatch >> 1; + /* Now check the low bit to see if it's set */ + return (mismatch & 1) == 0; +} + +uint8_t Cryptography_check_ansix923_padding(const uint8_t *data, + uint16_t block_len) { + uint16_t i; + uint16_t pad_size = data[block_len - 1]; + uint16_t mismatch = 0; + /* Skip the first one with the pad size */ + for (i = 1; i < block_len; i++) { + unsigned int mask = Cryptography_constant_time_lt(i, pad_size); + uint16_t b = data[block_len - 1 - i]; + mismatch |= (mask & b); + } + + /* Check to make sure the pad_size was within the valid range. */ + mismatch |= ~Cryptography_constant_time_lt(0, pad_size); + mismatch |= Cryptography_constant_time_lt(block_len, pad_size); + + /* Make sure any bits set are copied to the lowest bit */ + mismatch |= mismatch >> 8; mismatch |= mismatch >> 4; mismatch |= mismatch >> 2; mismatch |= mismatch >> 1; diff --git a/src/_cffi_src/hazmat_src/padding.h b/src/_cffi_src/hazmat_src/padding.h index 4d218b1a..fb023c17 100644 --- a/src/_cffi_src/hazmat_src/padding.h +++ b/src/_cffi_src/hazmat_src/padding.h @@ -3,3 +3,4 @@ // repository for complete details. uint8_t Cryptography_check_pkcs7_padding(const uint8_t *, uint8_t); +uint8_t Cryptography_check_ansix923_padding(const uint8_t *, uint8_t); diff --git a/src/_cffi_src/openssl/aes.py b/src/_cffi_src/openssl/aes.py index 15da9b62..5c9dee6d 100644 --- a/src/_cffi_src/openssl/aes.py +++ b/src/_cffi_src/openssl/aes.py @@ -9,53 +9,18 @@ INCLUDES = """ """ TYPES = """ -static const int Cryptography_HAS_AES_WRAP; - -struct aes_key_st { - ...; -}; -typedef struct aes_key_st AES_KEY; +typedef ... AES_KEY; """ FUNCTIONS = """ int AES_set_encrypt_key(const unsigned char *, const int, AES_KEY *); int AES_set_decrypt_key(const unsigned char *, const int, AES_KEY *); -""" -MACROS = """ -/* these can be moved back to FUNCTIONS once we drop support for 0.9.8h. - This should be when we drop RHEL/CentOS 5, which is on 0.9.8e. */ int AES_wrap_key(AES_KEY *, const unsigned char *, unsigned char *, const unsigned char *, unsigned int); int AES_unwrap_key(AES_KEY *, const unsigned char *, unsigned char *, const unsigned char *, unsigned int); - -/* The ctr128_encrypt function is only useful in 0.9.8. You should use EVP for - this in 1.0.0+. It is defined in macros because the function signature - changed after 0.9.8 */ -void AES_ctr128_encrypt(const unsigned char *, unsigned char *, - const size_t, const AES_KEY *, - unsigned char[], unsigned char[], unsigned int *); - """ CUSTOMIZATIONS = """ -/* OpenSSL 0.9.8h+ */ -#if OPENSSL_VERSION_NUMBER >= 0x0090808fL -static const long Cryptography_HAS_AES_WRAP = 1; -#else -static const long Cryptography_HAS_AES_WRAP = 0; -int (*AES_wrap_key)(AES_KEY *, const unsigned char *, unsigned char *, - const unsigned char *, unsigned int) = NULL; -int (*AES_unwrap_key)(AES_KEY *, const unsigned char *, unsigned char *, - const unsigned char *, unsigned int) = NULL; -#endif - """ - -CONDITIONAL_NAMES = { - "Cryptography_HAS_AES_WRAP": [ - "AES_wrap_key", - "AES_unwrap_key", - ], -} diff --git a/src/_cffi_src/openssl/asn1.py b/src/_cffi_src/openssl/asn1.py index c18708c5..3e148ce0 100644 --- a/src/_cffi_src/openssl/asn1.py +++ b/src/_cffi_src/openssl/asn1.py @@ -9,26 +9,8 @@ INCLUDES = """ """ TYPES = """ -/* - * TODO: This typedef is wrong. - * - * This is due to limitations of cffi. - * See https://bitbucket.org/cffi/cffi/issue/69 - * - * For another possible work-around (not used here because it involves more - * complicated use of the cffi API which falls outside the general pattern used - * by this package), see - * http://paste.pound-python.org/show/iJcTUMkKeBeS6yXpZWUU/ - * - * The work-around used here is to just be sure to declare a type that is at - * least as large as the real type. Maciej explains: - * - * <fijal> I think you want to declare your value too large (e.g. long) - * <fijal> that way you'll never pass garbage - */ -typedef intptr_t time_t; - -typedef int ASN1_BOOLEAN; +typedef int... time_t; + typedef ... ASN1_INTEGER; struct asn1_string_st { @@ -40,121 +22,88 @@ struct asn1_string_st { typedef struct asn1_string_st ASN1_OCTET_STRING; typedef struct asn1_string_st ASN1_IA5STRING; -typedef ... ASN1_BIT_STRING; +typedef struct asn1_string_st ASN1_BIT_STRING; +typedef struct asn1_string_st ASN1_TIME; typedef ... ASN1_OBJECT; -typedef ... ASN1_STRING; -typedef ... ASN1_TYPE; -typedef ... ASN1_GENERALIZEDTIME; -typedef ... ASN1_ENUMERATED; -typedef ... ASN1_ITEM; -typedef ... ASN1_VALUE; - +typedef struct asn1_string_st ASN1_STRING; +typedef struct asn1_string_st ASN1_UTF8STRING; typedef struct { + int type; ...; -} ASN1_TIME; -typedef ... ASN1_ITEM_EXP; - -typedef ... ASN1_UTCTIME; +} ASN1_TYPE; +typedef ... ASN1_GENERALIZEDTIME; +typedef ... ASN1_ENUMERATED; +typedef ... ASN1_NULL; static const int V_ASN1_GENERALIZEDTIME; -static const int MBSTRING_FLAG; -static const int MBSTRING_ASC; -static const int MBSTRING_BMP; static const int MBSTRING_UTF8; -static const int MBSTRING_UNIV; """ FUNCTIONS = """ -ASN1_OBJECT *ASN1_OBJECT_new(void); void ASN1_OBJECT_free(ASN1_OBJECT *); -/* ASN1 OBJECT IDENTIFIER */ -ASN1_OBJECT *d2i_ASN1_OBJECT(ASN1_OBJECT **, const unsigned char **, long); -int i2d_ASN1_OBJECT(ASN1_OBJECT *, unsigned char **); - /* ASN1 STRING */ -ASN1_STRING *ASN1_STRING_new(void); -ASN1_STRING *ASN1_STRING_type_new(int); -void ASN1_STRING_free(ASN1_STRING *); unsigned char *ASN1_STRING_data(ASN1_STRING *); int ASN1_STRING_set(ASN1_STRING *, const void *, int); -int ASN1_STRING_type(ASN1_STRING *); -int ASN1_STRING_to_UTF8(unsigned char **, ASN1_STRING *); /* ASN1 OCTET STRING */ ASN1_OCTET_STRING *ASN1_OCTET_STRING_new(void); void ASN1_OCTET_STRING_free(ASN1_OCTET_STRING *); int ASN1_OCTET_STRING_set(ASN1_OCTET_STRING *, const unsigned char *, int); +/* ASN1 IA5STRING */ +ASN1_IA5STRING *ASN1_IA5STRING_new(void); + /* ASN1 INTEGER */ -ASN1_INTEGER *ASN1_INTEGER_new(void); void ASN1_INTEGER_free(ASN1_INTEGER *); int ASN1_INTEGER_set(ASN1_INTEGER *, long); -int i2a_ASN1_INTEGER(BIO *, ASN1_INTEGER *); /* ASN1 TIME */ ASN1_TIME *ASN1_TIME_new(void); void ASN1_TIME_free(ASN1_TIME *); -ASN1_GENERALIZEDTIME *ASN1_TIME_to_generalizedtime(ASN1_TIME *, - ASN1_GENERALIZEDTIME **); ASN1_TIME *ASN1_TIME_set(ASN1_TIME *, time_t); - -/* ASN1 UTCTIME */ -ASN1_UTCTIME *ASN1_UTCTIME_new(void); -void ASN1_UTCTIME_free(ASN1_UTCTIME *); -int ASN1_UTCTIME_cmp_time_t(const ASN1_UTCTIME *, time_t); -ASN1_UTCTIME *ASN1_UTCTIME_set(ASN1_UTCTIME *, time_t); +int ASN1_TIME_set_string(ASN1_TIME *, const char *); /* ASN1 GENERALIZEDTIME */ -int ASN1_GENERALIZEDTIME_set_string(ASN1_GENERALIZEDTIME *, const char *); +ASN1_GENERALIZEDTIME *ASN1_GENERALIZEDTIME_set(ASN1_GENERALIZEDTIME *, time_t); void ASN1_GENERALIZEDTIME_free(ASN1_GENERALIZEDTIME *); /* ASN1 ENUMERATED */ ASN1_ENUMERATED *ASN1_ENUMERATED_new(void); void ASN1_ENUMERATED_free(ASN1_ENUMERATED *); int ASN1_ENUMERATED_set(ASN1_ENUMERATED *, long); -long ASN1_ENUMERATED_get(ASN1_ENUMERATED *); -ASN1_VALUE *ASN1_item_d2i(ASN1_VALUE **, const unsigned char **, long, - const ASN1_ITEM *); int ASN1_BIT_STRING_set_bit(ASN1_BIT_STRING *, int, int); -""" +/* These became const ASN1_* in 1.1.0 */ +int ASN1_STRING_type(ASN1_STRING *); +int ASN1_STRING_to_UTF8(unsigned char **, ASN1_STRING *); +long ASN1_ENUMERATED_get(ASN1_ENUMERATED *); +int i2a_ASN1_INTEGER(BIO *, ASN1_INTEGER *); + +/* This became const ASN1_TIME in 1.1.0f */ +ASN1_GENERALIZEDTIME *ASN1_TIME_to_generalizedtime(ASN1_TIME *, + ASN1_GENERALIZEDTIME **); + +ASN1_UTF8STRING *ASN1_UTF8STRING_new(void); +void ASN1_UTF8STRING_free(ASN1_UTF8STRING *); -MACROS = """ +ASN1_BIT_STRING *ASN1_BIT_STRING_new(void); void ASN1_BIT_STRING_free(ASN1_BIT_STRING *); /* This is not a macro, but is const on some versions of OpenSSL */ int ASN1_BIT_STRING_get_bit(ASN1_BIT_STRING *, int); -ASN1_TIME *M_ASN1_TIME_dup(void *); -const ASN1_ITEM *ASN1_ITEM_ptr(ASN1_ITEM_EXP *); -/* These aren't macros these arguments are all const X on openssl > 1.0.x */ - -int ASN1_TIME_print(BIO *, ASN1_TIME *); int ASN1_STRING_length(ASN1_STRING *); -ASN1_STRING *ASN1_STRING_dup(ASN1_STRING *); -int ASN1_STRING_cmp(ASN1_STRING *, ASN1_STRING *); -int ASN1_UTCTIME_print(BIO *, ASN1_UTCTIME *); - -ASN1_OCTET_STRING *ASN1_OCTET_STRING_dup(ASN1_OCTET_STRING *); -int ASN1_OCTET_STRING_cmp(ASN1_OCTET_STRING *, ASN1_OCTET_STRING *); - -ASN1_INTEGER *ASN1_INTEGER_dup(ASN1_INTEGER *); -int ASN1_INTEGER_cmp(ASN1_INTEGER *, ASN1_INTEGER *); -long ASN1_INTEGER_get(ASN1_INTEGER *); +int ASN1_STRING_set_default_mask_asc(char *); BIGNUM *ASN1_INTEGER_to_BN(ASN1_INTEGER *, BIGNUM *); ASN1_INTEGER *BN_to_ASN1_INTEGER(BIGNUM *, ASN1_INTEGER *); -/* These isn't a macro the arg is const on openssl 1.0.2+ */ -int ASN1_GENERALIZEDTIME_check(ASN1_GENERALIZEDTIME *); -int ASN1_UTCTIME_check(ASN1_UTCTIME *); +int i2d_ASN1_TYPE(ASN1_TYPE *, unsigned char **); +ASN1_TYPE *d2i_ASN1_TYPE(ASN1_TYPE **, const unsigned char **, long); -/* Not a macro, const on openssl 1.0 */ -int ASN1_STRING_set_default_mask_asc(char *); +ASN1_NULL *ASN1_NULL_new(void); """ CUSTOMIZATIONS = """ """ - -CONDITIONAL_NAMES = {} diff --git a/src/_cffi_src/openssl/bignum.py b/src/_cffi_src/openssl/bignum.py index d974e04e..75101839 100644 --- a/src/_cffi_src/openssl/bignum.py +++ b/src/_cffi_src/openssl/bignum.py @@ -10,30 +10,21 @@ INCLUDES = """ TYPES = """ typedef ... BN_CTX; +typedef ... BN_MONT_CTX; typedef ... BIGNUM; -/* - * TODO: This typedef is wrong. - * - * This is due to limitations of cffi. - * See https://bitbucket.org/cffi/cffi/issue/69 - * - * For another possible work-around (not used here because it involves more - * complicated use of the cffi API which falls outside the general pattern used - * by this package), see - * http://paste.pound-python.org/show/iJcTUMkKeBeS6yXpZWUU/ - * - * The work-around used here is to just be sure to declare a type that is at - * least as large as the real type. Maciej explains: - * - * <fijal> I think you want to declare your value too large (e.g. long) - * <fijal> that way you'll never pass garbage - */ -typedef uintptr_t BN_ULONG; +typedef int... BN_ULONG; """ FUNCTIONS = """ +#define BN_FLG_CONSTTIME ... + +void BN_set_flags(BIGNUM *, int); + BIGNUM *BN_new(void); void BN_free(BIGNUM *); +void BN_clear_free(BIGNUM *); + +int BN_rand_range(BIGNUM *, const BIGNUM *); BN_CTX *BN_CTX_new(void); void BN_CTX_free(BN_CTX *); @@ -42,17 +33,18 @@ void BN_CTX_start(BN_CTX *); BIGNUM *BN_CTX_get(BN_CTX *); void BN_CTX_end(BN_CTX *); -BIGNUM *BN_copy(BIGNUM *, const BIGNUM *); +BN_MONT_CTX *BN_MONT_CTX_new(void); +int BN_MONT_CTX_set(BN_MONT_CTX *, const BIGNUM *, BN_CTX *); +void BN_MONT_CTX_free(BN_MONT_CTX *); + BIGNUM *BN_dup(const BIGNUM *); int BN_set_word(BIGNUM *, BN_ULONG); -BN_ULONG BN_get_word(const BIGNUM *); const BIGNUM *BN_value_one(void); char *BN_bn2hex(const BIGNUM *); int BN_hex2bn(BIGNUM **, const char *); -int BN_dec2bn(BIGNUM **, const char *); int BN_bn2bin(const BIGNUM *, unsigned char *); BIGNUM *BN_bin2bn(const unsigned char *, int, BIGNUM *); @@ -60,11 +52,9 @@ BIGNUM *BN_bin2bn(const unsigned char *, int, BIGNUM *); int BN_num_bits(const BIGNUM *); int BN_cmp(const BIGNUM *, const BIGNUM *); +int BN_is_negative(const BIGNUM *); int BN_add(BIGNUM *, const BIGNUM *, const BIGNUM *); int BN_sub(BIGNUM *, const BIGNUM *, const BIGNUM *); -int BN_mul(BIGNUM *, const BIGNUM *, const BIGNUM *, BN_CTX *); -int BN_sqr(BIGNUM *, const BIGNUM *, BN_CTX *); -int BN_div(BIGNUM *, BIGNUM *, const BIGNUM *, const BIGNUM *, BN_CTX *); int BN_nnmod(BIGNUM *, const BIGNUM *, const BIGNUM *, BN_CTX *); int BN_mod_add(BIGNUM *, const BIGNUM *, const BIGNUM *, const BIGNUM *, BN_CTX *); @@ -72,34 +62,24 @@ int BN_mod_sub(BIGNUM *, const BIGNUM *, const BIGNUM *, const BIGNUM *, BN_CTX *); int BN_mod_mul(BIGNUM *, const BIGNUM *, const BIGNUM *, const BIGNUM *, BN_CTX *); -int BN_mod_sqr(BIGNUM *, const BIGNUM *, const BIGNUM *, BN_CTX *); -int BN_exp(BIGNUM *, const BIGNUM *, const BIGNUM *, BN_CTX *); int BN_mod_exp(BIGNUM *, const BIGNUM *, const BIGNUM *, const BIGNUM *, BN_CTX *); -int BN_gcd(BIGNUM *, const BIGNUM *, const BIGNUM *, BN_CTX *); +int BN_mod_exp_mont(BIGNUM *, const BIGNUM *, const BIGNUM *, const BIGNUM *, + BN_CTX *, BN_MONT_CTX *); +int BN_mod_exp_mont_consttime(BIGNUM *, const BIGNUM *, const BIGNUM *, + const BIGNUM *, BN_CTX *, BN_MONT_CTX *); BIGNUM *BN_mod_inverse(BIGNUM *, const BIGNUM *, const BIGNUM *, BN_CTX *); -int BN_set_bit(BIGNUM *, int); -int BN_clear_bit(BIGNUM *, int); - -int BN_is_bit_set(const BIGNUM *, int); +int BN_num_bytes(const BIGNUM *); -int BN_mask_bits(BIGNUM *, int); -""" - -MACROS = """ -int BN_zero(BIGNUM *); -int BN_one(BIGNUM *); int BN_mod(BIGNUM *, const BIGNUM *, const BIGNUM *, BN_CTX *); -int BN_lshift(BIGNUM *, const BIGNUM *, int); -int BN_lshift1(BIGNUM *, BIGNUM *); - -int BN_rshift(BIGNUM *, BIGNUM *, int); -int BN_rshift1(BIGNUM *, BIGNUM *); +/* The following 3 prime methods are exposed for Tribler. */ +int BN_generate_prime_ex(BIGNUM *, int, int, const BIGNUM *, + const BIGNUM *, BN_GENCB *); +int BN_is_prime_ex(const BIGNUM *, int, BN_CTX *, BN_GENCB *); +const int BN_prime_checks_for_size(int); """ CUSTOMIZATIONS = """ """ - -CONDITIONAL_NAMES = {} diff --git a/src/_cffi_src/openssl/bio.py b/src/_cffi_src/openssl/bio.py index 6cc1bcb2..8f5a3e6a 100644 --- a/src/_cffi_src/openssl/bio.py +++ b/src/_cffi_src/openssl/bio.py @@ -9,164 +9,42 @@ INCLUDES = """ """ TYPES = """ -typedef struct bio_st BIO; -typedef void bio_info_cb(BIO *, int, const char *, int, long, long); -struct bio_method_st { - int type; - const char *name; - int (*bwrite)(BIO *, const char *, int); - int (*bread)(BIO *, char *, int); - int (*bputs)(BIO *, const char *); - int (*bgets)(BIO *, char *, int); - long (*ctrl)(BIO *, int, long, void *); - int (*create)(BIO *); - int (*destroy)(BIO *); - long (*callback_ctrl)(BIO *, int, bio_info_cb *); - ...; -}; -typedef struct bio_method_st BIO_METHOD; -struct bio_st { - BIO_METHOD *method; - long (*callback)(struct bio_st *, int, const char *, int, long, long); - char *cb_arg; - int init; - int shutdown; - int flags; - int retry_reason; - int num; - void *ptr; - struct bio_st *next_bio; - struct bio_st *prev_bio; - int references; - unsigned long num_read; - unsigned long num_write; - ...; -}; -typedef ... BUF_MEM; - -static const int BIO_TYPE_MEM; -static const int BIO_TYPE_FILE; -static const int BIO_TYPE_FD; -static const int BIO_TYPE_SOCKET; -static const int BIO_TYPE_CONNECT; -static const int BIO_TYPE_ACCEPT; -static const int BIO_TYPE_NULL; -static const int BIO_CLOSE; -static const int BIO_NOCLOSE; -static const int BIO_TYPE_SOURCE_SINK; -static const int BIO_CTRL_RESET; -static const int BIO_CTRL_EOF; -static const int BIO_CTRL_SET; -static const int BIO_CTRL_SET_CLOSE; -static const int BIO_CTRL_FLUSH; -static const int BIO_CTRL_DUP; -static const int BIO_CTRL_GET_CLOSE; -static const int BIO_CTRL_INFO; -static const int BIO_CTRL_GET; -static const int BIO_CTRL_PENDING; -static const int BIO_CTRL_WPENDING; -static const int BIO_C_FILE_SEEK; -static const int BIO_C_FILE_TELL; -static const int BIO_TYPE_NONE; -static const int BIO_TYPE_PROXY_CLIENT; -static const int BIO_TYPE_PROXY_SERVER; -static const int BIO_TYPE_NBIO_TEST; -static const int BIO_TYPE_BER; -static const int BIO_TYPE_BIO; -static const int BIO_TYPE_DESCRIPTOR; -static const int BIO_FLAGS_READ; -static const int BIO_FLAGS_WRITE; -static const int BIO_FLAGS_IO_SPECIAL; -static const int BIO_FLAGS_RWS; -static const int BIO_FLAGS_SHOULD_RETRY; -static const int BIO_TYPE_NULL_FILTER; -static const int BIO_TYPE_SSL; -static const int BIO_TYPE_MD; -static const int BIO_TYPE_BUFFER; -static const int BIO_TYPE_CIPHER; -static const int BIO_TYPE_BASE64; -static const int BIO_TYPE_FILTER; +typedef ... BIO; +typedef ... BIO_METHOD; """ FUNCTIONS = """ -BIO *BIO_new(BIO_METHOD *); -int BIO_set(BIO *, BIO_METHOD *); int BIO_free(BIO *); -void BIO_vfree(BIO *); void BIO_free_all(BIO *); -BIO *BIO_push(BIO *, BIO *); -BIO *BIO_pop(BIO *); -BIO *BIO_next(BIO *); -BIO *BIO_find_type(BIO *, int); -BIO_METHOD *BIO_s_mem(void); -BIO *BIO_new_mem_buf(void *, int); -BIO_METHOD *BIO_s_file(void); BIO *BIO_new_file(const char *, const char *); -BIO *BIO_new_fp(FILE *, int); -BIO_METHOD *BIO_s_fd(void); -BIO *BIO_new_fd(int, int); -BIO_METHOD *BIO_s_socket(void); -BIO *BIO_new_socket(int, int); -BIO_METHOD *BIO_s_null(void); -long BIO_ctrl(BIO *, int, long, void *); -long BIO_callback_ctrl( - BIO *, - int, - void (*)(struct bio_st *, int, const char *, int, long, long) -); -char *BIO_ptr_ctrl(BIO *, int, long); -long BIO_int_ctrl(BIO *, int, long, int); +BIO *BIO_new_dgram(int, int); size_t BIO_ctrl_pending(BIO *); -size_t BIO_ctrl_wpending(BIO *); int BIO_read(BIO *, void *, int); int BIO_gets(BIO *, char *, int); int BIO_write(BIO *, const void *, int); -int BIO_puts(BIO *, const char *); -BIO_METHOD *BIO_f_null(void); -BIO_METHOD *BIO_f_buffer(void); -""" +/* Added in 1.1.0 */ +int BIO_up_ref(BIO *); -MACROS = """ -long BIO_set_fd(BIO *, long, int); -long BIO_get_fd(BIO *, char *); +BIO *BIO_new(BIO_METHOD *); +BIO_METHOD *BIO_s_mem(void); +BIO_METHOD *BIO_s_datagram(void); +BIO *BIO_new_mem_buf(const void *, int); long BIO_set_mem_eof_return(BIO *, int); long BIO_get_mem_data(BIO *, char **); -long BIO_set_mem_buf(BIO *, BUF_MEM *, int); -long BIO_get_mem_ptr(BIO *, BUF_MEM **); -long BIO_set_fp(BIO *, FILE *, int); -long BIO_get_fp(BIO *, FILE **); -long BIO_read_filename(BIO *, char *); -long BIO_write_filename(BIO *, char *); -long BIO_append_filename(BIO *, char *); -long BIO_rw_filename(BIO *, char *); int BIO_should_read(BIO *); int BIO_should_write(BIO *); int BIO_should_io_special(BIO *); -int BIO_retry_type(BIO *); int BIO_should_retry(BIO *); int BIO_reset(BIO *); -int BIO_seek(BIO *, int); -int BIO_tell(BIO *); -int BIO_flush(BIO *); -int BIO_eof(BIO *); -int BIO_set_close(BIO *,long); -int BIO_get_close(BIO *); -int BIO_pending(BIO *); -int BIO_wpending(BIO *); -int BIO_get_info_callback(BIO *, bio_info_cb **); -int BIO_set_info_callback(BIO *, bio_info_cb *); -long BIO_get_buffer_num_lines(BIO *); -long BIO_set_read_buffer_size(BIO *, long); -long BIO_set_write_buffer_size(BIO *, long); -long BIO_set_buffer_size(BIO *, long); -long BIO_set_buffer_read_data(BIO *, void *, long); - -/* The following was a macro in 0.9.8e. Once we drop support for RHEL/CentOS 5 - we should move this back to FUNCTIONS. */ -int BIO_method_type(const BIO *); +void BIO_set_retry_read(BIO *); +void BIO_clear_retry_flags(BIO *); """ CUSTOMIZATIONS = """ +#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 && !CRYPTOGRAPHY_IS_LIBRESSL +int BIO_up_ref(BIO *b) { + CRYPTO_add(&b->references, 1, CRYPTO_LOCK_BIO); + return 1; +} +#endif """ - -CONDITIONAL_NAMES = {} diff --git a/src/_cffi_src/openssl/callbacks.py b/src/_cffi_src/openssl/callbacks.py new file mode 100644 index 00000000..8ee01e0e --- /dev/null +++ b/src/_cffi_src/openssl/callbacks.py @@ -0,0 +1,168 @@ +# 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 + +INCLUDES = """ +#include <openssl/ssl.h> +#include <openssl/x509.h> +#include <openssl/x509_vfy.h> +#include <openssl/crypto.h> + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include <Windows.h> +#include <Wincrypt.h> +#include <Winsock2.h> +#else +#include <stdio.h> +#include <stdlib.h> +#include <pthread.h> +#endif +""" + +TYPES = """ +typedef struct { + char *password; + int length; + int called; + int error; + int maxsize; +} CRYPTOGRAPHY_PASSWORD_DATA; +""" + +FUNCTIONS = """ +int Cryptography_setup_ssl_threads(void); +int Cryptography_pem_password_cb(char *, int, int, void *); +""" + +CUSTOMIZATIONS = """ +/* This code is derived from the locking code found in the Python _ssl module's + locking callback for OpenSSL. + + Copyright 2001-2016 Python Software Foundation; All Rights Reserved. + + It has been subsequently modified to use cross platform locking without + using CPython APIs by Armin Rigo of the PyPy project. +*/ + +#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 +#ifdef _WIN32 +typedef CRITICAL_SECTION Cryptography_mutex; +static __inline void cryptography_mutex_init(Cryptography_mutex *mutex) { + InitializeCriticalSection(mutex); +} +static __inline void cryptography_mutex_lock(Cryptography_mutex *mutex) { + EnterCriticalSection(mutex); +} +static __inline void cryptography_mutex_unlock(Cryptography_mutex *mutex) { + LeaveCriticalSection(mutex); +} +#else +typedef pthread_mutex_t Cryptography_mutex; +#define ASSERT_STATUS(call) \ + if ((call) != 0) { \ + perror("Fatal error in callback initialization: " #call); \ + abort(); \ + } +static inline void cryptography_mutex_init(Cryptography_mutex *mutex) { +#if !defined(pthread_mutexattr_default) +# define pthread_mutexattr_default ((pthread_mutexattr_t *)NULL) +#endif + ASSERT_STATUS(pthread_mutex_init(mutex, pthread_mutexattr_default)); +} +static inline void cryptography_mutex_lock(Cryptography_mutex *mutex) { + ASSERT_STATUS(pthread_mutex_lock(mutex)); +} +static inline void cryptography_mutex_unlock(Cryptography_mutex *mutex) { + ASSERT_STATUS(pthread_mutex_unlock(mutex)); +} +#endif + + +static int _ssl_locks_count = 0; +static Cryptography_mutex *_ssl_locks = NULL; + +static void _ssl_thread_locking_function(int mode, int n, const char *file, + int line) { + /* this function is needed to perform locking on shared data + structures. (Note that OpenSSL uses a number of global data + structures that will be implicitly shared whenever multiple + threads use OpenSSL.) Multi-threaded applications will + crash at random if it is not set. + + locking_function() must be able to handle up to + CRYPTO_num_locks() different mutex locks. It sets the n-th + lock if mode & CRYPTO_LOCK, and releases it otherwise. + + file and line are the file number of the function setting the + lock. They can be useful for debugging. + */ + + if ((_ssl_locks == NULL) || + (n < 0) || (n >= _ssl_locks_count)) { + return; + } + + if (mode & CRYPTO_LOCK) { + cryptography_mutex_lock(_ssl_locks + n); + } else { + cryptography_mutex_unlock(_ssl_locks + n); + } +} + +static void init_mutexes(void) { + int i; + for (i = 0; i < _ssl_locks_count; i++) { + cryptography_mutex_init(_ssl_locks + i); + } +} + + +int Cryptography_setup_ssl_threads(void) { + if (_ssl_locks == NULL) { + _ssl_locks_count = CRYPTO_num_locks(); + _ssl_locks = calloc(_ssl_locks_count, sizeof(Cryptography_mutex)); + if (_ssl_locks == NULL) { + return 0; + } + init_mutexes(); + CRYPTO_set_locking_callback(_ssl_thread_locking_function); +#ifndef _WIN32 + pthread_atfork(NULL, NULL, &init_mutexes); +#endif + } + return 1; +} +#else +int (*Cryptography_setup_ssl_threads)(void) = NULL; +#endif + +typedef struct { + char *password; + int length; + int called; + int error; + int maxsize; +} CRYPTOGRAPHY_PASSWORD_DATA; + +int Cryptography_pem_password_cb(char *buf, int size, + int rwflag, void *userdata) { + /* The password cb is only invoked if OpenSSL decides the private + key is encrypted. So this path only occurs if it needs a password */ + CRYPTOGRAPHY_PASSWORD_DATA *st = (CRYPTOGRAPHY_PASSWORD_DATA *)userdata; + st->called += 1; + st->maxsize = size; + if (st->length == 0) { + st->error = -1; + return 0; + } else if (st->length < size) { + memcpy(buf, st->password, st->length); + return st->length; + } else { + st->error = -2; + return 0; + } +} +""" diff --git a/src/_cffi_src/openssl/cmac.py b/src/_cffi_src/openssl/cmac.py index c01a449f..557abd1c 100644 --- a/src/_cffi_src/openssl/cmac.py +++ b/src/_cffi_src/openssl/cmac.py @@ -5,20 +5,16 @@ from __future__ import absolute_import, division, print_function INCLUDES = """ -#if OPENSSL_VERSION_NUMBER >= 0x10001000L +#if !defined(OPENSSL_NO_CMAC) #include <openssl/cmac.h> #endif """ TYPES = """ -static const int Cryptography_HAS_CMAC; typedef ... CMAC_CTX; """ FUNCTIONS = """ -""" - -MACROS = """ CMAC_CTX *CMAC_CTX_new(void); int CMAC_Init(CMAC_CTX *, const void *, size_t, const EVP_CIPHER *, ENGINE *); int CMAC_Update(CMAC_CTX *, const void *, size_t); @@ -28,29 +24,4 @@ void CMAC_CTX_free(CMAC_CTX *); """ CUSTOMIZATIONS = """ -#if OPENSSL_VERSION_NUMBER < 0x10001000L - -static const long Cryptography_HAS_CMAC = 0; -typedef void CMAC_CTX; -CMAC_CTX *(*CMAC_CTX_new)(void) = NULL; -int (*CMAC_Init)(CMAC_CTX *, const void *, size_t, const EVP_CIPHER *, - ENGINE *) = NULL; -int (*CMAC_Update)(CMAC_CTX *, const void *, size_t) = NULL; -int (*CMAC_Final)(CMAC_CTX *, unsigned char *, size_t *) = NULL; -int (*CMAC_CTX_copy)(CMAC_CTX *, const CMAC_CTX *) = NULL; -void (*CMAC_CTX_free)(CMAC_CTX *) = NULL; -#else -static const long Cryptography_HAS_CMAC = 1; -#endif """ - -CONDITIONAL_NAMES = { - "Cryptography_HAS_CMAC": [ - "CMAC_CTX_new", - "CMAC_Init", - "CMAC_Update", - "CMAC_Final", - "CMAC_CTX_copy", - "CMAC_CTX_free", - ], -} diff --git a/src/_cffi_src/openssl/cms.py b/src/_cffi_src/openssl/cms.py deleted file mode 100644 index a43df5d9..00000000 --- a/src/_cffi_src/openssl/cms.py +++ /dev/null @@ -1,152 +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 - -INCLUDES = """ -#if !defined(OPENSSL_NO_CMS) && OPENSSL_VERSION_NUMBER >= 0x0090808fL -/* The next define should really be in the OpenSSL header, but it is missing. - Failing to include this on Windows causes compilation failures. */ -#if defined(OPENSSL_SYS_WINDOWS) -#include <windows.h> -#endif -#include <openssl/cms.h> -#endif -""" - -TYPES = """ -static const long Cryptography_HAS_CMS; - -typedef ... CMS_ContentInfo; -typedef ... CMS_SignerInfo; -typedef ... CMS_CertificateChoices; -typedef ... CMS_RevocationInfoChoice; -typedef ... CMS_RecipientInfo; -typedef ... CMS_ReceiptRequest; -typedef ... CMS_Receipt; - -static const int CMS_TEXT; -static const int CMS_NOCERTS; -static const int CMS_NO_CONTENT_VERIFY; -static const int CMS_NO_ATTR_VERIFY; -static const int CMS_NOSIGS; -static const int CMS_NOINTERN; -static const int CMS_NO_SIGNER_CERT_VERIFY; -static const int CMS_NOVERIFY; -static const int CMS_DETACHED; -static const int CMS_BINARY; -static const int CMS_NOATTR; -static const int CMS_NOSMIMECAP; -static const int CMS_NOOLDMIMETYPE; -static const int CMS_CRLFEOL; -static const int CMS_STREAM; -static const int CMS_NOCRL; -static const int CMS_PARTIAL; -static const int CMS_REUSE_DIGEST; -static const int CMS_USE_KEYID; -static const int CMS_DEBUG_DECRYPT; -""" - -FUNCTIONS = """ -""" - -MACROS = """ -BIO *BIO_new_CMS(BIO *, CMS_ContentInfo *); -int i2d_CMS_bio_stream(BIO *, CMS_ContentInfo *, BIO *, int); -int PEM_write_bio_CMS_stream(BIO *, CMS_ContentInfo *, BIO *, int); -int CMS_final(CMS_ContentInfo *, BIO *, BIO *, unsigned int); -CMS_ContentInfo *CMS_sign(X509 *, EVP_PKEY *, Cryptography_STACK_OF_X509 *, - BIO *, unsigned int); -int CMS_verify(CMS_ContentInfo *, Cryptography_STACK_OF_X509 *, X509_STORE *, - BIO *, BIO *, unsigned int); -CMS_ContentInfo *CMS_encrypt(Cryptography_STACK_OF_X509 *, BIO *, - const EVP_CIPHER *, unsigned int); -int CMS_decrypt(CMS_ContentInfo *, EVP_PKEY *, X509 *, BIO *, BIO *, - unsigned int); -CMS_SignerInfo *CMS_add1_signer(CMS_ContentInfo *, X509 *, EVP_PKEY *, - const EVP_MD *, unsigned int); -""" - -CUSTOMIZATIONS = """ -#if !defined(OPENSSL_NO_CMS) && OPENSSL_VERSION_NUMBER >= 0x0090808fL -static const long Cryptography_HAS_CMS = 1; -#else -static const long Cryptography_HAS_CMS = 0; -typedef void CMS_ContentInfo; -typedef void CMS_SignerInfo; -typedef void CMS_CertificateChoices; -typedef void CMS_RevocationInfoChoice; -typedef void CMS_RecipientInfo; -typedef void CMS_ReceiptRequest; -typedef void CMS_Receipt; -const long CMS_TEXT = 0; -const long CMS_NOCERTS = 0; -const long CMS_NO_CONTENT_VERIFY = 0; -const long CMS_NO_ATTR_VERIFY = 0; -const long CMS_NOSIGS = 0; -const long CMS_NOINTERN = 0; -const long CMS_NO_SIGNER_CERT_VERIFY = 0; -const long CMS_NOVERIFY = 0; -const long CMS_DETACHED = 0; -const long CMS_BINARY = 0; -const long CMS_NOATTR = 0; -const long CMS_NOSMIMECAP = 0; -const long CMS_NOOLDMIMETYPE = 0; -const long CMS_CRLFEOL = 0; -const long CMS_STREAM = 0; -const long CMS_NOCRL = 0; -const long CMS_PARTIAL = 0; -const long CMS_REUSE_DIGEST = 0; -const long CMS_USE_KEYID = 0; -const long CMS_DEBUG_DECRYPT = 0; -BIO *(*BIO_new_CMS)(BIO *, CMS_ContentInfo *) = NULL; -int (*i2d_CMS_bio_stream)(BIO *, CMS_ContentInfo *, BIO *, int) = NULL; -int (*PEM_write_bio_CMS_stream)(BIO *, CMS_ContentInfo *, BIO *, int) = NULL; -int (*CMS_final)(CMS_ContentInfo *, BIO *, BIO *, unsigned int) = NULL; -CMS_ContentInfo *(*CMS_sign)(X509 *, EVP_PKEY *, Cryptography_STACK_OF_X509 *, - BIO *, unsigned int) = NULL; -int (*CMS_verify)(CMS_ContentInfo *, Cryptography_STACK_OF_X509 *, - X509_STORE *, BIO *, BIO *, unsigned int) = NULL; -CMS_ContentInfo *(*CMS_encrypt)(Cryptography_STACK_OF_X509 *, BIO *, - const EVP_CIPHER *, unsigned int) = NULL; -int (*CMS_decrypt)(CMS_ContentInfo *, EVP_PKEY *, X509 *, BIO *, BIO *, - unsigned int) = NULL; -CMS_SignerInfo *(*CMS_add1_signer)(CMS_ContentInfo *, X509 *, EVP_PKEY *, - const EVP_MD *, unsigned int) = NULL; -#endif -""" - -CONDITIONAL_NAMES = { - "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", - ] -} diff --git a/src/_cffi_src/openssl/conf.py b/src/_cffi_src/openssl/conf.py index cab246f0..9db0162a 100644 --- a/src/_cffi_src/openssl/conf.py +++ b/src/_cffi_src/openssl/conf.py @@ -9,18 +9,13 @@ INCLUDES = """ """ TYPES = """ -typedef ... CONF; """ FUNCTIONS = """ void OPENSSL_config(const char *); +/* This is a macro in 1.1.0 */ void OPENSSL_no_config(void); """ -MACROS = """ -""" - CUSTOMIZATIONS = """ """ - -CONDITIONAL_NAMES = {} diff --git a/src/_cffi_src/openssl/crypto.py b/src/_cffi_src/openssl/crypto.py index 641e95a8..d8835442 100644 --- a/src/_cffi_src/openssl/crypto.py +++ b/src/_cffi_src/openssl/crypto.py @@ -9,50 +9,125 @@ INCLUDES = """ """ TYPES = """ -typedef ... CRYPTO_THREADID; +static const long Cryptography_HAS_LOCKING_CALLBACKS; +static const long Cryptography_HAS_MEM_FUNCTIONS; +static const long Cryptography_HAS_OPENSSL_CLEANUP; static const int SSLEAY_VERSION; static const int SSLEAY_CFLAGS; static const int SSLEAY_PLATFORM; static const int SSLEAY_DIR; static const int SSLEAY_BUILT_ON; +static const int OPENSSL_VERSION; +static const int OPENSSL_CFLAGS; +static const int OPENSSL_BUILT_ON; +static const int OPENSSL_PLATFORM; +static const int OPENSSL_DIR; static const int CRYPTO_MEM_CHECK_ON; static const int CRYPTO_MEM_CHECK_OFF; static const int CRYPTO_MEM_CHECK_ENABLE; static const int CRYPTO_MEM_CHECK_DISABLE; -static const int CRYPTO_LOCK; -static const int CRYPTO_UNLOCK; -static const int CRYPTO_READ; -static const int CRYPTO_WRITE; -static const int CRYPTO_LOCK_SSL; """ FUNCTIONS = """ -unsigned long SSLeay(void); -const char *SSLeay_version(int); - -void CRYPTO_free(void *); int CRYPTO_mem_ctrl(int); -int CRYPTO_is_mem_check_on(void); -void CRYPTO_mem_leaks(struct bio_st *); -void CRYPTO_cleanup_all_ex_data(void); -int CRYPTO_num_locks(void); -void CRYPTO_set_locking_callback(void(*)(int, int, const char *, int)); -void CRYPTO_set_id_callback(unsigned long (*)(void)); -unsigned long (*CRYPTO_get_id_callback(void))(void); + +void OPENSSL_cleanup(void); + +/* as of 1.1.0 OpenSSL does its own locking *angelic chorus*. This function + is now a noop macro. We can delete this once we drop 1.0.2 support. */ void (*CRYPTO_get_locking_callback(void))(int, int, const char *, int); -void CRYPTO_lock(int, int, const char *, int); +/* SSLeay was removed in 1.1.0 */ +unsigned long SSLeay(void); +const char *SSLeay_version(int); +/* these functions were added to replace the SSLeay functions in 1.1.0 */ +unsigned long OpenSSL_version_num(void); +const char *OpenSSL_version(int); + +/* this is a macro in 1.1.0 */ +void *OPENSSL_malloc(size_t); void OPENSSL_free(void *); -""" -MACROS = """ -void CRYPTO_add(int *, int, int); -void CRYPTO_malloc_init(void); -void CRYPTO_malloc_debug_init(void); + +/* Signature changed significantly in 1.1.0, only expose there for sanity */ +int Cryptography_CRYPTO_set_mem_functions( + void *(*)(size_t, const char *, int), + void *(*)(void *, size_t, const char *, int), + void (*)(void *, const char *, int)); + +void *Cryptography_malloc_wrapper(size_t, const char *, int); +void *Cryptography_realloc_wrapper(void *, size_t, const char *, int); +void Cryptography_free_wrapper(void *, const char *, int); """ CUSTOMIZATIONS = """ -""" +/* In 1.1.0 SSLeay has finally been retired. We bidirectionally define the + values so you can use either one. This is so we can use the new function + names no matter what OpenSSL we're running on, but users on older pyOpenSSL + releases won't see issues if they're running OpenSSL 1.1.0 */ +#if !defined(SSLEAY_VERSION) +# define SSLeay OpenSSL_version_num +# define SSLeay_version OpenSSL_version +# define SSLEAY_VERSION_NUMBER OPENSSL_VERSION_NUMBER +# define SSLEAY_VERSION OPENSSL_VERSION +# define SSLEAY_CFLAGS OPENSSL_CFLAGS +# define SSLEAY_BUILT_ON OPENSSL_BUILT_ON +# define SSLEAY_PLATFORM OPENSSL_PLATFORM +# define SSLEAY_DIR OPENSSL_DIR +#endif +#if !defined(OPENSSL_VERSION) +# define OpenSSL_version_num SSLeay +# define OpenSSL_version SSLeay_version +# define OPENSSL_VERSION SSLEAY_VERSION +# define OPENSSL_CFLAGS SSLEAY_CFLAGS +# define OPENSSL_BUILT_ON SSLEAY_BUILT_ON +# define OPENSSL_PLATFORM SSLEAY_PLATFORM +# define OPENSSL_DIR SSLEAY_DIR +#endif +#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 +static const long Cryptography_HAS_LOCKING_CALLBACKS = 1; +#else +static const long Cryptography_HAS_LOCKING_CALLBACKS = 0; +#endif + +#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 +static const long Cryptography_HAS_OPENSSL_CLEANUP = 0; + +void (*OPENSSL_cleanup)(void) = NULL; + +/* This function has a significantly different signature pre-1.1.0. since it is + * for testing only, we don't bother to expose it on older OpenSSLs. + */ +static const long Cryptography_HAS_MEM_FUNCTIONS = 0; +int (*Cryptography_CRYPTO_set_mem_functions)( + void *(*)(size_t, const char *, int), + void *(*)(void *, size_t, const char *, int), + void (*)(void *, const char *, int)) = NULL; -CONDITIONAL_NAMES = {} +#else +static const long Cryptography_HAS_OPENSSL_CLEANUP = 1; +static const long Cryptography_HAS_MEM_FUNCTIONS = 1; + +int Cryptography_CRYPTO_set_mem_functions( + void *(*m)(size_t, const char *, int), + void *(*r)(void *, size_t, const char *, int), + void (*f)(void *, const char *, int) +) { + return CRYPTO_set_mem_functions(m, r, f); +} +#endif + +void *Cryptography_malloc_wrapper(size_t size, const char *path, int line) { + return malloc(size); +} + +void *Cryptography_realloc_wrapper(void *ptr, size_t size, const char *path, + int line) { + return realloc(ptr, size); +} + +void Cryptography_free_wrapper(void *ptr, const char *path, int line) { + free(ptr); +} +""" diff --git a/src/_cffi_src/openssl/cryptography.py b/src/_cffi_src/openssl/cryptography.py new file mode 100644 index 00000000..cd583313 --- /dev/null +++ b/src/_cffi_src/openssl/cryptography.py @@ -0,0 +1,71 @@ +# 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 + +INCLUDES = """ +/* define our OpenSSL API compatibility level to 1.0.1. Any symbols older than + that will raise an error during compilation. We can raise this number again + after we drop 1.0.2 support in the distant future. */ +#define OPENSSL_API_COMPAT 0x10001000L + +#include <openssl/opensslv.h> + + +#if defined(LIBRESSL_VERSION_NUMBER) +#define CRYPTOGRAPHY_IS_LIBRESSL 1 +#else +#define CRYPTOGRAPHY_IS_LIBRESSL 0 +#endif + +/* + LibreSSL removed e_os2.h from the public headers so we'll only include it + if we're using vanilla OpenSSL. +*/ +#if !CRYPTOGRAPHY_IS_LIBRESSL +#include <openssl/e_os2.h> +#endif +#if defined(_WIN32) +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <Wincrypt.h> +#include <Winsock2.h> +#endif + +#define CRYPTOGRAPHY_OPENSSL_102L_OR_GREATER \ + (OPENSSL_VERSION_NUMBER >= 0x100020cf && !CRYPTOGRAPHY_IS_LIBRESSL) +#define CRYPTOGRAPHY_OPENSSL_110_OR_GREATER \ + (OPENSSL_VERSION_NUMBER >= 0x10100000 && !CRYPTOGRAPHY_IS_LIBRESSL) +#define CRYPTOGRAPHY_OPENSSL_110F_OR_GREATER \ + (OPENSSL_VERSION_NUMBER >= 0x1010006f && !CRYPTOGRAPHY_IS_LIBRESSL) + +#define CRYPTOGRAPHY_OPENSSL_LESS_THAN_102I \ + (OPENSSL_VERSION_NUMBER < 0x1000209f || CRYPTOGRAPHY_IS_LIBRESSL) +#define CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 \ + (OPENSSL_VERSION_NUMBER < 0x10100000 || CRYPTOGRAPHY_IS_LIBRESSL) +#define CRYPTOGRAPHY_OPENSSL_LESS_THAN_110J \ + (OPENSSL_VERSION_NUMBER < 0x101000af || CRYPTOGRAPHY_IS_LIBRESSL) +#define CRYPTOGRAPHY_OPENSSL_LESS_THAN_111 \ + (OPENSSL_VERSION_NUMBER < 0x10101000 || CRYPTOGRAPHY_IS_LIBRESSL) +#define CRYPTOGRAPHY_OPENSSL_LESS_THAN_111B \ + (OPENSSL_VERSION_NUMBER < 0x10101020 || CRYPTOGRAPHY_IS_LIBRESSL) +""" + +TYPES = """ +static const int CRYPTOGRAPHY_OPENSSL_102L_OR_GREATER; +static const int CRYPTOGRAPHY_OPENSSL_110_OR_GREATER; +static const int CRYPTOGRAPHY_OPENSSL_110F_OR_GREATER; + +static const int CRYPTOGRAPHY_OPENSSL_LESS_THAN_102I; +static const int CRYPTOGRAPHY_OPENSSL_LESS_THAN_111; +static const int CRYPTOGRAPHY_OPENSSL_LESS_THAN_111B; + +static const int CRYPTOGRAPHY_IS_LIBRESSL; +""" + +FUNCTIONS = """ +""" + +CUSTOMIZATIONS = """ +""" diff --git a/src/_cffi_src/openssl/ct.py b/src/_cffi_src/openssl/ct.py new file mode 100644 index 00000000..71125dd1 --- /dev/null +++ b/src/_cffi_src/openssl/ct.py @@ -0,0 +1,111 @@ +# 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 + +INCLUDES = """ +#if CRYPTOGRAPHY_OPENSSL_110_OR_GREATER +#include <openssl/ct.h> + +typedef STACK_OF(SCT) Cryptography_STACK_OF_SCT; +#endif +""" + +TYPES = """ +static const long Cryptography_HAS_SCT; + +typedef enum { + SCT_VERSION_NOT_SET, + SCT_VERSION_V1 +} sct_version_t; + +typedef enum { + CT_LOG_ENTRY_TYPE_NOT_SET, + CT_LOG_ENTRY_TYPE_X509, + CT_LOG_ENTRY_TYPE_PRECERT +} ct_log_entry_type_t; + +typedef enum { + SCT_SOURCE_UNKNOWN, + SCT_SOURCE_TLS_EXTENSION, + SCT_SOURCE_X509V3_EXTENSION, + SCT_SOURCE_OCSP_STAPLED_RESPONSE +} sct_source_t; + +typedef ... SCT; +typedef ... Cryptography_STACK_OF_SCT; +""" + +FUNCTIONS = """ +sct_version_t SCT_get_version(const SCT *); + +ct_log_entry_type_t SCT_get_log_entry_type(const SCT *); + +size_t SCT_get0_log_id(const SCT *, unsigned char **); + +size_t SCT_get0_signature(const SCT *, unsigned char **); + +uint64_t SCT_get_timestamp(const SCT *); + +int SCT_set_source(SCT *, sct_source_t); + +int sk_SCT_num(const Cryptography_STACK_OF_SCT *); +SCT *sk_SCT_value(const Cryptography_STACK_OF_SCT *, int); + +void SCT_LIST_free(Cryptography_STACK_OF_SCT *); + +int sk_SCT_push(Cryptography_STACK_OF_SCT *, SCT *); +Cryptography_STACK_OF_SCT *sk_SCT_new_null(void); +SCT *SCT_new(void); +int SCT_set1_log_id(SCT *, unsigned char *, size_t); +void SCT_set_timestamp(SCT *, uint64_t); +int SCT_set_version(SCT *, sct_version_t); +int SCT_set_log_entry_type(SCT *, ct_log_entry_type_t); +""" + +CUSTOMIZATIONS = """ +#if CRYPTOGRAPHY_OPENSSL_110_OR_GREATER +static const long Cryptography_HAS_SCT = 1; +#else +static const long Cryptography_HAS_SCT = 0; + +typedef enum { + SCT_VERSION_NOT_SET, + SCT_VERSION_V1 +} sct_version_t; +typedef enum { + CT_LOG_ENTRY_TYPE_NOT_SET, + CT_LOG_ENTRY_TYPE_X509, + CT_LOG_ENTRY_TYPE_PRECERT +} ct_log_entry_type_t; +typedef enum { + SCT_SOURCE_UNKNOWN, + SCT_SOURCE_TLS_EXTENSION, + SCT_SOURCE_X509V3_EXTENSION, + SCT_SOURCE_OCSP_STAPLED_RESPONSE +} sct_source_t; +typedef void SCT; +typedef void Cryptography_STACK_OF_SCT; + +sct_version_t (*SCT_get_version)(const SCT *) = NULL; +ct_log_entry_type_t (*SCT_get_log_entry_type)(const SCT *) = NULL; +size_t (*SCT_get0_log_id)(const SCT *, unsigned char **) = NULL; +size_t (*SCT_get0_signature)(const SCT *, unsigned char **) = NULL; +uint64_t (*SCT_get_timestamp)(const SCT *) = NULL; + +int (*SCT_set_source)(SCT *, sct_source_t) = NULL; + +int (*sk_SCT_num)(const Cryptography_STACK_OF_SCT *) = NULL; +SCT *(*sk_SCT_value)(const Cryptography_STACK_OF_SCT *, int) = NULL; + +void (*SCT_LIST_free)(Cryptography_STACK_OF_SCT *) = NULL; +int (*sk_SCT_push)(Cryptography_STACK_OF_SCT *, SCT *) = NULL; +Cryptography_STACK_OF_SCT *(*sk_SCT_new_null)(void) = NULL; +SCT *(*SCT_new)(void) = NULL; +int (*SCT_set1_log_id)(SCT *, unsigned char *, size_t) = NULL; +void (*SCT_set_timestamp)(SCT *, uint64_t) = NULL; +int (*SCT_set_version)(SCT *, sct_version_t) = NULL; +int (*SCT_set_log_entry_type)(SCT *, ct_log_entry_type_t) = NULL; +#endif +""" diff --git a/src/_cffi_src/openssl/dh.py b/src/_cffi_src/openssl/dh.py index b66e7196..0e1df23a 100644 --- a/src/_cffi_src/openssl/dh.py +++ b/src/_cffi_src/openssl/dh.py @@ -9,44 +9,230 @@ INCLUDES = """ """ TYPES = """ -typedef struct dh_st { - /* Prime number (shared) */ - BIGNUM *p; - /* Generator of Z_p (shared) */ - BIGNUM *g; - /* Private DH value x */ - BIGNUM *priv_key; - /* Public DH value g^x */ - BIGNUM *pub_key; - /* X9.42/RFC 2631 */ - BIGNUM *q; - BIGNUM *j; - ...; -} DH; +typedef ... DH; + +const long DH_NOT_SUITABLE_GENERATOR; """ FUNCTIONS = """ DH *DH_new(void); void DH_free(DH *); int DH_size(const DH *); -DH *DH_generate_parameters(int, int, void (*)(int, int, void *), void *); -int DH_check(const DH *, int *); -int DH_check_pub_key(const DH *, const BIGNUM *, int *); int DH_generate_key(DH *); int DH_compute_key(unsigned char *, const BIGNUM *, DH *); -int DH_set_ex_data(DH *, int, void *); -void *DH_get_ex_data(DH *, int); -DH *d2i_DHparams(DH **, const unsigned char **, long); -int i2d_DHparams(const DH *, unsigned char **); -int DHparams_print_fp(FILE *, const DH *); -int DHparams_print(BIO *, const DH *); -""" +DH *DHparams_dup(DH *); + +/* added in 1.1.0 when the DH struct was opaqued */ +void DH_get0_pqg(const DH *, const BIGNUM **, const BIGNUM **, + const BIGNUM **); +int DH_set0_pqg(DH *, BIGNUM *, BIGNUM *, BIGNUM *); +void DH_get0_key(const DH *, const BIGNUM **, const BIGNUM **); +int DH_set0_key(DH *, BIGNUM *, BIGNUM *); -MACROS = """ +int Cryptography_DH_check(const DH *, int *); int DH_generate_parameters_ex(DH *, int, int, BN_GENCB *); +DH *d2i_DHparams_bio(BIO *, DH **); +int i2d_DHparams_bio(BIO *, DH *); +DH *Cryptography_d2i_DHxparams_bio(BIO *bp, DH **x); +int Cryptography_i2d_DHxparams_bio(BIO *bp, DH *x); """ CUSTOMIZATIONS = """ -""" +/* These functions were added in OpenSSL 1.1.0 */ +#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 && !CRYPTOGRAPHY_IS_LIBRESSL +void DH_get0_pqg(const DH *dh, + const BIGNUM **p, const BIGNUM **q, const BIGNUM **g) +{ + if (p != NULL) + *p = dh->p; + if (q != NULL) + *q = dh->q; + if (g != NULL) + *g = dh->g; +} + +int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g) +{ + /* If the fields p and g in d are NULL, the corresponding input + * parameters MUST be non-NULL. q may remain NULL. + */ + if ((dh->p == NULL && p == NULL) + || (dh->g == NULL && g == NULL)) + return 0; + + if (p != NULL) { + BN_free(dh->p); + dh->p = p; + } + if (q != NULL) { + BN_free(dh->q); + dh->q = q; + } + if (g != NULL) { + BN_free(dh->g); + dh->g = g; + } + + if (q != NULL) { + dh->length = BN_num_bits(q); + } + + return 1; +} + +void DH_get0_key(const DH *dh, const BIGNUM **pub_key, const BIGNUM **priv_key) +{ + if (pub_key != NULL) + *pub_key = dh->pub_key; + if (priv_key != NULL) + *priv_key = dh->priv_key; +} + +int DH_set0_key(DH *dh, BIGNUM *pub_key, BIGNUM *priv_key) +{ + /* If the field pub_key in dh is NULL, the corresponding input + * parameters MUST be non-NULL. The priv_key field may + * be left NULL. + */ + if (dh->pub_key == NULL && pub_key == NULL) + return 0; + + if (pub_key != NULL) { + BN_free(dh->pub_key); + dh->pub_key = pub_key; + } + if (priv_key != NULL) { + BN_free(dh->priv_key); + dh->priv_key = priv_key; + } + + return 1; +} +#endif -CONDITIONAL_NAMES = {} +#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 +#ifndef DH_CHECK_Q_NOT_PRIME +#define DH_CHECK_Q_NOT_PRIME 0x10 +#endif + +#ifndef DH_CHECK_INVALID_Q_VALUE +#define DH_CHECK_INVALID_Q_VALUE 0x20 +#endif + +#ifndef DH_CHECK_INVALID_J_VALUE +#define DH_CHECK_INVALID_J_VALUE 0x40 +#endif + +/* DH_check implementation taken from OpenSSL 1.1.0pre6 */ + +/*- + * Check that p is a safe prime and + * if g is 2, 3 or 5, check that it is a suitable generator + * where + * for 2, p mod 24 == 11 + * for 3, p mod 12 == 5 + * for 5, p mod 10 == 3 or 7 + * should hold. + */ + +int Cryptography_DH_check(const DH *dh, int *ret) +{ + int ok = 0, r; + BN_CTX *ctx = NULL; + BN_ULONG l; + BIGNUM *t1 = NULL, *t2 = NULL; + + *ret = 0; + ctx = BN_CTX_new(); + if (ctx == NULL) + goto err; + BN_CTX_start(ctx); + t1 = BN_CTX_get(ctx); + if (t1 == NULL) + goto err; + t2 = BN_CTX_get(ctx); + if (t2 == NULL) + goto err; + + if (dh->q) { + if (BN_cmp(dh->g, BN_value_one()) <= 0) + *ret |= DH_NOT_SUITABLE_GENERATOR; + else if (BN_cmp(dh->g, dh->p) >= 0) + *ret |= DH_NOT_SUITABLE_GENERATOR; + else { + /* Check g^q == 1 mod p */ + if (!BN_mod_exp(t1, dh->g, dh->q, dh->p, ctx)) + goto err; + if (!BN_is_one(t1)) + *ret |= DH_NOT_SUITABLE_GENERATOR; + } + r = BN_is_prime_ex(dh->q, BN_prime_checks, ctx, NULL); + if (r < 0) + goto err; + if (!r) + *ret |= DH_CHECK_Q_NOT_PRIME; + /* Check p == 1 mod q i.e. q divides p - 1 */ + if (!BN_div(t1, t2, dh->p, dh->q, ctx)) + goto err; + if (!BN_is_one(t2)) + *ret |= DH_CHECK_INVALID_Q_VALUE; + if (dh->j && BN_cmp(dh->j, t1)) + *ret |= DH_CHECK_INVALID_J_VALUE; + + } else if (BN_is_word(dh->g, DH_GENERATOR_2)) { + l = BN_mod_word(dh->p, 24); + if (l == (BN_ULONG)-1) + goto err; + if (l != 11) + *ret |= DH_NOT_SUITABLE_GENERATOR; + } else if (BN_is_word(dh->g, DH_GENERATOR_5)) { + l = BN_mod_word(dh->p, 10); + if (l == (BN_ULONG)-1) + goto err; + if ((l != 3) && (l != 7)) + *ret |= DH_NOT_SUITABLE_GENERATOR; + } else + *ret |= DH_UNABLE_TO_CHECK_GENERATOR; + + r = BN_is_prime_ex(dh->p, BN_prime_checks, ctx, NULL); + if (r < 0) + goto err; + if (!r) + *ret |= DH_CHECK_P_NOT_PRIME; + else if (!dh->q) { + if (!BN_rshift1(t1, dh->p)) + goto err; + r = BN_is_prime_ex(t1, BN_prime_checks, ctx, NULL); + if (r < 0) + goto err; + if (!r) + *ret |= DH_CHECK_P_NOT_SAFE_PRIME; + } + ok = 1; + err: + if (ctx != NULL) { + BN_CTX_end(ctx); + BN_CTX_free(ctx); + } + return (ok); +} +#else +int Cryptography_DH_check(const DH *dh, int *ret) { + return DH_check(dh, ret); +} +#endif + +/* These functions were added in OpenSSL 1.1.0f commit d0c50e80a8 */ +/* Define our own to simplify support across all versions. */ +#if defined(EVP_PKEY_DHX) && EVP_PKEY_DHX != -1 +DH *Cryptography_d2i_DHxparams_bio(BIO *bp, DH **x) { + return ASN1_d2i_bio_of(DH, DH_new, d2i_DHxparams, bp, x); +} +int Cryptography_i2d_DHxparams_bio(BIO *bp, DH *x) { + return ASN1_i2d_bio_of_const(DH, i2d_DHxparams, bp, x); +} +#else +DH *(*Cryptography_d2i_DHxparams_bio)(BIO *bp, DH **x) = NULL; +int (*Cryptography_i2d_DHxparams_bio)(BIO *bp, DH *x) = NULL; +#endif +""" diff --git a/src/_cffi_src/openssl/dsa.py b/src/_cffi_src/openssl/dsa.py index 99a685df..938c18fc 100644 --- a/src/_cffi_src/openssl/dsa.py +++ b/src/_cffi_src/openssl/dsa.py @@ -9,48 +9,95 @@ INCLUDES = """ """ TYPES = """ -typedef struct dsa_st { - /* Prime number (public) */ - BIGNUM *p; - /* Subprime (160-bit, q | p-1, public) */ - BIGNUM *q; - /* Generator of subgroup (public) */ - BIGNUM *g; - /* Private key x */ - BIGNUM *priv_key; - /* Public key y = g^x */ - BIGNUM *pub_key; - ...; -} DSA; -typedef struct { - BIGNUM *r; - BIGNUM *s; -} DSA_SIG; +typedef ... DSA; """ FUNCTIONS = """ -DSA *DSA_generate_parameters(int, unsigned char *, int, int *, unsigned long *, - void (*)(int, int, void *), void *); int DSA_generate_key(DSA *); DSA *DSA_new(void); void DSA_free(DSA *); -DSA_SIG *DSA_SIG_new(void); -void DSA_SIG_free(DSA_SIG *); -int i2d_DSA_SIG(const DSA_SIG *, unsigned char **); -DSA_SIG *d2i_DSA_SIG(DSA_SIG **, const unsigned char **, long); +DSA *DSAparams_dup(DSA *); int DSA_size(const DSA *); int DSA_sign(int, const unsigned char *, int, unsigned char *, unsigned int *, DSA *); int DSA_verify(int, const unsigned char *, int, const unsigned char *, int, DSA *); -""" -MACROS = """ +/* added in 1.1.0 to access the opaque struct */ +void DSA_get0_pqg(const DSA *, const BIGNUM **, const BIGNUM **, + const BIGNUM **); +int DSA_set0_pqg(DSA *, BIGNUM *, BIGNUM *, BIGNUM *); +void DSA_get0_key(const DSA *, const BIGNUM **, const BIGNUM **); +int DSA_set0_key(DSA *, BIGNUM *, BIGNUM *); int DSA_generate_parameters_ex(DSA *, int, unsigned char *, int, int *, unsigned long *, BN_GENCB *); """ CUSTOMIZATIONS = """ -""" +/* These functions were added in OpenSSL 1.1.0 */ +#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 && !CRYPTOGRAPHY_IS_LIBRESSL +void DSA_get0_pqg(const DSA *d, + const BIGNUM **p, const BIGNUM **q, const BIGNUM **g) +{ + if (p != NULL) + *p = d->p; + if (q != NULL) + *q = d->q; + if (g != NULL) + *g = d->g; +} +int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g) +{ + /* If the fields p, q and g in d are NULL, the corresponding input + * parameters MUST be non-NULL. + */ + if ((d->p == NULL && p == NULL) + || (d->q == NULL && q == NULL) + || (d->g == NULL && g == NULL)) + return 0; + + if (p != NULL) { + BN_free(d->p); + d->p = p; + } + if (q != NULL) { + BN_free(d->q); + d->q = q; + } + if (g != NULL) { + BN_free(d->g); + d->g = g; + } -CONDITIONAL_NAMES = {} + return 1; +} +void DSA_get0_key(const DSA *d, + const BIGNUM **pub_key, const BIGNUM **priv_key) +{ + if (pub_key != NULL) + *pub_key = d->pub_key; + if (priv_key != NULL) + *priv_key = d->priv_key; +} +int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key) +{ + /* If the field pub_key in d is NULL, the corresponding input + * parameters MUST be non-NULL. The priv_key field may + * be left NULL. + */ + if (d->pub_key == NULL && pub_key == NULL) + return 0; + + if (pub_key != NULL) { + BN_free(d->pub_key); + d->pub_key = pub_key; + } + if (priv_key != NULL) { + BN_free(d->priv_key); + d->priv_key = priv_key; + } + + return 1; +} +#endif +""" diff --git a/src/_cffi_src/openssl/ec.py b/src/_cffi_src/openssl/ec.py index c5052d36..6432fc22 100644 --- a/src/_cffi_src/openssl/ec.py +++ b/src/_cffi_src/openssl/ec.py @@ -5,19 +5,12 @@ from __future__ import absolute_import, division, print_function INCLUDES = """ -#ifndef OPENSSL_NO_EC #include <openssl/ec.h> -#endif - #include <openssl/obj_mac.h> """ TYPES = """ -static const int Cryptography_HAS_EC; -static const int Cryptography_HAS_EC_1_0_1; -static const int Cryptography_HAS_EC_NISTP_64_GCC_128; static const int Cryptography_HAS_EC2M; -static const int Cryptography_HAS_EC_1_0_2; static const int OPENSSL_EC_NAMED_CURVE; @@ -29,32 +22,18 @@ typedef struct { int nid; const char *comment; } EC_builtin_curve; -typedef enum { ... } point_conversion_form_t; +typedef enum { + POINT_CONVERSION_COMPRESSED, + POINT_CONVERSION_UNCOMPRESSED, + ... +} point_conversion_form_t; """ FUNCTIONS = """ -""" - -MACROS = """ -EC_GROUP *EC_GROUP_new(const EC_METHOD *); void EC_GROUP_free(EC_GROUP *); -void EC_GROUP_clear_free(EC_GROUP *); -EC_GROUP *EC_GROUP_new_curve_GFp( - const BIGNUM *, const BIGNUM *, const BIGNUM *, BN_CTX *); -EC_GROUP *EC_GROUP_new_curve_GF2m( - const BIGNUM *, const BIGNUM *, const BIGNUM *, BN_CTX *); EC_GROUP *EC_GROUP_new_by_curve_name(int); -int EC_GROUP_set_curve_GFp( - EC_GROUP *, const BIGNUM *, const BIGNUM *, const BIGNUM *, BN_CTX *); -int EC_GROUP_get_curve_GFp( - const EC_GROUP *, BIGNUM *, BIGNUM *, BIGNUM *, BN_CTX *); -int EC_GROUP_set_curve_GF2m( - EC_GROUP *, const BIGNUM *, const BIGNUM *, const BIGNUM *, BN_CTX *); -int EC_GROUP_get_curve_GF2m( - const EC_GROUP *, BIGNUM *, BIGNUM *, BIGNUM *, BN_CTX *); - int EC_GROUP_get_degree(const EC_GROUP *); const EC_METHOD *EC_GROUP_method_of(const EC_GROUP *); @@ -63,15 +42,10 @@ int EC_GROUP_get_curve_name(const EC_GROUP *); size_t EC_get_builtin_curves(EC_builtin_curve *, size_t); +EC_KEY *EC_KEY_new(void); void EC_KEY_free(EC_KEY *); -int EC_KEY_get_flags(const EC_KEY *); -void EC_KEY_set_flags(EC_KEY *, int); -void EC_KEY_clear_flags(EC_KEY *, int); EC_KEY *EC_KEY_new_by_curve_name(int); -EC_KEY *EC_KEY_copy(EC_KEY *, const EC_KEY *); -EC_KEY *EC_KEY_dup(const EC_KEY *); -int EC_KEY_up_ref(EC_KEY *); const EC_GROUP *EC_KEY_get0_group(const EC_KEY *); int EC_GROUP_get_order(const EC_GROUP *, BIGNUM *, BN_CTX *); int EC_KEY_set_group(EC_KEY *, const EC_GROUP *); @@ -79,43 +53,14 @@ const BIGNUM *EC_KEY_get0_private_key(const EC_KEY *); int EC_KEY_set_private_key(EC_KEY *, const BIGNUM *); const EC_POINT *EC_KEY_get0_public_key(const EC_KEY *); int EC_KEY_set_public_key(EC_KEY *, const EC_POINT *); -unsigned int EC_KEY_get_enc_flags(const EC_KEY *); -void EC_KEY_set_enc_flags(EC_KEY *eckey, unsigned int); -point_conversion_form_t EC_KEY_get_conv_form(const EC_KEY *); -void EC_KEY_set_conv_form(EC_KEY *, point_conversion_form_t); -void *EC_KEY_get_key_method_data( - EC_KEY *, - void *(*)(void *), - void (*)(void *), - void (*)(void *) -); -void EC_KEY_insert_key_method_data( - EC_KEY *, - void *, - void *(*)(void *), - void (*)(void *), - void (*)(void *) -); void EC_KEY_set_asn1_flag(EC_KEY *, int); -int EC_KEY_precompute_mult(EC_KEY *, BN_CTX *); int EC_KEY_generate_key(EC_KEY *); -int EC_KEY_check_key(const EC_KEY *); int EC_KEY_set_public_key_affine_coordinates(EC_KEY *, BIGNUM *, BIGNUM *); EC_POINT *EC_POINT_new(const EC_GROUP *); void EC_POINT_free(EC_POINT *); void EC_POINT_clear_free(EC_POINT *); -int EC_POINT_copy(EC_POINT *, const EC_POINT *); EC_POINT *EC_POINT_dup(const EC_POINT *, const EC_GROUP *); -const EC_METHOD *EC_POINT_method_of(const EC_POINT *); - -int EC_POINT_set_to_infinity(const EC_GROUP *, EC_POINT *); - -int EC_POINT_set_Jprojective_coordinates_GFp(const EC_GROUP *, EC_POINT *, - const BIGNUM *, const BIGNUM *, const BIGNUM *, BN_CTX *); - -int EC_POINT_get_Jprojective_coordinates_GFp(const EC_GROUP *, - const EC_POINT *, BIGNUM *, BIGNUM *, BIGNUM *, BN_CTX *); int EC_POINT_set_affine_coordinates_GFp(const EC_GROUP *, EC_POINT *, const BIGNUM *, const BIGNUM *, BN_CTX *); @@ -142,18 +87,6 @@ size_t EC_POINT_point2oct(const EC_GROUP *, const EC_POINT *, int EC_POINT_oct2point(const EC_GROUP *, EC_POINT *, const unsigned char *, size_t, BN_CTX *); -BIGNUM *EC_POINT_point2bn(const EC_GROUP *, const EC_POINT *, - point_conversion_form_t form, BIGNUM *, BN_CTX *); - -EC_POINT *EC_POINT_bn2point(const EC_GROUP *, const BIGNUM *, - EC_POINT *, BN_CTX *); - -char *EC_POINT_point2hex(const EC_GROUP *, const EC_POINT *, - point_conversion_form_t form, BN_CTX *); - -EC_POINT *EC_POINT_hex2point(const EC_GROUP *, const char *, - EC_POINT *, BN_CTX *); - int EC_POINT_add(const EC_GROUP *, EC_POINT *, const EC_POINT *, const EC_POINT *, BN_CTX *); @@ -165,209 +98,20 @@ int EC_POINT_is_on_curve(const EC_GROUP *, const EC_POINT *, BN_CTX *); int EC_POINT_cmp( const EC_GROUP *, const EC_POINT *, const EC_POINT *, BN_CTX *); -int EC_POINT_make_affine(const EC_GROUP *, EC_POINT *, BN_CTX *); -int EC_POINTs_make_affine(const EC_GROUP *, size_t, EC_POINT *[], BN_CTX *); - -int EC_POINTs_mul( - const EC_GROUP *, EC_POINT *, const BIGNUM *, - size_t, const EC_POINT *[], const BIGNUM *[], BN_CTX *); - int EC_POINT_mul(const EC_GROUP *, EC_POINT *, const BIGNUM *, const EC_POINT *, const BIGNUM *, BN_CTX *); -int EC_GROUP_precompute_mult(EC_GROUP *, BN_CTX *); -int EC_GROUP_have_precompute_mult(const EC_GROUP *); - -const EC_METHOD *EC_GFp_simple_method(); -const EC_METHOD *EC_GFp_mont_method(); -const EC_METHOD *EC_GFp_nist_method(); - -const EC_METHOD *EC_GFp_nistp224_method(); -const EC_METHOD *EC_GFp_nistp256_method(); -const EC_METHOD *EC_GFp_nistp521_method(); - -const EC_METHOD *EC_GF2m_simple_method(); - int EC_METHOD_get_field_type(const EC_METHOD *); const char *EC_curve_nid2nist(int); + +int EC_GROUP_get_asn1_flag(const EC_GROUP *); """ CUSTOMIZATIONS = """ -#ifdef OPENSSL_NO_EC -static const long Cryptography_HAS_EC = 0; - -typedef void EC_KEY; -typedef void EC_GROUP; -typedef void EC_POINT; -typedef void EC_METHOD; -typedef struct { - int nid; - const char *comment; -} EC_builtin_curve; -typedef long point_conversion_form_t; - -static const int OPENSSL_EC_NAMED_CURVE = 0; - -void (*EC_KEY_free)(EC_KEY *) = NULL; -size_t (*EC_get_builtin_curves)(EC_builtin_curve *, size_t) = NULL; -EC_KEY *(*EC_KEY_new_by_curve_name)(int) = NULL; -EC_KEY *(*EC_KEY_copy)(EC_KEY *, const EC_KEY *) = NULL; -EC_KEY *(*EC_KEY_dup)(const EC_KEY *) = NULL; -int (*EC_KEY_up_ref)(EC_KEY *) = NULL; -const EC_GROUP *(*EC_KEY_get0_group)(const EC_KEY *) = NULL; -int (*EC_GROUP_get_order)(const EC_GROUP *, BIGNUM *, BN_CTX *) = NULL; -int (*EC_KEY_set_group)(EC_KEY *, const EC_GROUP *) = NULL; -const BIGNUM *(*EC_KEY_get0_private_key)(const EC_KEY *) = NULL; -int (*EC_KEY_set_private_key)(EC_KEY *, const BIGNUM *) = NULL; -const EC_POINT *(*EC_KEY_get0_public_key)(const EC_KEY *) = NULL; -int (*EC_KEY_set_public_key)(EC_KEY *, const EC_POINT *) = NULL; -unsigned int (*EC_KEY_get_enc_flags)(const EC_KEY *) = NULL; -void (*EC_KEY_set_enc_flags)(EC_KEY *eckey, unsigned int) = NULL; -point_conversion_form_t (*EC_KEY_get_conv_form)(const EC_KEY *) = NULL; -void (*EC_KEY_set_conv_form)(EC_KEY *, point_conversion_form_t) = NULL; -void *(*EC_KEY_get_key_method_data)( - EC_KEY *, void *(*)(void *), void (*)(void *), void (*)(void *)) = NULL; -void (*EC_KEY_insert_key_method_data)( - EC_KEY *, void *, - void *(*)(void *), void (*)(void *), void (*)(void *)) = NULL; -void (*EC_KEY_set_asn1_flag)(EC_KEY *, int) = NULL; -int (*EC_KEY_precompute_mult)(EC_KEY *, BN_CTX *) = NULL; -int (*EC_KEY_generate_key)(EC_KEY *) = NULL; -int (*EC_KEY_check_key)(const EC_KEY *) = NULL; - -EC_GROUP *(*EC_GROUP_new)(const EC_METHOD *); -void (*EC_GROUP_free)(EC_GROUP *); -void (*EC_GROUP_clear_free)(EC_GROUP *); - -EC_GROUP *(*EC_GROUP_new_curve_GFp)( - const BIGNUM *, const BIGNUM *, const BIGNUM *, BN_CTX *); - -EC_GROUP *(*EC_GROUP_new_by_curve_name)(int); - -int (*EC_GROUP_set_curve_GFp)( - EC_GROUP *, const BIGNUM *, const BIGNUM *, const BIGNUM *, BN_CTX *); - -int (*EC_GROUP_get_curve_GFp)( - const EC_GROUP *, BIGNUM *, BIGNUM *, BIGNUM *, BN_CTX *); - -int (*EC_GROUP_get_degree)(const EC_GROUP *) = NULL; - -const EC_METHOD *(*EC_GROUP_method_of)(const EC_GROUP *) = NULL; -const EC_POINT *(*EC_GROUP_get0_generator)(const EC_GROUP *) = NULL; -int (*EC_GROUP_get_curve_name)(const EC_GROUP *) = NULL; - -EC_POINT *(*EC_POINT_new)(const EC_GROUP *) = NULL; -void (*EC_POINT_free)(EC_POINT *) = NULL; -void (*EC_POINT_clear_free)(EC_POINT *) = NULL; -int (*EC_POINT_copy)(EC_POINT *, const EC_POINT *) = NULL; -EC_POINT *(*EC_POINT_dup)(const EC_POINT *, const EC_GROUP *) = NULL; -const EC_METHOD *(*EC_POINT_method_of)(const EC_POINT *) = NULL; -int (*EC_POINT_set_to_infinity)(const EC_GROUP *, EC_POINT *) = NULL; -int (*EC_POINT_set_Jprojective_coordinates_GFp)(const EC_GROUP *, EC_POINT *, - const BIGNUM *, const BIGNUM *, const BIGNUM *, BN_CTX *) = NULL; - -int (*EC_POINT_get_Jprojective_coordinates_GFp)(const EC_GROUP *, - const EC_POINT *, BIGNUM *, BIGNUM *, BIGNUM *, BN_CTX *) = NULL; - -int (*EC_POINT_set_affine_coordinates_GFp)(const EC_GROUP *, EC_POINT *, - const BIGNUM *, const BIGNUM *, BN_CTX *) = NULL; - -int (*EC_POINT_get_affine_coordinates_GFp)(const EC_GROUP *, - const EC_POINT *, BIGNUM *, BIGNUM *, BN_CTX *) = NULL; - -int (*EC_POINT_set_compressed_coordinates_GFp)(const EC_GROUP *, EC_POINT *, - const BIGNUM *, int, BN_CTX *) = NULL; - -size_t (*EC_POINT_point2oct)(const EC_GROUP *, const EC_POINT *, - point_conversion_form_t, - unsigned char *, size_t, BN_CTX *) = NULL; - -int (*EC_POINT_oct2point)(const EC_GROUP *, EC_POINT *, - const unsigned char *, size_t, BN_CTX *) = NULL; - -BIGNUM *(*EC_POINT_point2bn)(const EC_GROUP *, const EC_POINT *, - point_conversion_form_t form, BIGNUM *, BN_CTX *) = NULL; - -EC_POINT *(*EC_POINT_bn2point)(const EC_GROUP *, const BIGNUM *, - EC_POINT *, BN_CTX *) = NULL; - -char *(*EC_POINT_point2hex)(const EC_GROUP *, const EC_POINT *, - point_conversion_form_t form, BN_CTX *) = NULL; - -EC_POINT *(*EC_POINT_hex2point)(const EC_GROUP *, const char *, - EC_POINT *, BN_CTX *) = NULL; - -int (*EC_POINT_add)(const EC_GROUP *, EC_POINT *, const EC_POINT *, - const EC_POINT *, BN_CTX *) = NULL; - -int (*EC_POINT_dbl)(const EC_GROUP *, EC_POINT *, const EC_POINT *, - BN_CTX *) = NULL; - -int (*EC_POINT_invert)(const EC_GROUP *, EC_POINT *, BN_CTX *) = NULL; -int (*EC_POINT_is_at_infinity)(const EC_GROUP *, const EC_POINT *) = NULL; - -int (*EC_POINT_is_on_curve)(const EC_GROUP *, const EC_POINT *, - BN_CTX *) = NULL; - -int (*EC_POINT_cmp)( - const EC_GROUP *, const EC_POINT *, const EC_POINT *, BN_CTX *) = NULL; - -int (*EC_POINT_make_affine)(const EC_GROUP *, EC_POINT *, BN_CTX *) = NULL; - -int (*EC_POINTs_make_affine)(const EC_GROUP *, size_t, EC_POINT *[], - BN_CTX *) = NULL; - -int (*EC_POINTs_mul)( - const EC_GROUP *, EC_POINT *, const BIGNUM *, - size_t, const EC_POINT *[], const BIGNUM *[], BN_CTX *) = NULL; - -int (*EC_POINT_mul)(const EC_GROUP *, EC_POINT *, const BIGNUM *, - const EC_POINT *, const BIGNUM *, BN_CTX *) = NULL; - -int (*EC_GROUP_precompute_mult)(EC_GROUP *, BN_CTX *) = NULL; -int (*EC_GROUP_have_precompute_mult)(const EC_GROUP *) = NULL; - -const EC_METHOD *(*EC_GFp_simple_method)() = NULL; -const EC_METHOD *(*EC_GFp_mont_method)() = NULL; -const EC_METHOD *(*EC_GFp_nist_method)() = NULL; - -int (*EC_METHOD_get_field_type)(const EC_METHOD *) = NULL; - -#else -static const long Cryptography_HAS_EC = 1; -#endif - -#if defined(OPENSSL_NO_EC) || OPENSSL_VERSION_NUMBER < 0x1000100f -static const long Cryptography_HAS_EC_1_0_1 = 0; - -int (*EC_KEY_get_flags)(const EC_KEY *) = NULL; -void (*EC_KEY_set_flags)(EC_KEY *, int) = NULL; -void (*EC_KEY_clear_flags)(EC_KEY *, int) = NULL; - -int (*EC_KEY_set_public_key_affine_coordinates)( - EC_KEY *, BIGNUM *, BIGNUM *) = NULL; -#else -static const long Cryptography_HAS_EC_1_0_1 = 1; -#endif - - -#if defined(OPENSSL_NO_EC) || OPENSSL_VERSION_NUMBER < 0x1000100f || \ - defined(OPENSSL_NO_EC_NISTP_64_GCC_128) -static const long Cryptography_HAS_EC_NISTP_64_GCC_128 = 0; - -const EC_METHOD *(*EC_GFp_nistp224_method)(void) = NULL; -const EC_METHOD *(*EC_GFp_nistp256_method)(void) = NULL; -const EC_METHOD *(*EC_GFp_nistp521_method)(void) = NULL; -#else -static const long Cryptography_HAS_EC_NISTP_64_GCC_128 = 1; -#endif - -#if defined(OPENSSL_NO_EC) || defined(OPENSSL_NO_EC2M) +#if defined(OPENSSL_NO_EC2M) static const long Cryptography_HAS_EC2M = 0; -const EC_METHOD *(*EC_GF2m_simple_method)() = NULL; - int (*EC_POINT_set_affine_coordinates_GF2m)(const EC_GROUP *, EC_POINT *, const BIGNUM *, const BIGNUM *, BN_CTX *) = NULL; @@ -376,121 +120,7 @@ int (*EC_POINT_get_affine_coordinates_GF2m)(const EC_GROUP *, int (*EC_POINT_set_compressed_coordinates_GF2m)(const EC_GROUP *, EC_POINT *, const BIGNUM *, int, BN_CTX *) = NULL; - -int (*EC_GROUP_set_curve_GF2m)( - EC_GROUP *, const BIGNUM *, const BIGNUM *, const BIGNUM *, BN_CTX *); - -int (*EC_GROUP_get_curve_GF2m)( - const EC_GROUP *, BIGNUM *, BIGNUM *, BIGNUM *, BN_CTX *); - -EC_GROUP *(*EC_GROUP_new_curve_GF2m)( - const BIGNUM *, const BIGNUM *, const BIGNUM *, BN_CTX *); #else static const long Cryptography_HAS_EC2M = 1; #endif - -#if defined(OPENSSL_NO_EC) || OPENSSL_VERSION_NUMBER < 0x1000200f || \ - defined(LIBRESSL_VERSION_NUMBER) -static const long Cryptography_HAS_EC_1_0_2 = 0; -const char *(*EC_curve_nid2nist)(int) = NULL; -#else -static const long Cryptography_HAS_EC_1_0_2 = 1; -#endif """ - -CONDITIONAL_NAMES = { - "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_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", - ], - - "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_EC_NISTP_64_GCC_128": [ - "EC_GFp_nistp224_method", - "EC_GFp_nistp256_method", - "EC_GFp_nistp521_method", - ], - - "Cryptography_HAS_EC2M": [ - "EC_GF2m_simple_method", - "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": [ - "EC_curve_nid2nist", - ], -} diff --git a/src/_cffi_src/openssl/ecdh.py b/src/_cffi_src/openssl/ecdh.py index 6c7e010c..c73cc9f3 100644 --- a/src/_cffi_src/openssl/ecdh.py +++ b/src/_cffi_src/openssl/ecdh.py @@ -5,55 +5,17 @@ from __future__ import absolute_import, division, print_function INCLUDES = """ -#ifndef OPENSSL_NO_ECDH #include <openssl/ecdh.h> -#endif """ TYPES = """ -static const int Cryptography_HAS_ECDH; """ FUNCTIONS = """ -""" - -MACROS = """ int ECDH_compute_key(void *, size_t, const EC_POINT *, EC_KEY *, void *(*)(const void *, size_t, void *, size_t *)); - -int ECDH_get_ex_new_index(long, void *, CRYPTO_EX_new *, CRYPTO_EX_dup *, - CRYPTO_EX_free *); - -int ECDH_set_ex_data(EC_KEY *, int, void *); - -void *ECDH_get_ex_data(EC_KEY *, int); +long SSL_CTX_set_ecdh_auto(SSL_CTX *, int); """ CUSTOMIZATIONS = """ -#ifdef OPENSSL_NO_ECDH -static const long Cryptography_HAS_ECDH = 0; - -int (*ECDH_compute_key)(void *, size_t, const EC_POINT *, EC_KEY *, - void *(*)(const void *, size_t, void *, - size_t *)) = NULL; - -int (*ECDH_get_ex_new_index)(long, void *, CRYPTO_EX_new *, CRYPTO_EX_dup *, - CRYPTO_EX_free *) = NULL; - -int (*ECDH_set_ex_data)(EC_KEY *, int, void *) = NULL; - -void *(*ECDH_get_ex_data)(EC_KEY *, int) = NULL; - -#else -static const long Cryptography_HAS_ECDH = 1; -#endif """ - -CONDITIONAL_NAMES = { - "Cryptography_HAS_ECDH": [ - "ECDH_compute_key", - "ECDH_get_ex_new_index", - "ECDH_set_ex_data", - "ECDH_get_ex_data", - ], -} diff --git a/src/_cffi_src/openssl/ecdsa.py b/src/_cffi_src/openssl/ecdsa.py index db21025c..44a778a6 100644 --- a/src/_cffi_src/openssl/ecdsa.py +++ b/src/_cffi_src/openssl/ecdsa.py @@ -5,18 +5,13 @@ from __future__ import absolute_import, division, print_function INCLUDES = """ -#ifndef OPENSSL_NO_ECDSA #include <openssl/ecdsa.h> -#endif """ TYPES = """ static const int Cryptography_HAS_ECDSA; -typedef struct { - BIGNUM *r; - BIGNUM *s; -} ECDSA_SIG; +typedef ... ECDSA_SIG; typedef ... CRYPTO_EX_new; typedef ... CRYPTO_EX_dup; @@ -24,98 +19,20 @@ typedef ... CRYPTO_EX_free; """ FUNCTIONS = """ -""" - -MACROS = """ ECDSA_SIG *ECDSA_SIG_new(); void ECDSA_SIG_free(ECDSA_SIG *); int i2d_ECDSA_SIG(const ECDSA_SIG *, unsigned char **); ECDSA_SIG *d2i_ECDSA_SIG(ECDSA_SIG **s, const unsigned char **, long); ECDSA_SIG *ECDSA_do_sign(const unsigned char *, int, EC_KEY *); -ECDSA_SIG *ECDSA_do_sign_ex(const unsigned char *, int, const BIGNUM *, - const BIGNUM *, EC_KEY *); int ECDSA_do_verify(const unsigned char *, int, const ECDSA_SIG *, EC_KEY *); -int ECDSA_sign_setup(EC_KEY *, BN_CTX *, BIGNUM **, BIGNUM **); int ECDSA_sign(int, const unsigned char *, int, unsigned char *, unsigned int *, EC_KEY *); -int ECDSA_sign_ex(int, const unsigned char *, int dgstlen, unsigned char *, - unsigned int *, const BIGNUM *, const BIGNUM *, EC_KEY *); int ECDSA_verify(int, const unsigned char *, int, const unsigned char *, int, EC_KEY *); int ECDSA_size(const EC_KEY *); -const ECDSA_METHOD *ECDSA_OpenSSL(); -void ECDSA_set_default_method(const ECDSA_METHOD *); -const ECDSA_METHOD *ECDSA_get_default_method(); -int ECDSA_get_ex_new_index(long, void *, CRYPTO_EX_new *, - CRYPTO_EX_dup *, CRYPTO_EX_free *); -int ECDSA_set_method(EC_KEY *, const ECDSA_METHOD *); -int ECDSA_set_ex_data(EC_KEY *, int, void *); -void *ECDSA_get_ex_data(EC_KEY *, int); """ CUSTOMIZATIONS = """ -#ifdef OPENSSL_NO_ECDSA -static const long Cryptography_HAS_ECDSA = 0; - -typedef struct { - BIGNUM *r; - BIGNUM *s; -} ECDSA_SIG; - -ECDSA_SIG* (*ECDSA_SIG_new)() = NULL; -void (*ECDSA_SIG_free)(ECDSA_SIG *) = NULL; -int (*i2d_ECDSA_SIG)(const ECDSA_SIG *, unsigned char **) = NULL; -ECDSA_SIG* (*d2i_ECDSA_SIG)(ECDSA_SIG **s, const unsigned char **, - long) = NULL; -ECDSA_SIG* (*ECDSA_do_sign)(const unsigned char *, int, EC_KEY *eckey) = NULL; -ECDSA_SIG* (*ECDSA_do_sign_ex)(const unsigned char *, int, const BIGNUM *, - const BIGNUM *, EC_KEY *) = NULL; -int (*ECDSA_do_verify)(const unsigned char *, int, const ECDSA_SIG *, - EC_KEY *) = NULL; -int (*ECDSA_sign_setup)(EC_KEY *, BN_CTX *, BIGNUM **, BIGNUM **) = NULL; -int (*ECDSA_sign)(int, const unsigned char *, int, unsigned char *, - unsigned int *, EC_KEY *) = NULL; -int (*ECDSA_sign_ex)(int, const unsigned char *, int dgstlen, unsigned char *, - unsigned int *, const BIGNUM *, const BIGNUM *, - EC_KEY *) = NULL; -int (*ECDSA_verify)(int, const unsigned char *, int, const unsigned char *, - int, EC_KEY *) = NULL; -int (*ECDSA_size)(const EC_KEY *) = NULL; - -const ECDSA_METHOD* (*ECDSA_OpenSSL)() = NULL; -void (*ECDSA_set_default_method)(const ECDSA_METHOD *) = NULL; -const ECDSA_METHOD* (*ECDSA_get_default_method)() = NULL; -int (*ECDSA_set_method)(EC_KEY *, const ECDSA_METHOD *) = NULL; -int (*ECDSA_get_ex_new_index)(long, void *, CRYPTO_EX_new *, - CRYPTO_EX_dup *, CRYPTO_EX_free *) = NULL; -int (*ECDSA_set_ex_data)(EC_KEY *, int, void *) = NULL; -void* (*ECDSA_get_ex_data)(EC_KEY *, int) = NULL; -#else static const long Cryptography_HAS_ECDSA = 1; -#endif """ - -CONDITIONAL_NAMES = { - "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", - ], -} diff --git a/src/_cffi_src/openssl/engine.py b/src/_cffi_src/openssl/engine.py index bc5c1906..fa503a26 100644 --- a/src/_cffi_src/openssl/engine.py +++ b/src/_cffi_src/openssl/engine.py @@ -9,169 +9,42 @@ INCLUDES = """ """ TYPES = """ -static const long Cryptography_HAS_ENGINE_CRYPTODEV; - -struct rand_meth_st { - void (*seed)(const void *buf, int num); - int (*bytes)(unsigned char *buf, int num); - void (*cleanup)(void); - void (*add)(const void *buf, int num, double entropy); - int (*pseudorand)(unsigned char *buf, int num); - int (*status)(void); -}; - typedef ... ENGINE; -typedef ... RSA_METHOD; -typedef ... DSA_METHOD; -typedef ... ECDH_METHOD; -typedef ... ECDSA_METHOD; -typedef ... DH_METHOD; -typedef struct rand_meth_st RAND_METHOD; -typedef ... STORE_METHOD; -typedef int(*ENGINE_GEN_INT_FUNC_PTR)(ENGINE*); -typedef ... *ENGINE_CTRL_FUNC_PTR; -typedef ... *ENGINE_LOAD_KEY_PTR; -typedef ... *ENGINE_CIPHERS_PTR; -typedef ... *ENGINE_DIGESTS_PTR; -typedef ... ENGINE_CMD_DEFN; -typedef ... UI_METHOD; -static const unsigned int ENGINE_METHOD_RSA; -static const unsigned int ENGINE_METHOD_DSA; -static const unsigned int ENGINE_METHOD_RAND; -static const unsigned int ENGINE_METHOD_ECDH; -static const unsigned int ENGINE_METHOD_ECDSA; -static const unsigned int ENGINE_METHOD_CIPHERS; -static const unsigned int ENGINE_METHOD_DIGESTS; -static const unsigned int ENGINE_METHOD_STORE; -static const unsigned int ENGINE_METHOD_ALL; -static const unsigned int ENGINE_METHOD_NONE; +static const long Cryptography_HAS_ENGINE; """ FUNCTIONS = """ -ENGINE *ENGINE_get_first(void); -ENGINE *ENGINE_get_last(void); -ENGINE *ENGINE_get_next(ENGINE *); -ENGINE *ENGINE_get_prev(ENGINE *); -int ENGINE_add(ENGINE *); -int ENGINE_remove(ENGINE *); ENGINE *ENGINE_by_id(const char *); int ENGINE_init(ENGINE *); int ENGINE_finish(ENGINE *); -void ENGINE_load_openssl(void); -void ENGINE_load_dynamic(void); -void ENGINE_load_builtin_engines(void); -void ENGINE_cleanup(void); -ENGINE *ENGINE_get_default_RSA(void); -ENGINE *ENGINE_get_default_DSA(void); -ENGINE *ENGINE_get_default_ECDH(void); -ENGINE *ENGINE_get_default_ECDSA(void); -ENGINE *ENGINE_get_default_DH(void); ENGINE *ENGINE_get_default_RAND(void); -ENGINE *ENGINE_get_cipher_engine(int); -ENGINE *ENGINE_get_digest_engine(int); -int ENGINE_set_default_RSA(ENGINE *); -int ENGINE_set_default_DSA(ENGINE *); -int ENGINE_set_default_ECDH(ENGINE *); -int ENGINE_set_default_ECDSA(ENGINE *); -int ENGINE_set_default_DH(ENGINE *); int ENGINE_set_default_RAND(ENGINE *); -int ENGINE_set_default_ciphers(ENGINE *); -int ENGINE_set_default_digests(ENGINE *); -int ENGINE_set_default_string(ENGINE *, const char *); -int ENGINE_set_default(ENGINE *, unsigned int); -unsigned int ENGINE_get_table_flags(void); -void ENGINE_set_table_flags(unsigned int); -int ENGINE_register_RSA(ENGINE *); -void ENGINE_unregister_RSA(ENGINE *); -void ENGINE_register_all_RSA(void); -int ENGINE_register_DSA(ENGINE *); -void ENGINE_unregister_DSA(ENGINE *); -void ENGINE_register_all_DSA(void); -int ENGINE_register_ECDH(ENGINE *); -void ENGINE_unregister_ECDH(ENGINE *); -void ENGINE_register_all_ECDH(void); -int ENGINE_register_ECDSA(ENGINE *); -void ENGINE_unregister_ECDSA(ENGINE *); -void ENGINE_register_all_ECDSA(void); -int ENGINE_register_DH(ENGINE *); -void ENGINE_unregister_DH(ENGINE *); -void ENGINE_register_all_DH(void); -int ENGINE_register_RAND(ENGINE *); void ENGINE_unregister_RAND(ENGINE *); -void ENGINE_register_all_RAND(void); -int ENGINE_register_STORE(ENGINE *); -void ENGINE_unregister_STORE(ENGINE *); -void ENGINE_register_all_STORE(void); -int ENGINE_register_ciphers(ENGINE *); -void ENGINE_unregister_ciphers(ENGINE *); -void ENGINE_register_all_ciphers(void); -int ENGINE_register_digests(ENGINE *); -void ENGINE_unregister_digests(ENGINE *); -void ENGINE_register_all_digests(void); -int ENGINE_register_complete(ENGINE *); -int ENGINE_register_all_complete(void); -int ENGINE_ctrl(ENGINE *, int, long, void *, void (*)(void)); -int ENGINE_cmd_is_executable(ENGINE *, int); int ENGINE_ctrl_cmd(ENGINE *, const char *, long, void *, void (*)(void), int); -int ENGINE_ctrl_cmd_string(ENGINE *, const char *, const char *, int); - -ENGINE *ENGINE_new(void); int ENGINE_free(ENGINE *); -int ENGINE_up_ref(ENGINE *); -int ENGINE_set_id(ENGINE *, const char *); -int ENGINE_set_name(ENGINE *, const char *); -int ENGINE_set_RSA(ENGINE *, const RSA_METHOD *); -int ENGINE_set_DSA(ENGINE *, const DSA_METHOD *); -int ENGINE_set_ECDH(ENGINE *, const ECDH_METHOD *); -int ENGINE_set_ECDSA(ENGINE *, const ECDSA_METHOD *); -int ENGINE_set_DH(ENGINE *, const DH_METHOD *); -int ENGINE_set_RAND(ENGINE *, const RAND_METHOD *); -int ENGINE_set_STORE(ENGINE *, const STORE_METHOD *); -int ENGINE_set_destroy_function(ENGINE *, ENGINE_GEN_INT_FUNC_PTR); -int ENGINE_set_init_function(ENGINE *, ENGINE_GEN_INT_FUNC_PTR); -int ENGINE_set_finish_function(ENGINE *, ENGINE_GEN_INT_FUNC_PTR); -int ENGINE_set_ctrl_function(ENGINE *, ENGINE_CTRL_FUNC_PTR); -int ENGINE_set_load_privkey_function(ENGINE *, ENGINE_LOAD_KEY_PTR); -int ENGINE_set_load_pubkey_function(ENGINE *, ENGINE_LOAD_KEY_PTR); -int ENGINE_set_ciphers(ENGINE *, ENGINE_CIPHERS_PTR); -int ENGINE_set_digests(ENGINE *, ENGINE_DIGESTS_PTR); -int ENGINE_set_flags(ENGINE *, int); -int ENGINE_set_cmd_defns(ENGINE *, const ENGINE_CMD_DEFN *); -const char *ENGINE_get_id(const ENGINE *); const char *ENGINE_get_name(const ENGINE *); -const RSA_METHOD *ENGINE_get_RSA(const ENGINE *); -const DSA_METHOD *ENGINE_get_DSA(const ENGINE *); -const ECDH_METHOD *ENGINE_get_ECDH(const ENGINE *); -const ECDSA_METHOD *ENGINE_get_ECDSA(const ENGINE *); -const DH_METHOD *ENGINE_get_DH(const ENGINE *); -const RAND_METHOD *ENGINE_get_RAND(const ENGINE *); -const STORE_METHOD *ENGINE_get_STORE(const ENGINE *); -const EVP_CIPHER *ENGINE_get_cipher(ENGINE *, int); -const EVP_MD *ENGINE_get_digest(ENGINE *, int); -int ENGINE_get_flags(const ENGINE *); -const ENGINE_CMD_DEFN *ENGINE_get_cmd_defns(const ENGINE *); -EVP_PKEY *ENGINE_load_private_key(ENGINE *, const char *, UI_METHOD *, void *); -EVP_PKEY *ENGINE_load_public_key(ENGINE *, const char *, UI_METHOD *, void *); -void ENGINE_add_conf_module(void); -""" - -MACROS = """ -void ENGINE_load_cryptodev(void); """ CUSTOMIZATIONS = """ -#if defined(LIBRESSL_VERSION_NUMBER) -static const long Cryptography_HAS_ENGINE_CRYPTODEV = 0; -void (*ENGINE_load_cryptodev)(void) = NULL; +#ifdef OPENSSL_NO_ENGINE +static const long Cryptography_HAS_ENGINE = 0; + +ENGINE *(*ENGINE_by_id)(const char *) = NULL; +int (*ENGINE_init)(ENGINE *) = NULL; +int (*ENGINE_finish)(ENGINE *) = NULL; +ENGINE *(*ENGINE_get_default_RAND)(void) = NULL; +int (*ENGINE_set_default_RAND)(ENGINE *) = NULL; +void (*ENGINE_unregister_RAND)(ENGINE *) = NULL; +int (*ENGINE_ctrl_cmd)(ENGINE *, const char *, long, void *, + void (*)(void), int) = NULL; + +int (*ENGINE_free)(ENGINE *) = NULL; +const char *(*ENGINE_get_id)(const ENGINE *) = NULL; +const char *(*ENGINE_get_name)(const ENGINE *) = NULL; + #else -static const long Cryptography_HAS_ENGINE_CRYPTODEV = 1; +static const long Cryptography_HAS_ENGINE = 1; #endif """ - -CONDITIONAL_NAMES = { - "Cryptography_HAS_ENGINE_CRYPTODEV": [ - "ENGINE_load_cryptodev" - ] -} diff --git a/src/_cffi_src/openssl/err.py b/src/_cffi_src/openssl/err.py index 0ee19c9e..ecdc6e3d 100644 --- a/src/_cffi_src/openssl/err.py +++ b/src/_cffi_src/openssl/err.py @@ -9,17 +9,9 @@ INCLUDES = """ """ TYPES = """ -static const int Cryptography_HAS_REMOVE_THREAD_STATE; -static const int Cryptography_HAS_098H_ERROR_CODES; -static const int Cryptography_HAS_098C_CAMELLIA_CODES; static const int Cryptography_HAS_EC_CODES; static const int Cryptography_HAS_RSA_R_PKCS_DECODING_ERROR; - -struct ERR_string_data_st { - unsigned long error; - const char *string; -}; -typedef struct ERR_string_data_st ERR_STRING_DATA; +static const int Cryptography_HAS_EVP_R_MEMORY_LIMIT_EXCEEDED; static const int ERR_LIB_DH; static const int ERR_LIB_EVP; @@ -28,55 +20,17 @@ static const int ERR_LIB_PEM; static const int ERR_LIB_ASN1; static const int ERR_LIB_RSA; static const int ERR_LIB_PKCS12; +static const int ERR_LIB_SSL; +static const int ERR_LIB_X509; -static const int ASN1_F_ASN1_ENUMERATED_TO_BN; -static const int ASN1_F_ASN1_EX_C2I; -static const int ASN1_F_ASN1_FIND_END; -static const int ASN1_F_ASN1_GENERALIZEDTIME_SET; -static const int ASN1_F_ASN1_GENERATE_V3; -static const int ASN1_F_ASN1_GET_OBJECT; -static const int ASN1_F_ASN1_ITEM_I2D_FP; -static const int ASN1_F_ASN1_ITEM_PACK; -static const int ASN1_F_ASN1_ITEM_SIGN; -static const int ASN1_F_ASN1_ITEM_UNPACK; -static const int ASN1_F_ASN1_ITEM_VERIFY; -static const int ASN1_F_ASN1_MBSTRING_NCOPY; -static const int ASN1_F_ASN1_TEMPLATE_EX_D2I; -static const int ASN1_F_ASN1_TEMPLATE_NEW; -static const int ASN1_F_ASN1_TEMPLATE_NOEXP_D2I; -static const int ASN1_F_ASN1_TIME_SET; -static const int ASN1_F_ASN1_TYPE_GET_INT_OCTETSTRING; -static const int ASN1_F_ASN1_TYPE_GET_OCTETSTRING; -static const int ASN1_F_ASN1_UNPACK_STRING; -static const int ASN1_F_ASN1_UTCTIME_SET; -static const int ASN1_F_ASN1_VERIFY; -static const int ASN1_F_BITSTR_CB; -static const int ASN1_F_BN_TO_ASN1_ENUMERATED; -static const int ASN1_F_BN_TO_ASN1_INTEGER; -static const int ASN1_F_D2I_ASN1_TYPE_BYTES; -static const int ASN1_F_D2I_ASN1_UINTEGER; -static const int ASN1_F_D2I_ASN1_UTCTIME; -static const int ASN1_F_D2I_NETSCAPE_RSA; -static const int ASN1_F_D2I_NETSCAPE_RSA_2; -static const int ASN1_F_D2I_PRIVATEKEY; -static const int ASN1_F_D2I_X509; -static const int ASN1_F_D2I_X509_CINF; -static const int ASN1_F_D2I_X509_PKEY; -static const int ASN1_F_I2D_ASN1_SET; -static const int ASN1_F_I2D_ASN1_TIME; -static const int ASN1_F_I2D_DSA_PUBKEY; -static const int ASN1_F_LONG_C2I; -static const int ASN1_F_OID_MODULE_INIT; -static const int ASN1_F_PARSE_TAGGING; -static const int ASN1_F_PKCS5_PBE_SET; -static const int ASN1_F_X509_CINF_NEW; +static const int ERR_R_MALLOC_FAILURE; +static const int EVP_R_MEMORY_LIMIT_EXCEEDED; static const int ASN1_R_BOOLEAN_IS_WRONG_LENGTH; static const int ASN1_R_BUFFER_TOO_SMALL; static const int ASN1_R_CIPHER_HAS_NO_OBJECT_IDENTIFIER; static const int ASN1_R_DATA_IS_WRONG; static const int ASN1_R_DECODE_ERROR; -static const int ASN1_R_DECODING_ERROR; static const int ASN1_R_DEPTH_EXCEEDED; static const int ASN1_R_ENCODE_ERROR; static const int ASN1_R_ERROR_GETTING_TIME; @@ -88,122 +42,46 @@ static const int ASN1_R_UNKNOWN_MESSAGE_DIGEST_ALGORITHM; static const int ASN1_R_UNKNOWN_OBJECT_TYPE; static const int ASN1_R_UNKNOWN_PUBLIC_KEY_TYPE; static const int ASN1_R_UNKNOWN_TAG; -static const int ASN1_R_UNKOWN_FORMAT; static const int ASN1_R_UNSUPPORTED_ANY_DEFINED_BY_TYPE; -static const int ASN1_R_UNSUPPORTED_ENCRYPTION_ALGORITHM; static const int ASN1_R_UNSUPPORTED_PUBLIC_KEY_TYPE; static const int ASN1_R_UNSUPPORTED_TYPE; static const int ASN1_R_WRONG_TAG; -static const int ASN1_R_WRONG_TYPE; - -static const int DH_F_COMPUTE_KEY; +static const int ASN1_R_NO_CONTENT_TYPE; +static const int ASN1_R_NO_MULTIPART_BODY_FAILURE; +static const int ASN1_R_NO_MULTIPART_BOUNDARY; +static const int ASN1_R_HEADER_TOO_LONG; static const int DH_R_INVALID_PUBKEY; -static const int EVP_F_AES_INIT_KEY; -static const int EVP_F_D2I_PKEY; -static const int EVP_F_DSA_PKEY2PKCS8; -static const int EVP_F_DSAPKEY2PKCS8; -static const int EVP_F_ECDSA_PKEY2PKCS8; -static const int EVP_F_ECKEY_PKEY2PKCS8; -static const int EVP_F_EVP_CIPHER_CTX_CTRL; -static const int EVP_F_EVP_CIPHER_CTX_SET_KEY_LENGTH; -static const int EVP_F_EVP_CIPHERINIT_EX; -static const int EVP_F_EVP_DECRYPTFINAL_EX; -static const int EVP_F_EVP_DIGESTINIT_EX; static const int EVP_F_EVP_ENCRYPTFINAL_EX; -static const int EVP_F_EVP_MD_CTX_COPY_EX; -static const int EVP_F_EVP_OPENINIT; -static const int EVP_F_EVP_PBE_ALG_ADD; -static const int EVP_F_EVP_PBE_CIPHERINIT; -static const int EVP_F_EVP_PKCS82PKEY; -static const int EVP_F_EVP_PKEY2PKCS8_BROKEN; -static const int EVP_F_EVP_PKEY_COPY_PARAMETERS; -static const int EVP_F_EVP_PKEY_DECRYPT; -static const int EVP_F_EVP_PKEY_ENCRYPT; -static const int EVP_F_EVP_PKEY_GET1_DH; -static const int EVP_F_EVP_PKEY_GET1_DSA; -static const int EVP_F_EVP_PKEY_GET1_ECDSA; -static const int EVP_F_EVP_PKEY_GET1_EC_KEY; -static const int EVP_F_EVP_PKEY_GET1_RSA; -static const int EVP_F_EVP_PKEY_NEW; -static const int EVP_F_EVP_RIJNDAEL; -static const int EVP_F_EVP_SIGNFINAL; -static const int EVP_F_EVP_VERIFYFINAL; -static const int EVP_F_PKCS5_PBE_KEYIVGEN; -static const int EVP_F_PKCS5_V2_PBE_KEYIVGEN; -static const int EVP_F_PKCS8_SET_BROKEN; -static const int EVP_F_RC2_MAGIC_TO_METH; -static const int EVP_F_RC5_CTRL; static const int EVP_R_AES_KEY_SETUP_FAILED; -static const int EVP_R_ASN1_LIB; -static const int EVP_R_BAD_BLOCK_LENGTH; static const int EVP_R_BAD_DECRYPT; -static const int EVP_R_BAD_KEY_LENGTH; -static const int EVP_R_BN_DECODE_ERROR; -static const int EVP_R_BN_PUBKEY_ERROR; static const int EVP_R_CIPHER_PARAMETER_ERROR; static const int EVP_R_CTRL_NOT_IMPLEMENTED; static const int EVP_R_CTRL_OPERATION_NOT_IMPLEMENTED; static const int EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH; static const int EVP_R_DECODE_ERROR; static const int EVP_R_DIFFERENT_KEY_TYPES; -static const int EVP_R_ENCODE_ERROR; static const int EVP_R_INITIALIZATION_ERROR; static const int EVP_R_INPUT_NOT_INITIALIZED; static const int EVP_R_INVALID_KEY_LENGTH; -static const int EVP_R_IV_TOO_LARGE; static const int EVP_R_KEYGEN_FAILURE; static const int EVP_R_MISSING_PARAMETERS; static const int EVP_R_NO_CIPHER_SET; static const int EVP_R_NO_DIGEST_SET; -static const int EVP_R_NO_DSA_PARAMETERS; -static const int EVP_R_NO_SIGN_FUNCTION_CONFIGURED; -static const int EVP_R_NO_VERIFY_FUNCTION_CONFIGURED; -static const int EVP_R_PKCS8_UNKNOWN_BROKEN_TYPE; static const int EVP_R_PUBLIC_KEY_NOT_RSA; static const int EVP_R_UNKNOWN_PBE_ALGORITHM; -static const int EVP_R_UNSUPORTED_NUMBER_OF_ROUNDS; static const int EVP_R_UNSUPPORTED_CIPHER; static const int EVP_R_UNSUPPORTED_KEY_DERIVATION_FUNCTION; static const int EVP_R_UNSUPPORTED_KEYLENGTH; static const int EVP_R_UNSUPPORTED_SALT_TYPE; static const int EVP_R_UNSUPPORTED_PRIVATE_KEY_ALGORITHM; static const int EVP_R_WRONG_FINAL_BLOCK_LENGTH; -static const int EVP_R_WRONG_PUBLIC_KEY_TYPE; - -static const int EC_F_EC_GROUP_NEW_BY_CURVE_NAME; +static const int EVP_R_CAMELLIA_KEY_SETUP_FAILED; static const int EC_R_UNKNOWN_GROUP; -static const int PEM_F_D2I_PKCS8PRIVATEKEY_BIO; -static const int PEM_F_D2I_PKCS8PRIVATEKEY_FP; -static const int PEM_F_DO_PK8PKEY; -static const int PEM_F_DO_PK8PKEY_FP; -static const int PEM_F_LOAD_IV; -static const int PEM_F_PEM_ASN1_READ; -static const int PEM_F_PEM_ASN1_READ_BIO; -static const int PEM_F_PEM_ASN1_WRITE; -static const int PEM_F_PEM_ASN1_WRITE_BIO; -static const int PEM_F_PEM_DEF_CALLBACK; -static const int PEM_F_PEM_DO_HEADER; -static const int PEM_F_PEM_F_PEM_WRITE_PKCS8PRIVATEKEY; -static const int PEM_F_PEM_GET_EVP_CIPHER_INFO; -static const int PEM_F_PEM_PK8PKEY; -static const int PEM_F_PEM_READ; -static const int PEM_F_PEM_READ_BIO; -static const int PEM_F_PEM_READ_BIO_PRIVATEKEY; -static const int PEM_F_PEM_READ_PRIVATEKEY; -static const int PEM_F_PEM_SEALFINAL; -static const int PEM_F_PEM_SEALINIT; -static const int PEM_F_PEM_SIGNFINAL; -static const int PEM_F_PEM_WRITE; -static const int PEM_F_PEM_WRITE_BIO; -static const int PEM_F_PEM_X509_INFO_READ; -static const int PEM_F_PEM_X509_INFO_READ_BIO; -static const int PEM_F_PEM_X509_INFO_WRITE_BIO; - static const int PEM_R_BAD_BASE64_DECODE; static const int PEM_R_BAD_DECRYPT; static const int PEM_R_BAD_END_LINE; @@ -215,116 +93,79 @@ static const int PEM_R_NOT_DEK_INFO; static const int PEM_R_NOT_ENCRYPTED; static const int PEM_R_NOT_PROC_TYPE; static const int PEM_R_PROBLEMS_GETTING_PASSWORD; -static const int PEM_R_PUBLIC_KEY_NO_RSA; static const int PEM_R_READ_KEY; static const int PEM_R_SHORT_HEADER; static const int PEM_R_UNSUPPORTED_CIPHER; static const int PEM_R_UNSUPPORTED_ENCRYPTION; -static const int PKCS12_F_PKCS12_PBE_CRYPT; - static const int PKCS12_R_PKCS12_CIPHERFINAL_ERROR; +static const int RSA_R_BAD_PAD_BYTE_COUNT; static const int RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE; +static const int RSA_R_DATA_TOO_LARGE_FOR_MODULUS; static const int RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY; static const int RSA_R_BLOCK_TYPE_IS_NOT_01; static const int RSA_R_BLOCK_TYPE_IS_NOT_02; static const int RSA_R_PKCS_DECODING_ERROR; +static const int RSA_R_OAEP_DECODING_ERROR; + +static const int SSL_TLSEXT_ERR_OK; +static const int SSL_TLSEXT_ERR_ALERT_WARNING; +static const int SSL_TLSEXT_ERR_ALERT_FATAL; +static const int SSL_TLSEXT_ERR_NOACK; + +static const int SSL_AD_CLOSE_NOTIFY; +static const int SSL_AD_UNEXPECTED_MESSAGE; +static const int SSL_AD_BAD_RECORD_MAC; +static const int SSL_AD_RECORD_OVERFLOW; +static const int SSL_AD_DECOMPRESSION_FAILURE; +static const int SSL_AD_HANDSHAKE_FAILURE; +static const int SSL_AD_BAD_CERTIFICATE; +static const int SSL_AD_UNSUPPORTED_CERTIFICATE; +static const int SSL_AD_CERTIFICATE_REVOKED; +static const int SSL_AD_CERTIFICATE_EXPIRED; +static const int SSL_AD_CERTIFICATE_UNKNOWN; +static const int SSL_AD_ILLEGAL_PARAMETER; +static const int SSL_AD_UNKNOWN_CA; +static const int SSL_AD_ACCESS_DENIED; +static const int SSL_AD_DECODE_ERROR; +static const int SSL_AD_DECRYPT_ERROR; +static const int SSL_AD_PROTOCOL_VERSION; +static const int SSL_AD_INSUFFICIENT_SECURITY; +static const int SSL_AD_INTERNAL_ERROR; +static const int SSL_AD_USER_CANCELLED; +static const int SSL_AD_NO_RENEGOTIATION; + +static const int SSL_AD_UNSUPPORTED_EXTENSION; +static const int SSL_AD_CERTIFICATE_UNOBTAINABLE; +static const int SSL_AD_UNRECOGNIZED_NAME; +static const int SSL_AD_BAD_CERTIFICATE_STATUS_RESPONSE; +static const int SSL_AD_BAD_CERTIFICATE_HASH_VALUE; +static const int SSL_AD_UNKNOWN_PSK_IDENTITY; + +static const int X509_R_CERT_ALREADY_IN_HASH_TABLE; +static const int X509_R_KEY_VALUES_MISMATCH; """ FUNCTIONS = """ -void ERR_load_crypto_strings(void); -void ERR_load_SSL_strings(void); -void ERR_free_strings(void); -char *ERR_error_string(unsigned long, char *); void ERR_error_string_n(unsigned long, char *, size_t); const char *ERR_lib_error_string(unsigned long); const char *ERR_func_error_string(unsigned long); const char *ERR_reason_error_string(unsigned long); -void ERR_print_errors(BIO *); -void ERR_print_errors_fp(FILE *); unsigned long ERR_get_error(void); unsigned long ERR_peek_error(void); unsigned long ERR_peek_last_error(void); -unsigned long ERR_get_error_line(const char **, int *); -unsigned long ERR_peek_error_line(const char **, int *); -unsigned long ERR_peek_last_error_line(const char **, int *); -unsigned long ERR_get_error_line_data(const char **, int *, - const char **, int *); -unsigned long ERR_peek_error_line_data(const char **, - int *, const char **, int *); -unsigned long ERR_peek_last_error_line_data(const char **, - int *, const char **, int *); +void ERR_clear_error(void); void ERR_put_error(int, int, int, const char *, int); -void ERR_add_error_data(int, ...); -int ERR_get_next_error_library(void); -""" -MACROS = """ -unsigned long ERR_PACK(int, int, int); int ERR_GET_LIB(unsigned long); int ERR_GET_FUNC(unsigned long); int ERR_GET_REASON(unsigned long); -int ERR_FATAL_ERROR(unsigned long); -/* introduced in 1.0.0 so we have to handle this specially to continue - * supporting 0.9.8 - */ -void ERR_remove_thread_state(const CRYPTO_THREADID *); -/* These were added in OpenSSL 0.9.8h. When we drop support for RHEL/CentOS 5 - we should be able to move these back to TYPES. */ -static const int ASN1_F_B64_READ_ASN1; -static const int ASN1_F_B64_WRITE_ASN1; -static const int ASN1_F_SMIME_READ_ASN1; -static const int ASN1_F_SMIME_TEXT; -static const int ASN1_R_NO_CONTENT_TYPE; -static const int ASN1_R_NO_MULTIPART_BODY_FAILURE; -static const int ASN1_R_NO_MULTIPART_BOUNDARY; -/* These were added in OpenSSL 0.9.8c. */ -static const int EVP_F_CAMELLIA_INIT_KEY; -static const int EVP_R_CAMELLIA_KEY_SETUP_FAILED; """ CUSTOMIZATIONS = """ -#if OPENSSL_VERSION_NUMBER >= 0x10000000L -static const long Cryptography_HAS_REMOVE_THREAD_STATE = 1; -#else -static const long Cryptography_HAS_REMOVE_THREAD_STATE = 0; -typedef uint32_t CRYPTO_THREADID; -void (*ERR_remove_thread_state)(const CRYPTO_THREADID *) = NULL; -#endif - -/* OpenSSL 0.9.8h+ */ -#if OPENSSL_VERSION_NUMBER >= 0x0090808fL -static const long Cryptography_HAS_098H_ERROR_CODES = 1; -#else -static const long Cryptography_HAS_098H_ERROR_CODES = 0; -static const int ASN1_F_B64_READ_ASN1 = 0; -static const int ASN1_F_B64_WRITE_ASN1 = 0; -static const int ASN1_F_SMIME_READ_ASN1 = 0; -static const int ASN1_F_SMIME_TEXT = 0; -static const int ASN1_R_NO_CONTENT_TYPE = 0; -static const int ASN1_R_NO_MULTIPART_BODY_FAILURE = 0; -static const int ASN1_R_NO_MULTIPART_BOUNDARY = 0; -#endif - -/* OpenSSL 0.9.8c+ */ -#ifdef EVP_F_CAMELLIA_INIT_KEY -static const long Cryptography_HAS_098C_CAMELLIA_CODES = 1; -#else -static const long Cryptography_HAS_098C_CAMELLIA_CODES = 0; -static const int EVP_F_CAMELLIA_INIT_KEY = 0; -static const int EVP_R_CAMELLIA_KEY_SETUP_FAILED = 0; -#endif - -// OpenSSL without EC. e.g. RHEL -#ifndef OPENSSL_NO_EC static const long Cryptography_HAS_EC_CODES = 1; -#else -static const long Cryptography_HAS_EC_CODES = 0; -static const int EC_R_UNKNOWN_GROUP = 0; -static const int EC_F_EC_GROUP_NEW_BY_CURVE_NAME = 0; -#endif #ifdef RSA_R_PKCS_DECODING_ERROR static const long Cryptography_HAS_RSA_R_PKCS_DECODING_ERROR = 1; @@ -332,30 +173,11 @@ static const long Cryptography_HAS_RSA_R_PKCS_DECODING_ERROR = 1; static const long Cryptography_HAS_RSA_R_PKCS_DECODING_ERROR = 0; static const long RSA_R_PKCS_DECODING_ERROR = 0; #endif -""" -CONDITIONAL_NAMES = { - "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": [ - "RSA_R_PKCS_DECODING_ERROR" - ] -} +#ifdef EVP_R_MEMORY_LIMIT_EXCEEDED +static const long Cryptography_HAS_EVP_R_MEMORY_LIMIT_EXCEEDED = 1; +#else +static const long EVP_R_MEMORY_LIMIT_EXCEEDED = 0; +static const long Cryptography_HAS_EVP_R_MEMORY_LIMIT_EXCEEDED = 0; +#endif +""" diff --git a/src/_cffi_src/openssl/evp.py b/src/_cffi_src/openssl/evp.py index 93aa83de..a0767021 100644 --- a/src/_cffi_src/openssl/evp.py +++ b/src/_cffi_src/openssl/evp.py @@ -10,78 +10,69 @@ INCLUDES = """ TYPES = """ typedef ... EVP_CIPHER; -typedef struct { - const EVP_CIPHER *cipher; - ENGINE *engine; - int encrypt; - ...; -} EVP_CIPHER_CTX; +typedef ... EVP_CIPHER_CTX; typedef ... EVP_MD; -typedef struct env_md_ctx_st { - ...; -} EVP_MD_CTX; - -typedef struct evp_pkey_st { - int type; - ...; -} EVP_PKEY; +typedef ... EVP_MD_CTX; + +typedef ... EVP_PKEY; typedef ... EVP_PKEY_CTX; static const int EVP_PKEY_RSA; static const int EVP_PKEY_DSA; static const int EVP_PKEY_DH; +static const int EVP_PKEY_DHX; static const int EVP_PKEY_EC; +static const int EVP_PKEY_X25519; +static const int EVP_PKEY_ED25519; +static const int EVP_PKEY_X448; +static const int EVP_PKEY_ED448; +static const int EVP_PKEY_POLY1305; static const int EVP_MAX_MD_SIZE; -static const int EVP_CTRL_GCM_SET_IVLEN; -static const int EVP_CTRL_GCM_GET_TAG; -static const int EVP_CTRL_GCM_SET_TAG; +static const int EVP_CTRL_AEAD_SET_IVLEN; +static const int EVP_CTRL_AEAD_GET_TAG; +static const int EVP_CTRL_AEAD_SET_TAG; -static const int Cryptography_HAS_GCM; -static const int Cryptography_HAS_PBKDF2_HMAC; -static const int Cryptography_HAS_PKEY_CTX; +static const int Cryptography_HAS_SCRYPT; +static const int Cryptography_HAS_EVP_PKEY_DHX; +static const int Cryptography_HAS_EVP_PKEY_get_set_tls_encodedpoint; +static const int Cryptography_HAS_ONESHOT_EVP_DIGEST_SIGN_VERIFY; +static const long Cryptography_HAS_RAW_KEY; +static const long Cryptography_HAS_EVP_DIGESTFINAL_XOF; """ FUNCTIONS = """ const EVP_CIPHER *EVP_get_cipherbyname(const char *); -int EVP_EncryptInit_ex(EVP_CIPHER_CTX *, const EVP_CIPHER *, ENGINE *, - const unsigned char *, const unsigned char *); int EVP_CIPHER_CTX_set_padding(EVP_CIPHER_CTX *, int); -int EVP_EncryptUpdate(EVP_CIPHER_CTX *, unsigned char *, int *, - const unsigned char *, int); -int EVP_EncryptFinal_ex(EVP_CIPHER_CTX *, unsigned char *, int *); -int EVP_DecryptInit_ex(EVP_CIPHER_CTX *, const EVP_CIPHER *, ENGINE *, - const unsigned char *, const unsigned char *); -int EVP_DecryptUpdate(EVP_CIPHER_CTX *, unsigned char *, int *, - const unsigned char *, int); -int EVP_DecryptFinal_ex(EVP_CIPHER_CTX *, unsigned char *, int *); int EVP_CipherInit_ex(EVP_CIPHER_CTX *, const EVP_CIPHER *, ENGINE *, const unsigned char *, const unsigned char *, int); int EVP_CipherUpdate(EVP_CIPHER_CTX *, unsigned char *, int *, const unsigned char *, int); int EVP_CipherFinal_ex(EVP_CIPHER_CTX *, unsigned char *, int *); int EVP_CIPHER_CTX_cleanup(EVP_CIPHER_CTX *); -void EVP_CIPHER_CTX_init(EVP_CIPHER_CTX *); EVP_CIPHER_CTX *EVP_CIPHER_CTX_new(void); void EVP_CIPHER_CTX_free(EVP_CIPHER_CTX *); int EVP_CIPHER_CTX_set_key_length(EVP_CIPHER_CTX *, int); +const EVP_CIPHER *EVP_CIPHER_CTX_cipher(const EVP_CIPHER_CTX *); -EVP_MD_CTX *EVP_MD_CTX_create(void); int EVP_MD_CTX_copy_ex(EVP_MD_CTX *, const EVP_MD_CTX *); int EVP_DigestInit_ex(EVP_MD_CTX *, const EVP_MD *, ENGINE *); int EVP_DigestUpdate(EVP_MD_CTX *, const void *, size_t); int EVP_DigestFinal_ex(EVP_MD_CTX *, unsigned char *, unsigned int *); -int EVP_MD_CTX_cleanup(EVP_MD_CTX *); -void EVP_MD_CTX_destroy(EVP_MD_CTX *); +int EVP_DigestFinalXOF(EVP_MD_CTX *, unsigned char *, size_t); const EVP_MD *EVP_get_digestbyname(const char *); EVP_PKEY *EVP_PKEY_new(void); void EVP_PKEY_free(EVP_PKEY *); int EVP_PKEY_type(int); -int EVP_PKEY_bits(EVP_PKEY *); int EVP_PKEY_size(EVP_PKEY *); RSA *EVP_PKEY_get1_RSA(EVP_PKEY *); DSA *EVP_PKEY_get1_DSA(EVP_PKEY *); DH *EVP_PKEY_get1_DH(EVP_PKEY *); +int EVP_PKEY_encrypt(EVP_PKEY_CTX *, unsigned char *, size_t *, + const unsigned char *, size_t); +int EVP_PKEY_decrypt(EVP_PKEY_CTX *, unsigned char *, size_t *, + const unsigned char *, size_t); + int EVP_SignInit(EVP_MD_CTX *, const EVP_MD *); int EVP_SignUpdate(EVP_MD_CTX *, const void *, size_t); int EVP_SignFinal(EVP_MD_CTX *, unsigned char *, unsigned int *, EVP_PKEY *); @@ -91,49 +82,70 @@ int EVP_VerifyUpdate(EVP_MD_CTX *, const void *, size_t); int EVP_VerifyFinal(EVP_MD_CTX *, const unsigned char *, unsigned int, EVP_PKEY *); -const EVP_MD *EVP_md5(void); -const EVP_MD *EVP_sha1(void); -const EVP_MD *EVP_ripemd160(void); -const EVP_MD *EVP_sha224(void); -const EVP_MD *EVP_sha256(void); -const EVP_MD *EVP_sha384(void); -const EVP_MD *EVP_sha512(void); +int EVP_DigestSignInit(EVP_MD_CTX *, EVP_PKEY_CTX **, const EVP_MD *, + ENGINE *, EVP_PKEY *); +int EVP_DigestSignUpdate(EVP_MD_CTX *, const void *, size_t); +int EVP_DigestSignFinal(EVP_MD_CTX *, unsigned char *, size_t *); +int EVP_DigestVerifyInit(EVP_MD_CTX *, EVP_PKEY_CTX **, const EVP_MD *, + ENGINE *, EVP_PKEY *); + int PKCS5_PBKDF2_HMAC_SHA1(const char *, int, const unsigned char *, int, int, int, unsigned char *); -int EVP_PKEY_set1_RSA(EVP_PKEY *, struct rsa_st *); -int EVP_PKEY_set1_DSA(EVP_PKEY *, struct dsa_st *); -int EVP_PKEY_set1_DH(EVP_PKEY *, DH *); +EVP_PKEY_CTX *EVP_PKEY_CTX_new(EVP_PKEY *, ENGINE *); +EVP_PKEY_CTX *EVP_PKEY_CTX_new_id(int, ENGINE *); +EVP_PKEY_CTX *EVP_PKEY_CTX_dup(EVP_PKEY_CTX *); +void EVP_PKEY_CTX_free(EVP_PKEY_CTX *); +int EVP_PKEY_sign_init(EVP_PKEY_CTX *); +int EVP_PKEY_sign(EVP_PKEY_CTX *, unsigned char *, size_t *, + const unsigned char *, size_t); +int EVP_PKEY_verify_init(EVP_PKEY_CTX *); +int EVP_PKEY_verify(EVP_PKEY_CTX *, const unsigned char *, size_t, + const unsigned char *, size_t); +int EVP_PKEY_encrypt_init(EVP_PKEY_CTX *); +int EVP_PKEY_decrypt_init(EVP_PKEY_CTX *); -int EVP_PKEY_get_attr_count(const EVP_PKEY *); -int EVP_PKEY_get_attr_by_NID(const EVP_PKEY *, int, int); -int EVP_PKEY_get_attr_by_OBJ(const EVP_PKEY *, ASN1_OBJECT *, int); -X509_ATTRIBUTE *EVP_PKEY_get_attr(const EVP_PKEY *, int); -X509_ATTRIBUTE *EVP_PKEY_delete_attr(EVP_PKEY *, int); -int EVP_PKEY_add1_attr(EVP_PKEY *, X509_ATTRIBUTE *); -int EVP_PKEY_add1_attr_by_OBJ(EVP_PKEY *, const ASN1_OBJECT *, int, - const unsigned char *, int); -int EVP_PKEY_add1_attr_by_NID(EVP_PKEY *, int, int, - const unsigned char *, int); -int EVP_PKEY_add1_attr_by_txt(EVP_PKEY *, const char *, int, - const unsigned char *, int); +int EVP_PKEY_set1_RSA(EVP_PKEY *, RSA *); +int EVP_PKEY_set1_DSA(EVP_PKEY *, DSA *); +int EVP_PKEY_set1_DH(EVP_PKEY *, DH *); int EVP_PKEY_cmp(const EVP_PKEY *, const EVP_PKEY *); -EVP_PKEY *EVP_PKCS82PKEY(PKCS8_PRIV_KEY_INFO *); -""" +int EVP_PKEY_keygen_init(EVP_PKEY_CTX *); +int EVP_PKEY_keygen(EVP_PKEY_CTX *, EVP_PKEY **); +int EVP_PKEY_derive_init(EVP_PKEY_CTX *); +int EVP_PKEY_derive_set_peer(EVP_PKEY_CTX *, EVP_PKEY *); +int EVP_PKEY_derive(EVP_PKEY_CTX *, unsigned char *, size_t *); +int EVP_PKEY_set_type(EVP_PKEY *, int); + +int EVP_PKEY_id(const EVP_PKEY *); +int Cryptography_EVP_PKEY_id(const EVP_PKEY *); + +/* in 1.1.0 _create and _destroy were renamed to _new and _free. The following + two functions wrap both the old and new functions so we can call them + without worrying about what OpenSSL we're running against. */ +EVP_MD_CTX *Cryptography_EVP_MD_CTX_new(void); +void Cryptography_EVP_MD_CTX_free(EVP_MD_CTX *); +/* Added in 1.1.1 */ +int EVP_DigestSign(EVP_MD_CTX *, unsigned char *, size_t *, + const unsigned char *, size_t); +int EVP_DigestVerify(EVP_MD_CTX *, const unsigned char *, size_t, + const unsigned char *, size_t); +/* Added in 1.1.0 */ +size_t EVP_PKEY_get1_tls_encodedpoint(EVP_PKEY *, unsigned char **); +int EVP_PKEY_set1_tls_encodedpoint(EVP_PKEY *, const unsigned char *, + size_t); + +/* EVP_PKEY * became const in 1.1.0 */ +int EVP_PKEY_bits(EVP_PKEY *); -MACROS = """ void OpenSSL_add_all_algorithms(void); int EVP_PKEY_assign_RSA(EVP_PKEY *, RSA *); -int EVP_PKEY_assign_DSA(EVP_PKEY *, DSA *); -int EVP_PKEY_assign_EC_KEY(EVP_PKEY *, EC_KEY *); EC_KEY *EVP_PKEY_get1_EC_KEY(EVP_PKEY *); int EVP_PKEY_set1_EC_KEY(EVP_PKEY *, EC_KEY *); -int EVP_CIPHER_CTX_block_size(const EVP_CIPHER_CTX *); int EVP_CIPHER_CTX_ctrl(EVP_CIPHER_CTX *, int, int, void *); int PKCS5_PBKDF2_HMAC(const char *, int, const unsigned char *, int, int, @@ -141,125 +153,126 @@ int PKCS5_PBKDF2_HMAC(const char *, int, const unsigned char *, int, int, int EVP_PKEY_CTX_set_signature_md(EVP_PKEY_CTX *, const EVP_MD *); -/* These aren't macros, but must be in this section because they're not - available in 0.9.8. */ -EVP_PKEY_CTX *EVP_PKEY_CTX_new(EVP_PKEY *, ENGINE *); -EVP_PKEY_CTX *EVP_PKEY_CTX_new_id(int, ENGINE *); -EVP_PKEY_CTX *EVP_PKEY_CTX_dup(EVP_PKEY_CTX *); -void EVP_PKEY_CTX_free(EVP_PKEY_CTX *); -int EVP_PKEY_sign_init(EVP_PKEY_CTX *); -int EVP_PKEY_sign(EVP_PKEY_CTX *, unsigned char *, size_t *, - const unsigned char *, size_t); -int EVP_PKEY_verify_init(EVP_PKEY_CTX *); -int EVP_PKEY_verify(EVP_PKEY_CTX *, const unsigned char *, size_t, - const unsigned char *, size_t); -int EVP_PKEY_encrypt_init(EVP_PKEY_CTX *); -int EVP_PKEY_decrypt_init(EVP_PKEY_CTX *); -int EVP_PKEY_id(const EVP_PKEY *); +int EVP_PBE_scrypt(const char *, size_t, const unsigned char *, size_t, + uint64_t, uint64_t, uint64_t, uint64_t, unsigned char *, + size_t); -/* The following were macros in 0.9.8e. Once we drop support for RHEL/CentOS 5 - we should move these back to FUNCTIONS. */ -const EVP_CIPHER *EVP_CIPHER_CTX_cipher(const EVP_CIPHER_CTX *); -int EVP_CIPHER_block_size(const EVP_CIPHER *); -const EVP_MD *EVP_MD_CTX_md(const EVP_MD_CTX *); -int EVP_MD_size(const EVP_MD *); - -/* Must be in macros because EVP_PKEY_CTX is undefined in 0.9.8 */ -int Cryptography_EVP_PKEY_encrypt(EVP_PKEY_CTX *ctx, unsigned char *out, - size_t *outlen, const unsigned char *in, - size_t inlen); -int Cryptography_EVP_PKEY_decrypt(EVP_PKEY_CTX *ctx, unsigned char *out, - size_t *outlen, const unsigned char *in, - size_t inlen); +EVP_PKEY *EVP_PKEY_new_raw_private_key(int, ENGINE *, const unsigned char *, + size_t); +EVP_PKEY *EVP_PKEY_new_raw_public_key(int, ENGINE *, const unsigned char *, + size_t); +int EVP_PKEY_get_raw_private_key(const EVP_PKEY *, unsigned char *, size_t *); +int EVP_PKEY_get_raw_public_key(const EVP_PKEY *, unsigned char *, size_t *); """ CUSTOMIZATIONS = """ -#ifdef EVP_CTRL_GCM_SET_TAG -const long Cryptography_HAS_GCM = 1; +#ifdef EVP_PKEY_DHX +const long Cryptography_HAS_EVP_PKEY_DHX = 1; #else -const long Cryptography_HAS_GCM = 0; -const long EVP_CTRL_GCM_GET_TAG = -1; -const long EVP_CTRL_GCM_SET_TAG = -1; -const long EVP_CTRL_GCM_SET_IVLEN = -1; +const long Cryptography_HAS_EVP_PKEY_DHX = 0; +const long EVP_PKEY_DHX = -1; #endif -#if OPENSSL_VERSION_NUMBER >= 0x10000000L -const long Cryptography_HAS_PBKDF2_HMAC = 1; -const long Cryptography_HAS_PKEY_CTX = 1; - -/* OpenSSL 0.9.8 defines EVP_PKEY_encrypt and EVP_PKEY_decrypt functions, - but they are a completely different signature from the ones in 1.0.0+. - These wrapper functions allows us to safely declare them on any version and - conditionally remove them on 0.9.8. */ -int Cryptography_EVP_PKEY_encrypt(EVP_PKEY_CTX *ctx, unsigned char *out, - size_t *outlen, const unsigned char *in, - size_t inlen) { - return EVP_PKEY_encrypt(ctx, out, outlen, in, inlen); + +int Cryptography_EVP_PKEY_id(const EVP_PKEY *key) { + return EVP_PKEY_id(key); } -int Cryptography_EVP_PKEY_decrypt(EVP_PKEY_CTX *ctx, unsigned char *out, - size_t *outlen, const unsigned char *in, - size_t inlen) { - return EVP_PKEY_decrypt(ctx, out, outlen, in, inlen); + +EVP_MD_CTX *Cryptography_EVP_MD_CTX_new(void) { +#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 + return EVP_MD_CTX_create(); +#else + return EVP_MD_CTX_new(); +#endif } +void Cryptography_EVP_MD_CTX_free(EVP_MD_CTX *ctx) { +#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 + EVP_MD_CTX_destroy(ctx); #else -const long Cryptography_HAS_PBKDF2_HMAC = 0; -int (*PKCS5_PBKDF2_HMAC)(const char *, int, const unsigned char *, int, int, - const EVP_MD *, int, unsigned char *) = NULL; -const long Cryptography_HAS_PKEY_CTX = 0; -typedef void EVP_PKEY_CTX; -int (*EVP_PKEY_CTX_set_signature_md)(EVP_PKEY_CTX *, const EVP_MD *) = NULL; -int (*EVP_PKEY_sign_init)(EVP_PKEY_CTX *) = NULL; -int (*EVP_PKEY_sign)(EVP_PKEY_CTX *, unsigned char *, size_t *, - const unsigned char *, size_t) = NULL; -int (*EVP_PKEY_verify_init)(EVP_PKEY_CTX *) = NULL; -int (*EVP_PKEY_verify)(EVP_PKEY_CTX *, const unsigned char *, size_t, - const unsigned char *, size_t) = NULL; -EVP_PKEY_CTX *(*EVP_PKEY_CTX_new)(EVP_PKEY *, ENGINE *) = NULL; -EVP_PKEY_CTX *(*EVP_PKEY_CTX_new_id)(int, ENGINE *) = NULL; -EVP_PKEY_CTX *(*EVP_PKEY_CTX_dup)(EVP_PKEY_CTX *) = NULL; -void (*EVP_PKEY_CTX_free)(EVP_PKEY_CTX *) = NULL; -int (*EVP_PKEY_encrypt_init)(EVP_PKEY_CTX *) = NULL; -int (*EVP_PKEY_decrypt_init)(EVP_PKEY_CTX *) = NULL; -int (*Cryptography_EVP_PKEY_encrypt)(EVP_PKEY_CTX *, unsigned char *, size_t *, - const unsigned char *, size_t) = NULL; -int (*Cryptography_EVP_PKEY_decrypt)(EVP_PKEY_CTX *, unsigned char *, size_t *, - const unsigned char *, size_t) = NULL; -int (*EVP_PKEY_id)(const EVP_PKEY *) = NULL; + EVP_MD_CTX_free(ctx); #endif -#ifdef OPENSSL_NO_EC -int (*EVP_PKEY_assign_EC_KEY)(EVP_PKEY *, EC_KEY *) = NULL; -EC_KEY *(*EVP_PKEY_get1_EC_KEY)(EVP_PKEY *) = NULL; -int (*EVP_PKEY_set1_EC_KEY)(EVP_PKEY *, EC_KEY *) = NULL; +} +#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 || defined(OPENSSL_NO_SCRYPT) +static const long Cryptography_HAS_SCRYPT = 0; +int (*EVP_PBE_scrypt)(const char *, size_t, const unsigned char *, size_t, + uint64_t, uint64_t, uint64_t, uint64_t, unsigned char *, + size_t) = NULL; +#else +static const long Cryptography_HAS_SCRYPT = 1; #endif -""" -CONDITIONAL_NAMES = { - "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", - ], - "Cryptography_HAS_EC": [ - "EVP_PKEY_assign_EC_KEY", - "EVP_PKEY_get1_EC_KEY", - "EVP_PKEY_set1_EC_KEY", - ] -} +#if CRYPTOGRAPHY_OPENSSL_110_OR_GREATER +static const long Cryptography_HAS_EVP_PKEY_get_set_tls_encodedpoint = 1; +#else +static const long Cryptography_HAS_EVP_PKEY_get_set_tls_encodedpoint = 0; +size_t (*EVP_PKEY_get1_tls_encodedpoint)(EVP_PKEY *, unsigned char **) = NULL; +int (*EVP_PKEY_set1_tls_encodedpoint)(EVP_PKEY *, const unsigned char *, + size_t) = NULL; +#endif + +#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_111 +static const long Cryptography_HAS_ONESHOT_EVP_DIGEST_SIGN_VERIFY = 0; +static const long Cryptography_HAS_RAW_KEY = 0; +static const long Cryptography_HAS_EVP_DIGESTFINAL_XOF = 0; +int (*EVP_DigestFinalXOF)(EVP_MD_CTX *, unsigned char *, size_t) = NULL; +int (*EVP_DigestSign)(EVP_MD_CTX *, unsigned char *, size_t *, + const unsigned char *tbs, size_t) = NULL; +int (*EVP_DigestVerify)(EVP_MD_CTX *, const unsigned char *, size_t, + const unsigned char *, size_t) = NULL; +EVP_PKEY *(*EVP_PKEY_new_raw_private_key)(int, ENGINE *, const unsigned char *, + size_t) = NULL; +EVP_PKEY *(*EVP_PKEY_new_raw_public_key)(int, ENGINE *, const unsigned char *, + size_t) = NULL; +int (*EVP_PKEY_get_raw_private_key)(const EVP_PKEY *, unsigned char *, + size_t *) = NULL; +int (*EVP_PKEY_get_raw_public_key)(const EVP_PKEY *, unsigned char *, + size_t *) = NULL; +#else +static const long Cryptography_HAS_ONESHOT_EVP_DIGEST_SIGN_VERIFY = 1; +static const long Cryptography_HAS_RAW_KEY = 1; +static const long Cryptography_HAS_EVP_DIGESTFINAL_XOF = 1; +#endif + +/* OpenSSL 1.1.0+ does this define for us, but if not present we'll do it */ +#if !defined(EVP_CTRL_AEAD_SET_IVLEN) +# define EVP_CTRL_AEAD_SET_IVLEN EVP_CTRL_GCM_SET_IVLEN +#endif +#if !defined(EVP_CTRL_AEAD_GET_TAG) +# define EVP_CTRL_AEAD_GET_TAG EVP_CTRL_GCM_GET_TAG +#endif +#if !defined(EVP_CTRL_AEAD_SET_TAG) +# define EVP_CTRL_AEAD_SET_TAG EVP_CTRL_GCM_SET_TAG +#endif + +/* This is tied to X25519 support so we reuse the Cryptography_HAS_X25519 + conditional to remove it. OpenSSL 1.1.0 didn't have this define, but + 1.1.1 will when it is released. We can remove this in the distant + future when we drop 1.1.0 support. */ +#ifndef EVP_PKEY_X25519 +#define EVP_PKEY_X25519 NID_X25519 +#endif + +/* This is tied to X448 support so we reuse the Cryptography_HAS_X448 + conditional to remove it. OpenSSL 1.1.1 adds this define. We can remove + this in the distant future when we drop 1.1.0 support. */ +#ifndef EVP_PKEY_X448 +#define EVP_PKEY_X448 NID_X448 +#endif + +/* This is tied to ED25519 support so we reuse the Cryptography_HAS_ED25519 + conditional to remove it. */ +#ifndef EVP_PKEY_ED25519 +#define EVP_PKEY_ED25519 NID_ED25519 +#endif + +/* This is tied to ED448 support so we reuse the Cryptography_HAS_ED448 + conditional to remove it. */ +#ifndef EVP_PKEY_ED448 +#define EVP_PKEY_ED448 NID_ED448 +#endif + +/* This is tied to poly1305 support so we reuse the Cryptography_HAS_POLY1305 + conditional to remove it. */ +#ifndef EVP_PKEY_POLY1305 +#define EVP_PKEY_POLY1305 NID_poly1305 +#endif +""" diff --git a/src/_cffi_src/commoncrypto/secitem.py b/src/_cffi_src/openssl/fips.py index dd255430..c92bca49 100644 --- a/src/_cffi_src/commoncrypto/secitem.py +++ b/src/_cffi_src/openssl/fips.py @@ -5,25 +5,24 @@ from __future__ import absolute_import, division, print_function INCLUDES = """ -#include <Security/SecItem.h> +#include <openssl/crypto.h> """ TYPES = """ -const CFTypeRef kSecAttrKeyType; -const CFTypeRef kSecAttrKeySizeInBits; -const CFTypeRef kSecAttrIsPermanent; -const CFTypeRef kSecAttrKeyTypeRSA; -const CFTypeRef kSecAttrKeyTypeDSA; -const CFTypeRef kSecUseKeychain; +static const long Cryptography_HAS_FIPS; """ FUNCTIONS = """ -""" - -MACROS = """ +int FIPS_mode_set(int); +int FIPS_mode(void); """ CUSTOMIZATIONS = """ +#if CRYPTOGRAPHY_IS_LIBRESSL +static const long Cryptography_HAS_FIPS = 0; +int (*FIPS_mode_set)(int) = NULL; +int (*FIPS_mode)(void) = NULL; +#else +static const long Cryptography_HAS_FIPS = 1; +#endif """ - -CONDITIONAL_NAMES = {} diff --git a/src/_cffi_src/openssl/hmac.py b/src/_cffi_src/openssl/hmac.py index 86bbdfc2..b006e642 100644 --- a/src/_cffi_src/openssl/hmac.py +++ b/src/_cffi_src/openssl/hmac.py @@ -9,77 +9,40 @@ INCLUDES = """ """ TYPES = """ -typedef struct { ...; } HMAC_CTX; +typedef ... HMAC_CTX; """ FUNCTIONS = """ -void HMAC_CTX_init(HMAC_CTX *); -void HMAC_CTX_cleanup(HMAC_CTX *); +int HMAC_Init_ex(HMAC_CTX *, const void *, int, const EVP_MD *, ENGINE *); +int HMAC_Update(HMAC_CTX *, const unsigned char *, size_t); +int HMAC_Final(HMAC_CTX *, unsigned char *, unsigned int *); +int HMAC_CTX_copy(HMAC_CTX *, HMAC_CTX *); -int Cryptography_HMAC_Init_ex(HMAC_CTX *, const void *, int, const EVP_MD *, - ENGINE *); -int Cryptography_HMAC_Update(HMAC_CTX *, const unsigned char *, size_t); -int Cryptography_HMAC_Final(HMAC_CTX *, unsigned char *, unsigned int *); -int Cryptography_HMAC_CTX_copy(HMAC_CTX *, HMAC_CTX *); -""" - -MACROS = """ +HMAC_CTX *Cryptography_HMAC_CTX_new(void); +void Cryptography_HMAC_CTX_free(HMAC_CTX *ctx); """ CUSTOMIZATIONS = """ -int Cryptography_HMAC_Init_ex(HMAC_CTX *ctx, const void *key, int key_len, - const EVP_MD *md, ENGINE *impl) { -#if OPENSSL_VERSION_NUMBER >= 0x010000000 - return HMAC_Init_ex(ctx, key, key_len, md, impl); +HMAC_CTX *Cryptography_HMAC_CTX_new(void) { +#if CRYPTOGRAPHY_OPENSSL_110_OR_GREATER + return HMAC_CTX_new(); #else - HMAC_Init_ex(ctx, key, key_len, md, impl); - return 1; + /* This uses OPENSSL_zalloc in 1.1.0, which is malloc + memset */ + HMAC_CTX *ctx = (HMAC_CTX *)OPENSSL_malloc(sizeof(HMAC_CTX)); + memset(ctx, 0, sizeof(HMAC_CTX)); + return ctx; #endif } -int Cryptography_HMAC_Update(HMAC_CTX *ctx, const unsigned char *data, - size_t data_len) { -#if OPENSSL_VERSION_NUMBER >= 0x010000000 - return HMAC_Update(ctx, data, data_len); -#else - HMAC_Update(ctx, data, data_len); - return 1; -#endif -} -int Cryptography_HMAC_Final(HMAC_CTX *ctx, unsigned char *digest, - unsigned int *outlen) { -#if OPENSSL_VERSION_NUMBER >= 0x010000000 - return HMAC_Final(ctx, digest, outlen); +void Cryptography_HMAC_CTX_free(HMAC_CTX *ctx) { +#if CRYPTOGRAPHY_OPENSSL_110_OR_GREATER + return HMAC_CTX_free(ctx); #else - HMAC_Final(ctx, digest, outlen); - return 1; -#endif -} - -int Cryptography_HMAC_CTX_copy(HMAC_CTX *dst_ctx, HMAC_CTX *src_ctx) { -#if OPENSSL_VERSION_NUMBER >= 0x010000000 - return HMAC_CTX_copy(dst_ctx, src_ctx); -#else - HMAC_CTX_init(dst_ctx); - if (!EVP_MD_CTX_copy_ex(&dst_ctx->i_ctx, &src_ctx->i_ctx)) { - goto err; - } - if (!EVP_MD_CTX_copy_ex(&dst_ctx->o_ctx, &src_ctx->o_ctx)) { - goto err; + if (ctx != NULL) { + HMAC_CTX_cleanup(ctx); + OPENSSL_free(ctx); } - if (!EVP_MD_CTX_copy_ex(&dst_ctx->md_ctx, &src_ctx->md_ctx)) { - goto err; - } - memcpy(dst_ctx->key, src_ctx->key, HMAC_MAX_MD_CBLOCK); - dst_ctx->key_length = src_ctx->key_length; - dst_ctx->md = src_ctx->md; - return 1; - - err: - return 0; #endif } """ - -CONDITIONAL_NAMES = {} diff --git a/src/_cffi_src/openssl/nid.py b/src/_cffi_src/openssl/nid.py index c2c0552b..cdd4c0db 100644 --- a/src/_cffi_src/openssl/nid.py +++ b/src/_cffi_src/openssl/nid.py @@ -9,240 +9,56 @@ INCLUDES = """ """ TYPES = """ -static const int Cryptography_HAS_ECDSA_SHA2_NIDS; +static const int Cryptography_HAS_X25519; +static const int Cryptography_HAS_X448; +static const int Cryptography_HAS_ED448; +static const int Cryptography_HAS_ED25519; +static const int Cryptography_HAS_POLY1305; static const int NID_undef; -static const int NID_dsa; -static const int NID_dsaWithSHA; -static const int NID_dsaWithSHA1; -static const int NID_md2; -static const int NID_md4; -static const int NID_md5; -static const int NID_mdc2; -static const int NID_ripemd160; -static const int NID_sha; -static const int NID_sha1; -static const int NID_sha256; -static const int NID_sha384; -static const int NID_sha512; -static const int NID_sha224; -static const int NID_sha; -static const int NID_ecdsa_with_SHA1; -static const int NID_ecdsa_with_SHA224; -static const int NID_ecdsa_with_SHA256; -static const int NID_ecdsa_with_SHA384; -static const int NID_ecdsa_with_SHA512; static const int NID_pbe_WithSHA1And3_Key_TripleDES_CBC; -static const int NID_X9_62_c2pnb163v1; -static const int NID_X9_62_c2pnb163v2; -static const int NID_X9_62_c2pnb163v3; -static const int NID_X9_62_c2pnb176v1; -static const int NID_X9_62_c2tnb191v1; -static const int NID_X9_62_c2tnb191v2; -static const int NID_X9_62_c2tnb191v3; -static const int NID_X9_62_c2onb191v4; -static const int NID_X9_62_c2onb191v5; -static const int NID_X9_62_c2pnb208w1; -static const int NID_X9_62_c2tnb239v1; -static const int NID_X9_62_c2tnb239v2; -static const int NID_X9_62_c2tnb239v3; -static const int NID_X9_62_c2onb239v4; -static const int NID_X9_62_c2onb239v5; -static const int NID_X9_62_c2pnb272w1; -static const int NID_X9_62_c2pnb304w1; -static const int NID_X9_62_c2tnb359v1; -static const int NID_X9_62_c2pnb368w1; -static const int NID_X9_62_c2tnb431r1; -static const int NID_X9_62_prime192v1; -static const int NID_X9_62_prime192v2; -static const int NID_X9_62_prime192v3; -static const int NID_X9_62_prime239v1; -static const int NID_X9_62_prime239v2; -static const int NID_X9_62_prime239v3; -static const int NID_X9_62_prime256v1; -static const int NID_secp112r1; -static const int NID_secp112r2; -static const int NID_secp128r1; -static const int NID_secp128r2; -static const int NID_secp160k1; -static const int NID_secp160r1; -static const int NID_secp160r2; -static const int NID_sect163k1; -static const int NID_sect163r1; -static const int NID_sect163r2; -static const int NID_secp192k1; -static const int NID_secp224k1; -static const int NID_secp224r1; -static const int NID_secp256k1; -static const int NID_secp384r1; -static const int NID_secp521r1; -static const int NID_sect113r1; -static const int NID_sect113r2; -static const int NID_sect131r1; -static const int NID_sect131r2; -static const int NID_sect193r1; -static const int NID_sect193r2; -static const int NID_sect233k1; -static const int NID_sect233r1; -static const int NID_sect239k1; -static const int NID_sect283k1; -static const int NID_sect283r1; -static const int NID_sect409k1; -static const int NID_sect409r1; -static const int NID_sect571k1; -static const int NID_sect571r1; -static const int NID_wap_wsg_idm_ecid_wtls1; -static const int NID_wap_wsg_idm_ecid_wtls3; -static const int NID_wap_wsg_idm_ecid_wtls4; -static const int NID_wap_wsg_idm_ecid_wtls5; -static const int NID_wap_wsg_idm_ecid_wtls6; -static const int NID_wap_wsg_idm_ecid_wtls7; -static const int NID_wap_wsg_idm_ecid_wtls8; -static const int NID_wap_wsg_idm_ecid_wtls9; -static const int NID_wap_wsg_idm_ecid_wtls10; -static const int NID_wap_wsg_idm_ecid_wtls11; -static const int NID_wap_wsg_idm_ecid_wtls12; -static const int NID_ipsec3; -static const int NID_ipsec4; -static const char *const SN_X9_62_c2pnb163v1; -static const char *const SN_X9_62_c2pnb163v2; -static const char *const SN_X9_62_c2pnb163v3; -static const char *const SN_X9_62_c2pnb176v1; -static const char *const SN_X9_62_c2tnb191v1; -static const char *const SN_X9_62_c2tnb191v2; -static const char *const SN_X9_62_c2tnb191v3; -static const char *const SN_X9_62_c2onb191v4; -static const char *const SN_X9_62_c2onb191v5; -static const char *const SN_X9_62_c2pnb208w1; -static const char *const SN_X9_62_c2tnb239v1; -static const char *const SN_X9_62_c2tnb239v2; -static const char *const SN_X9_62_c2tnb239v3; -static const char *const SN_X9_62_c2onb239v4; -static const char *const SN_X9_62_c2onb239v5; -static const char *const SN_X9_62_c2pnb272w1; -static const char *const SN_X9_62_c2pnb304w1; -static const char *const SN_X9_62_c2tnb359v1; -static const char *const SN_X9_62_c2pnb368w1; -static const char *const SN_X9_62_c2tnb431r1; -static const char *const SN_X9_62_prime192v1; -static const char *const SN_X9_62_prime192v2; -static const char *const SN_X9_62_prime192v3; -static const char *const SN_X9_62_prime239v1; -static const char *const SN_X9_62_prime239v2; -static const char *const SN_X9_62_prime239v3; -static const char *const SN_X9_62_prime256v1; -static const char *const SN_secp112r1; -static const char *const SN_secp112r2; -static const char *const SN_secp128r1; -static const char *const SN_secp128r2; -static const char *const SN_secp160k1; -static const char *const SN_secp160r1; -static const char *const SN_secp160r2; -static const char *const SN_sect163k1; -static const char *const SN_sect163r1; -static const char *const SN_sect163r2; -static const char *const SN_secp192k1; -static const char *const SN_secp224k1; -static const char *const SN_secp224r1; -static const char *const SN_secp256k1; -static const char *const SN_secp384r1; -static const char *const SN_secp521r1; -static const char *const SN_sect113r1; -static const char *const SN_sect113r2; -static const char *const SN_sect131r1; -static const char *const SN_sect131r2; -static const char *const SN_sect193r1; -static const char *const SN_sect193r2; -static const char *const SN_sect233k1; -static const char *const SN_sect233r1; -static const char *const SN_sect239k1; -static const char *const SN_sect283k1; -static const char *const SN_sect283r1; -static const char *const SN_sect409k1; -static const char *const SN_sect409r1; -static const char *const SN_sect571k1; -static const char *const SN_sect571r1; -static const char *const SN_wap_wsg_idm_ecid_wtls1; -static const char *const SN_wap_wsg_idm_ecid_wtls3; -static const char *const SN_wap_wsg_idm_ecid_wtls4; -static const char *const SN_wap_wsg_idm_ecid_wtls5; -static const char *const SN_wap_wsg_idm_ecid_wtls6; -static const char *const SN_wap_wsg_idm_ecid_wtls7; -static const char *const SN_wap_wsg_idm_ecid_wtls8; -static const char *const SN_wap_wsg_idm_ecid_wtls9; -static const char *const SN_wap_wsg_idm_ecid_wtls10; -static const char *const SN_wap_wsg_idm_ecid_wtls11; -static const char *const SN_wap_wsg_idm_ecid_wtls12; -static const char *const SN_ipsec3; -static const char *const SN_ipsec4; +static const int NID_X25519; +static const int NID_X448; +static const int NID_ED25519; +static const int NID_ED448; +static const int NID_poly1305; -static const int NID_subject_key_identifier; -static const int NID_authority_key_identifier; -static const int NID_policy_constraints; -static const int NID_ext_key_usage; -static const int NID_info_access; -static const int NID_key_usage; static const int NID_subject_alt_name; -static const int NID_issuer_alt_name; -static const int NID_basic_constraints; -static const int NID_issuing_distribution_point; -static const int NID_certificate_issuer; -static const int NID_name_constraints; -static const int NID_crl_distribution_points; -static const int NID_certificate_policies; -static const int NID_inhibit_any_policy; - -static const int NID_private_key_usage_period; -static const int NID_crl_number; static const int NID_crl_reason; -static const int NID_invalidity_date; -static const int NID_delta_crl; -static const int NID_any_policy; -static const int NID_policy_mappings; -static const int NID_target_information; -static const int NID_no_rev_avail; - -static const int NID_commonName; -static const int NID_countryName; -static const int NID_localityName; -static const int NID_stateOrProvinceName; -static const int NID_organizationName; -static const int NID_organizationalUnitName; -static const int NID_serialNumber; -static const int NID_surname; -static const int NID_givenName; -static const int NID_title; -static const int NID_generationQualifier; -static const int NID_dnQualifier; -static const int NID_pseudonym; -static const int NID_domainComponent; -static const int NID_pkcs9_emailAddress; """ FUNCTIONS = """ """ -MACROS = """ -""" - CUSTOMIZATIONS = """ -/* OpenSSL 0.9.8g+ */ -#if OPENSSL_VERSION_NUMBER >= 0x0090807fL -static const long Cryptography_HAS_ECDSA_SHA2_NIDS = 1; +#ifndef NID_X25519 +static const long Cryptography_HAS_X25519 = 0; +static const int NID_X25519 = 0; #else -static const long Cryptography_HAS_ECDSA_SHA2_NIDS = 0; -static const int NID_ecdsa_with_SHA224 = 0; -static const int NID_ecdsa_with_SHA256 = 0; -static const int NID_ecdsa_with_SHA384 = 0; -static const int NID_ecdsa_with_SHA512 = 0; +static const long Cryptography_HAS_X25519 = 1; +#endif +#ifndef NID_ED25519 +static const long Cryptography_HAS_ED25519 = 0; +static const int NID_ED25519 = 0; +#else +static const long Cryptography_HAS_ED25519 = 1; +#endif +#ifndef NID_X448 +static const long Cryptography_HAS_X448 = 0; +static const int NID_X448 = 0; +#else +static const long Cryptography_HAS_X448 = 1; +#endif +#ifndef NID_ED448 +static const long Cryptography_HAS_ED448 = 0; +static const int NID_ED448 = 0; +#else +static const long Cryptography_HAS_ED448 = 1; +#endif +#ifndef NID_poly1305 +static const long Cryptography_HAS_POLY1305 = 0; +static const int NID_poly1305 = 0; +#else +static const long Cryptography_HAS_POLY1305 = 1; #endif """ - -CONDITIONAL_NAMES = { - "Cryptography_HAS_ECDSA_SHA2_NIDS": [ - "NID_ecdsa_with_SHA224", - "NID_ecdsa_with_SHA256", - "NID_ecdsa_with_SHA384", - "NID_ecdsa_with_SHA512", - ], -} diff --git a/src/_cffi_src/openssl/objects.py b/src/_cffi_src/openssl/objects.py index 9c480b37..265ac75c 100644 --- a/src/_cffi_src/openssl/objects.py +++ b/src/_cffi_src/openssl/objects.py @@ -9,6 +9,14 @@ INCLUDES = """ """ TYPES = """ +typedef struct { + int type; + int alias; + const char *name; + const char *data; +} OBJ_NAME; + +static const long OBJ_NAME_TYPE_MD_METH; """ FUNCTIONS = """ @@ -24,13 +32,10 @@ int OBJ_obj2txt(char *, int, const ASN1_OBJECT *, int); int OBJ_cmp(const ASN1_OBJECT *, const ASN1_OBJECT *); ASN1_OBJECT *OBJ_dup(const ASN1_OBJECT *); int OBJ_create(const char *, const char *, const char *); +void OBJ_NAME_do_all(int, void (*) (const OBJ_NAME *, void *), void *); +/* OBJ_cleanup became a macro in 1.1.0 */ void OBJ_cleanup(void); """ -MACROS = """ -""" - CUSTOMIZATIONS = """ """ - -CONDITIONAL_NAMES = {} diff --git a/src/_cffi_src/openssl/ocsp.py b/src/_cffi_src/openssl/ocsp.py new file mode 100644 index 00000000..829314a3 --- /dev/null +++ b/src/_cffi_src/openssl/ocsp.py @@ -0,0 +1,170 @@ +# 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 + +INCLUDES = """ +#include <openssl/ocsp.h> +""" + +TYPES = """ +typedef ... OCSP_REQUEST; +typedef ... OCSP_ONEREQ; +typedef ... OCSP_RESPONSE; +typedef ... OCSP_BASICRESP; +typedef ... OCSP_SINGLERESP; +typedef ... OCSP_CERTID; +typedef ... OCSP_RESPDATA; +static const long OCSP_NOCERTS; +static const long OCSP_RESPID_KEY; +""" + +FUNCTIONS = """ +int OCSP_response_status(OCSP_RESPONSE *); +OCSP_BASICRESP *OCSP_response_get1_basic(OCSP_RESPONSE *); +int OCSP_BASICRESP_get_ext_count(OCSP_BASICRESP *); +const ASN1_OCTET_STRING *OCSP_resp_get0_signature(const OCSP_BASICRESP *); +Cryptography_STACK_OF_X509 *OCSP_resp_get0_certs(const OCSP_BASICRESP *); +const ASN1_GENERALIZEDTIME *OCSP_resp_get0_produced_at( + const OCSP_BASICRESP *); +const OCSP_CERTID *OCSP_SINGLERESP_get0_id(const OCSP_SINGLERESP *); +int OCSP_resp_get0_id(const OCSP_BASICRESP *, const ASN1_OCTET_STRING **, + const X509_NAME **); +const X509_ALGOR *OCSP_resp_get0_tbs_sigalg(const OCSP_BASICRESP *); +const OCSP_RESPDATA *OCSP_resp_get0_respdata(const OCSP_BASICRESP *); +X509_EXTENSION *OCSP_BASICRESP_get_ext(OCSP_BASICRESP *, int); +int OCSP_resp_count(OCSP_BASICRESP *); +OCSP_SINGLERESP *OCSP_resp_get0(OCSP_BASICRESP *, int); +int OCSP_SINGLERESP_get_ext_count(OCSP_SINGLERESP *); +X509_EXTENSION *OCSP_SINGLERESP_get_ext(OCSP_SINGLERESP *, int); + +int OCSP_single_get0_status(OCSP_SINGLERESP *, int *, ASN1_GENERALIZEDTIME **, + ASN1_GENERALIZEDTIME **, ASN1_GENERALIZEDTIME **); + +int OCSP_REQUEST_get_ext_count(OCSP_REQUEST *); +X509_EXTENSION *OCSP_REQUEST_get_ext(OCSP_REQUEST *, int); +int OCSP_request_onereq_count(OCSP_REQUEST *); +OCSP_ONEREQ *OCSP_request_onereq_get0(OCSP_REQUEST *, int); +int OCSP_ONEREQ_get_ext_count(OCSP_ONEREQ *); +X509_EXTENSION *OCSP_ONEREQ_get_ext(OCSP_ONEREQ *, int); +OCSP_CERTID *OCSP_onereq_get0_id(OCSP_ONEREQ *); +OCSP_ONEREQ *OCSP_request_add0_id(OCSP_REQUEST *, OCSP_CERTID *); +OCSP_CERTID *OCSP_cert_to_id(const EVP_MD *, const X509 *, const X509 *); +void OCSP_CERTID_free(OCSP_CERTID *); + + +OCSP_BASICRESP *OCSP_BASICRESP_new(void); +void OCSP_BASICRESP_free(OCSP_BASICRESP *); +OCSP_SINGLERESP *OCSP_basic_add1_status(OCSP_BASICRESP *, OCSP_CERTID *, int, + int, ASN1_TIME *, ASN1_TIME *, + ASN1_TIME *); +int OCSP_basic_add1_nonce(OCSP_BASICRESP *, unsigned char *, int); +int OCSP_basic_add1_cert(OCSP_BASICRESP *, X509 *); +int OCSP_BASICRESP_add_ext(OCSP_BASICRESP *, X509_EXTENSION *, int); +int OCSP_basic_sign(OCSP_BASICRESP *, X509 *, EVP_PKEY *, const EVP_MD *, + Cryptography_STACK_OF_X509 *, unsigned long); +OCSP_RESPONSE *OCSP_response_create(int, OCSP_BASICRESP *); +void OCSP_RESPONSE_free(OCSP_RESPONSE *); + +OCSP_REQUEST *OCSP_REQUEST_new(void); +void OCSP_REQUEST_free(OCSP_REQUEST *); +int OCSP_request_add1_nonce(OCSP_REQUEST *, unsigned char *, int); +int OCSP_REQUEST_add_ext(OCSP_REQUEST *, X509_EXTENSION *, int); +int OCSP_id_get0_info(ASN1_OCTET_STRING **, ASN1_OBJECT **, + ASN1_OCTET_STRING **, ASN1_INTEGER **, OCSP_CERTID *); +OCSP_REQUEST *d2i_OCSP_REQUEST_bio(BIO *, OCSP_REQUEST **); +OCSP_RESPONSE *d2i_OCSP_RESPONSE_bio(BIO *, OCSP_RESPONSE **); +int i2d_OCSP_REQUEST_bio(BIO *, OCSP_REQUEST *); +int i2d_OCSP_RESPONSE_bio(BIO *, OCSP_RESPONSE *); +int i2d_OCSP_RESPDATA(OCSP_RESPDATA *, unsigned char **); +""" + +CUSTOMIZATIONS = """ +#if ( \ + CRYPTOGRAPHY_OPENSSL_110_OR_GREATER && \ + CRYPTOGRAPHY_OPENSSL_LESS_THAN_110J \ + ) +/* These structs come from ocsp_lcl.h and are needed to de-opaque the struct + for the getters in OpenSSL 1.1.0 through 1.1.0i */ +struct ocsp_responder_id_st { + int type; + union { + X509_NAME *byName; + ASN1_OCTET_STRING *byKey; + } value; +}; +struct ocsp_response_data_st { + ASN1_INTEGER *version; + OCSP_RESPID responderId; + ASN1_GENERALIZEDTIME *producedAt; + STACK_OF(OCSP_SINGLERESP) *responses; + STACK_OF(X509_EXTENSION) *responseExtensions; +}; +struct ocsp_basic_response_st { + OCSP_RESPDATA tbsResponseData; + X509_ALGOR signatureAlgorithm; + ASN1_BIT_STRING *signature; + STACK_OF(X509) *certs; +}; +#endif + +#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 +/* These functions are all taken from ocsp_cl.c in OpenSSL 1.1.0 */ +const OCSP_CERTID *OCSP_SINGLERESP_get0_id(const OCSP_SINGLERESP *single) +{ + return single->certId; +} +const Cryptography_STACK_OF_X509 *OCSP_resp_get0_certs( + const OCSP_BASICRESP *bs) +{ + return bs->certs; +} +int OCSP_resp_get0_id(const OCSP_BASICRESP *bs, + const ASN1_OCTET_STRING **pid, + const X509_NAME **pname) +{ + const OCSP_RESPID *rid = bs->tbsResponseData->responderId; + + if (rid->type == V_OCSP_RESPID_NAME) { + *pname = rid->value.byName; + *pid = NULL; + } else if (rid->type == V_OCSP_RESPID_KEY) { + *pid = rid->value.byKey; + *pname = NULL; + } else { + return 0; + } + return 1; +} +const ASN1_GENERALIZEDTIME *OCSP_resp_get0_produced_at( + const OCSP_BASICRESP* bs) +{ + return bs->tbsResponseData->producedAt; +} +const ASN1_OCTET_STRING *OCSP_resp_get0_signature(const OCSP_BASICRESP *bs) +{ + return bs->signature; +} +#endif + +#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110J +const X509_ALGOR *OCSP_resp_get0_tbs_sigalg(const OCSP_BASICRESP *bs) +{ +#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 + return bs->signatureAlgorithm; +#else + return &bs->signatureAlgorithm; +#endif +} + +const OCSP_RESPDATA *OCSP_resp_get0_respdata(const OCSP_BASICRESP *bs) +{ +#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 + return bs->tbsResponseData; +#else + return &bs->tbsResponseData; +#endif +} +#endif +""" diff --git a/src/_cffi_src/openssl/opensslv.py b/src/_cffi_src/openssl/opensslv.py index e6c5f269..9b0c6893 100644 --- a/src/_cffi_src/openssl/opensslv.py +++ b/src/_cffi_src/openssl/opensslv.py @@ -18,10 +18,5 @@ static const char *const OPENSSL_VERSION_TEXT; FUNCTIONS = """ """ -MACROS = """ -""" - CUSTOMIZATIONS = """ """ - -CONDITIONAL_NAMES = {} diff --git a/src/_cffi_src/openssl/osrandom_engine.py b/src/_cffi_src/openssl/osrandom_engine.py new file mode 100644 index 00000000..ed1068ef --- /dev/null +++ b/src/_cffi_src/openssl/osrandom_engine.py @@ -0,0 +1,24 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import absolute_import, division, print_function + +import os + +HERE = os.path.dirname(os.path.abspath(__file__)) + +with open(os.path.join(HERE, "src/osrandom_engine.h")) as f: + INCLUDES = f.read() + +TYPES = """ +static const char *const Cryptography_osrandom_engine_name; +static const char *const Cryptography_osrandom_engine_id; +""" + +FUNCTIONS = """ +int Cryptography_add_osrandom_engine(void); +""" + +with open(os.path.join(HERE, "src/osrandom_engine.c")) as f: + CUSTOMIZATIONS = f.read() diff --git a/src/_cffi_src/openssl/pem.py b/src/_cffi_src/openssl/pem.py index 8ec3fefd..09b523d6 100644 --- a/src/_cffi_src/openssl/pem.py +++ b/src/_cffi_src/openssl/pem.py @@ -51,10 +51,6 @@ int PEM_write_bio_PKCS7(BIO *, PKCS7 *); DH *PEM_read_bio_DHparams(BIO *, DH **, pem_password_cb *, void *); -DSA *PEM_read_bio_DSAPrivateKey(BIO *, DSA **, pem_password_cb *, void *); - -RSA *PEM_read_bio_RSAPrivateKey(BIO *, RSA **, pem_password_cb *, void *); - int PEM_write_bio_DSAPrivateKey(BIO *, DSA *, const EVP_CIPHER *, unsigned char *, int, pem_password_cb *, void *); @@ -63,36 +59,21 @@ int PEM_write_bio_RSAPrivateKey(BIO *, RSA *, const EVP_CIPHER *, unsigned char *, int, pem_password_cb *, void *); -DSA *PEM_read_bio_DSA_PUBKEY(BIO *, DSA **, pem_password_cb *, void *); - RSA *PEM_read_bio_RSAPublicKey(BIO *, RSA **, pem_password_cb *, void *); -int PEM_write_bio_DSA_PUBKEY(BIO *, DSA *); - int PEM_write_bio_RSAPublicKey(BIO *, const RSA *); EVP_PKEY *PEM_read_bio_PUBKEY(BIO *, EVP_PKEY **, pem_password_cb *, void *); int PEM_write_bio_PUBKEY(BIO *, EVP_PKEY *); -""" - -MACROS = """ int PEM_write_bio_ECPrivateKey(BIO *, EC_KEY *, const EVP_CIPHER *, unsigned char *, int, pem_password_cb *, void *); +int PEM_write_bio_DHparams(BIO *, DH *); +int PEM_write_bio_DHxparams(BIO *, DH *); """ CUSTOMIZATIONS = """ -// Cryptography_HAS_EC is provided by ec.py so we don't need to define it here -#ifdef OPENSSL_NO_EC -int (*PEM_write_bio_ECPrivateKey)(BIO *, EC_KEY *, const EVP_CIPHER *, - unsigned char *, int, pem_password_cb *, - void *) = NULL; +#if !defined(EVP_PKEY_DHX) || EVP_PKEY_DHX == -1 +int (*PEM_write_bio_DHxparams)(BIO *, DH *) = NULL; #endif - """ - -CONDITIONAL_NAMES = { - "Cryptography_HAS_EC": [ - "PEM_write_bio_ECPrivateKey" - ] -} diff --git a/src/_cffi_src/openssl/pkcs12.py b/src/_cffi_src/openssl/pkcs12.py index fa7564ac..21a8481f 100644 --- a/src/_cffi_src/openssl/pkcs12.py +++ b/src/_cffi_src/openssl/pkcs12.py @@ -17,9 +17,6 @@ void PKCS12_free(PKCS12 *); PKCS12 *d2i_PKCS12_bio(BIO *, PKCS12 **); int i2d_PKCS12_bio(BIO *, PKCS12 *); -""" - -MACROS = """ int PKCS12_parse(PKCS12 *, const char *, EVP_PKEY **, X509 **, Cryptography_STACK_OF_X509 **); PKCS12 *PKCS12_create(char *, char *, EVP_PKEY *, X509 *, @@ -28,5 +25,3 @@ PKCS12 *PKCS12_create(char *, char *, EVP_PKEY *, X509 *, CUSTOMIZATIONS = """ """ - -CONDITIONAL_NAMES = {} diff --git a/src/_cffi_src/openssl/pkcs7.py b/src/_cffi_src/openssl/pkcs7.py index df82afef..1bece5b7 100644 --- a/src/_cffi_src/openssl/pkcs7.py +++ b/src/_cffi_src/openssl/pkcs7.py @@ -10,7 +10,33 @@ INCLUDES = """ TYPES = """ typedef struct { + Cryptography_STACK_OF_X509 *cert; + Cryptography_STACK_OF_X509_CRL *crl; + ...; +} PKCS7_SIGNED; + +typedef struct { + Cryptography_STACK_OF_X509 *cert; + Cryptography_STACK_OF_X509_CRL *crl; + ...; +} PKCS7_SIGN_ENVELOPE; + +typedef ... PKCS7_DIGEST; +typedef ... PKCS7_ENCRYPT; +typedef ... PKCS7_ENVELOPE; + +typedef struct { ASN1_OBJECT *type; + union { + char *ptr; + ASN1_OCTET_STRING *data; + PKCS7_SIGNED *sign; + PKCS7_ENVELOPE *enveloped; + PKCS7_SIGN_ENVELOPE *signed_and_enveloped; + PKCS7_DIGEST *digest; + PKCS7_ENCRYPT *encrypted; + ASN1_TYPE *other; + } d; ...; } PKCS7; @@ -44,15 +70,14 @@ Cryptography_STACK_OF_X509 *PKCS7_get0_signers(PKCS7 *, PKCS7 *PKCS7_encrypt(Cryptography_STACK_OF_X509 *, BIO *, const EVP_CIPHER *, int); int PKCS7_decrypt(PKCS7 *, EVP_PKEY *, X509 *, BIO *, int); -""" -MACROS = """ +BIO *PKCS7_dataInit(PKCS7 *, BIO *); +int PKCS7_type_is_encrypted(PKCS7 *); int PKCS7_type_is_signed(PKCS7 *); int PKCS7_type_is_enveloped(PKCS7 *); int PKCS7_type_is_signedAndEnveloped(PKCS7 *); int PKCS7_type_is_data(PKCS7 *); +int PKCS7_type_is_digest(PKCS7 *); """ CUSTOMIZATIONS = "" - -CONDITIONAL_NAMES = {} diff --git a/src/_cffi_src/openssl/rand.py b/src/_cffi_src/openssl/rand.py index 6330482c..c0cd6836 100644 --- a/src/_cffi_src/openssl/rand.py +++ b/src/_cffi_src/openssl/rand.py @@ -9,43 +9,23 @@ INCLUDES = """ """ TYPES = """ +typedef ... RAND_METHOD; + static const long Cryptography_HAS_EGD; """ FUNCTIONS = """ -void ERR_load_RAND_strings(void); -void RAND_seed(const void *, int); +int RAND_set_rand_method(const RAND_METHOD *); void RAND_add(const void *, int, double); int RAND_status(void); -const char *RAND_file_name(char *, size_t); -int RAND_load_file(const char *, long); -int RAND_write_file(const char *); -void RAND_cleanup(void); int RAND_bytes(unsigned char *, int); -int RAND_pseudo_bytes(unsigned char *, int); -""" - -MACROS = """ -int RAND_egd(const char *); -int RAND_egd_bytes(const char *, int); -int RAND_query_egd_bytes(const char *, unsigned char *, int); +/* ERR_load_RAND_strings started returning an int in 1.1.0. Unfortunately we + can't declare a conditional signature like that. Since it always returns + 1 we'll just lie about the signature to preserve compatibility for + pyOpenSSL (which calls this in its rand.py as of mid-2016) */ +void ERR_load_RAND_strings(void); """ CUSTOMIZATIONS = """ -#if defined(LIBRESSL_VERSION_NUMBER) static const long Cryptography_HAS_EGD = 0; -int (*RAND_egd)(const char *) = NULL; -int (*RAND_egd_bytes)(const char *, int) = NULL; -int (*RAND_query_egd_bytes)(const char *, unsigned char *, int) = NULL; -#else -static const long Cryptography_HAS_EGD = 1; -#endif """ - -CONDITIONAL_NAMES = { - "Cryptography_HAS_EGD": [ - "RAND_egd", - "RAND_egd_bytes", - "RAND_query_egd_bytes", - ] -} diff --git a/src/_cffi_src/openssl/rsa.py b/src/_cffi_src/openssl/rsa.py index 8bac7895..4b915463 100644 --- a/src/_cffi_src/openssl/rsa.py +++ b/src/_cffi_src/openssl/rsa.py @@ -9,28 +9,17 @@ INCLUDES = """ """ TYPES = """ -typedef struct rsa_st { - BIGNUM *n; - BIGNUM *e; - BIGNUM *d; - BIGNUM *p; - BIGNUM *q; - BIGNUM *dmp1; - BIGNUM *dmq1; - BIGNUM *iqmp; - ...; -} RSA; +typedef ... RSA; typedef ... BN_GENCB; static const int RSA_PKCS1_PADDING; -static const int RSA_SSLV23_PADDING; static const int RSA_NO_PADDING; static const int RSA_PKCS1_OAEP_PADDING; -static const int RSA_X931_PADDING; static const int RSA_PKCS1_PSS_PADDING; static const int RSA_F4; static const int Cryptography_HAS_PSS_PADDING; -static const int Cryptography_HAS_MGF1_MD; +static const int Cryptography_HAS_RSA_OAEP_MD; +static const int Cryptography_HAS_RSA_OAEP_LABEL; """ FUNCTIONS = """ @@ -41,7 +30,6 @@ int RSA_generate_key_ex(RSA *, int, BIGNUM *, BN_GENCB *); int RSA_check_key(const RSA *); RSA *RSAPublicKey_dup(RSA *); int RSA_blinding_on(RSA *, BN_CTX *); -void RSA_blinding_off(RSA *); int RSA_public_encrypt(int, const unsigned char *, unsigned char *, RSA *, int); int RSA_private_encrypt(int, const unsigned char *, unsigned char *, @@ -51,49 +39,146 @@ int RSA_public_decrypt(int, const unsigned char *, unsigned char *, int RSA_private_decrypt(int, const unsigned char *, unsigned char *, RSA *, int); int RSA_print(BIO *, const RSA *, int); -int RSA_verify_PKCS1_PSS(RSA *, const unsigned char *, const EVP_MD *, - const unsigned char *, int); -int RSA_padding_add_PKCS1_PSS(RSA *, unsigned char *, const unsigned char *, - const EVP_MD *, int); -int RSA_padding_add_PKCS1_OAEP(unsigned char *, int, const unsigned char *, - int, const unsigned char *, int); -int RSA_padding_check_PKCS1_OAEP(unsigned char *, int, const unsigned char *, - int, int, const unsigned char *, int); -""" -MACROS = """ +/* added in 1.1.0 when the RSA struct was opaqued */ +int RSA_set0_key(RSA *, BIGNUM *, BIGNUM *, BIGNUM *); +int RSA_set0_factors(RSA *, BIGNUM *, BIGNUM *); +int RSA_set0_crt_params(RSA *, BIGNUM *, BIGNUM *, BIGNUM *); +void RSA_get0_key(const RSA *, const BIGNUM **, const BIGNUM **, + const BIGNUM **); +void RSA_get0_factors(const RSA *, const BIGNUM **, const BIGNUM **); +void RSA_get0_crt_params(const RSA *, const BIGNUM **, const BIGNUM **, + const BIGNUM **); int EVP_PKEY_CTX_set_rsa_padding(EVP_PKEY_CTX *, int); int EVP_PKEY_CTX_set_rsa_pss_saltlen(EVP_PKEY_CTX *, int); int EVP_PKEY_CTX_set_rsa_mgf1_md(EVP_PKEY_CTX *, EVP_MD *); +int EVP_PKEY_CTX_set0_rsa_oaep_label(EVP_PKEY_CTX *, unsigned char *, int); + +int EVP_PKEY_CTX_set_rsa_oaep_md(EVP_PKEY_CTX *, EVP_MD *); """ CUSTOMIZATIONS = """ -#if OPENSSL_VERSION_NUMBER >= 0x10000000 static const long Cryptography_HAS_PSS_PADDING = 1; + +#if defined(EVP_PKEY_CTX_set_rsa_oaep_md) +static const long Cryptography_HAS_RSA_OAEP_MD = 1; #else -/* see evp.py for the definition of Cryptography_HAS_PKEY_CTX */ -static const long Cryptography_HAS_PSS_PADDING = 0; -int (*EVP_PKEY_CTX_set_rsa_padding)(EVP_PKEY_CTX *, int) = NULL; -int (*EVP_PKEY_CTX_set_rsa_pss_saltlen)(EVP_PKEY_CTX *, int) = NULL; -static const long RSA_PKCS1_PSS_PADDING = 0; +static const long Cryptography_HAS_RSA_OAEP_MD = 0; +int (*EVP_PKEY_CTX_set_rsa_oaep_md)(EVP_PKEY_CTX *, EVP_MD *) = NULL; #endif -#if OPENSSL_VERSION_NUMBER >= 0x1000100f -static const long Cryptography_HAS_MGF1_MD = 1; + +#if defined(EVP_PKEY_CTX_set0_rsa_oaep_label) +static const long Cryptography_HAS_RSA_OAEP_LABEL = 1; #else -static const long Cryptography_HAS_MGF1_MD = 0; -int (*EVP_PKEY_CTX_set_rsa_mgf1_md)(EVP_PKEY_CTX *, EVP_MD *) = NULL; +static const long Cryptography_HAS_RSA_OAEP_LABEL = 0; +int (*EVP_PKEY_CTX_set0_rsa_oaep_label)(EVP_PKEY_CTX *, unsigned char *, + int) = NULL; #endif -""" -CONDITIONAL_NAMES = { - "Cryptography_HAS_PKEY_CTX": [ - "EVP_PKEY_CTX_set_rsa_padding", - "EVP_PKEY_CTX_set_rsa_pss_saltlen", - ], - "Cryptography_HAS_PSS_PADDING": [ - "RSA_PKCS1_PSS_PADDING", - ], - "Cryptography_HAS_MGF1_MD": [ - "EVP_PKEY_CTX_set_rsa_mgf1_md", - ], +/* These functions were added in OpenSSL 1.1.0 */ +#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 && !CRYPTOGRAPHY_IS_LIBRESSL +int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d) +{ + /* If the fields n and e in r are NULL, the corresponding input + * parameters MUST be non-NULL for n and e. d may be + * left NULL (in case only the public key is used). + */ + if ((r->n == NULL && n == NULL) + || (r->e == NULL && e == NULL)) + return 0; + + if (n != NULL) { + BN_free(r->n); + r->n = n; + } + if (e != NULL) { + BN_free(r->e); + r->e = e; + } + if (d != NULL) { + BN_free(r->d); + r->d = d; + } + + return 1; +} + +int RSA_set0_factors(RSA *r, BIGNUM *p, BIGNUM *q) +{ + /* If the fields p and q in r are NULL, the corresponding input + * parameters MUST be non-NULL. + */ + if ((r->p == NULL && p == NULL) + || (r->q == NULL && q == NULL)) + return 0; + + if (p != NULL) { + BN_free(r->p); + r->p = p; + } + if (q != NULL) { + BN_free(r->q); + r->q = q; + } + + return 1; } + +int RSA_set0_crt_params(RSA *r, BIGNUM *dmp1, BIGNUM *dmq1, BIGNUM *iqmp) +{ + /* If the fields dmp1, dmq1 and iqmp in r are NULL, the corresponding input + * parameters MUST be non-NULL. + */ + if ((r->dmp1 == NULL && dmp1 == NULL) + || (r->dmq1 == NULL && dmq1 == NULL) + || (r->iqmp == NULL && iqmp == NULL)) + return 0; + + if (dmp1 != NULL) { + BN_free(r->dmp1); + r->dmp1 = dmp1; + } + if (dmq1 != NULL) { + BN_free(r->dmq1); + r->dmq1 = dmq1; + } + if (iqmp != NULL) { + BN_free(r->iqmp); + r->iqmp = iqmp; + } + + return 1; +} + +void RSA_get0_key(const RSA *r, + const BIGNUM **n, const BIGNUM **e, const BIGNUM **d) +{ + if (n != NULL) + *n = r->n; + if (e != NULL) + *e = r->e; + if (d != NULL) + *d = r->d; +} + +void RSA_get0_factors(const RSA *r, const BIGNUM **p, const BIGNUM **q) +{ + if (p != NULL) + *p = r->p; + if (q != NULL) + *q = r->q; +} + +void RSA_get0_crt_params(const RSA *r, + const BIGNUM **dmp1, const BIGNUM **dmq1, + const BIGNUM **iqmp) +{ + if (dmp1 != NULL) + *dmp1 = r->dmp1; + if (dmq1 != NULL) + *dmq1 = r->dmq1; + if (iqmp != NULL) + *iqmp = r->iqmp; +} +#endif +""" diff --git a/src/_cffi_src/openssl/src/osrandom_engine.c b/src/_cffi_src/openssl/src/osrandom_engine.c new file mode 100644 index 00000000..dc7b1d5e --- /dev/null +++ b/src/_cffi_src/openssl/src/osrandom_engine.c @@ -0,0 +1,659 @@ +/* osurandom engine + * + * Windows CryptGenRandom() + * macOS >= 10.12 getentropy() + * OpenBSD 5.6+ getentropy() + * other BSD getentropy() if SYS_getentropy is defined + * Linux 3.17+ getrandom() with fallback to /dev/urandom + * other /dev/urandom with cached fd + * + * The /dev/urandom, getrandom and getentropy code is derived from Python's + * Python/random.c, written by Antoine Pitrou and Victor Stinner. + * + * Copyright 2001-2016 Python Software Foundation; All Rights Reserved. + */ + +#ifdef __linux__ +#include <poll.h> +#endif + +#ifndef OPENSSL_NO_ENGINE +/* OpenSSL has ENGINE support so build the engine. */ +static const char *Cryptography_osrandom_engine_id = "osrandom"; + +/**************************************************************************** + * Windows + */ +#if CRYPTOGRAPHY_OSRANDOM_ENGINE == CRYPTOGRAPHY_OSRANDOM_ENGINE_CRYPTGENRANDOM +static const char *Cryptography_osrandom_engine_name = "osrandom_engine CryptGenRandom()"; +static HCRYPTPROV hCryptProv = 0; + +static int osrandom_init(ENGINE *e) { + if (hCryptProv != 0) { + return 1; + } + if (CryptAcquireContext(&hCryptProv, NULL, NULL, + PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { + return 1; + } else { + ERR_Cryptography_OSRandom_error( + CRYPTOGRAPHY_OSRANDOM_F_INIT, + CRYPTOGRAPHY_OSRANDOM_R_CRYPTACQUIRECONTEXT, + __FILE__, __LINE__ + ); + return 0; + } +} + +static int osrandom_rand_bytes(unsigned char *buffer, int size) { + if (hCryptProv == 0) { + return 0; + } + + if (!CryptGenRandom(hCryptProv, (DWORD)size, buffer)) { + ERR_Cryptography_OSRandom_error( + CRYPTOGRAPHY_OSRANDOM_F_RAND_BYTES, + CRYPTOGRAPHY_OSRANDOM_R_CRYPTGENRANDOM, + __FILE__, __LINE__ + ); + return 0; + } + return 1; +} + +static int osrandom_finish(ENGINE *e) { + if (CryptReleaseContext(hCryptProv, 0)) { + hCryptProv = 0; + return 1; + } else { + ERR_Cryptography_OSRandom_error( + CRYPTOGRAPHY_OSRANDOM_F_FINISH, + CRYPTOGRAPHY_OSRANDOM_R_CRYPTRELEASECONTEXT, + __FILE__, __LINE__ + ); + return 0; + } +} + +static int osrandom_rand_status(void) { + return hCryptProv != 0; +} + +static const char *osurandom_get_implementation(void) { + return "CryptGenRandom"; +} + +#endif /* CRYPTOGRAPHY_OSRANDOM_ENGINE_CRYPTGENRANDOM */ + +/**************************************************************************** + * /dev/urandom helpers for all non-BSD Unix platforms + */ +#ifdef CRYPTOGRAPHY_OSRANDOM_NEEDS_DEV_URANDOM + +static struct { + int fd; + dev_t st_dev; + ino_t st_ino; +} urandom_cache = { -1 }; + +static int open_cloexec(const char *path) { + int open_flags = O_RDONLY; +#ifdef O_CLOEXEC + open_flags |= O_CLOEXEC; +#endif + + int fd = open(path, open_flags); + if (fd == -1) { + return -1; + } + +#ifndef O_CLOEXEC + int flags = fcntl(fd, F_GETFD); + if (flags == -1) { + return -1; + } + if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) { + return -1; + } +#endif + return fd; +} + +#ifdef __linux__ +/* On Linux, we open("/dev/random") and use poll() to wait until it's readable + * before we read from /dev/urandom, this ensures that we don't read from + * /dev/urandom before the kernel CSPRNG is initialized. This isn't necessary on + * other platforms because they don't have the same _bug_ as Linux does with + * /dev/urandom and early boot. */ +static int wait_on_devrandom(void) { + struct pollfd pfd = {}; + int ret = 0; + int random_fd = open_cloexec("/dev/random"); + if (random_fd < 0) { + return -1; + } + pfd.fd = random_fd; + pfd.events = POLLIN; + pfd.revents = 0; + do { + ret = poll(&pfd, 1, -1); + } while (ret < 0 && (errno == EINTR || errno == EAGAIN)); + close(random_fd); + return ret; +} +#endif + +/* return -1 on error */ +static int dev_urandom_fd(void) { + int fd = -1; + struct stat st; + + /* Check that fd still points to the correct device */ + if (urandom_cache.fd >= 0) { + if (fstat(urandom_cache.fd, &st) + || st.st_dev != urandom_cache.st_dev + || st.st_ino != urandom_cache.st_ino) { + /* Somebody replaced our FD. Invalidate our cache but don't + * close the fd. */ + urandom_cache.fd = -1; + } + } + if (urandom_cache.fd < 0) { +#ifdef __linux__ + if (wait_on_devrandom() < 0) { + goto error; + } +#endif + + fd = open_cloexec("/dev/urandom"); + if (fd < 0) { + goto error; + } + if (fstat(fd, &st)) { + goto error; + } + /* Another thread initialized the fd */ + if (urandom_cache.fd >= 0) { + close(fd); + return urandom_cache.fd; + } + urandom_cache.st_dev = st.st_dev; + urandom_cache.st_ino = st.st_ino; + urandom_cache.fd = fd; + } + return urandom_cache.fd; + + error: + if (fd != -1) { + close(fd); + } + ERR_Cryptography_OSRandom_error( + CRYPTOGRAPHY_OSRANDOM_F_DEV_URANDOM_FD, + CRYPTOGRAPHY_OSRANDOM_R_DEV_URANDOM_OPEN_FAILED, + __FILE__, __LINE__ + ); + return -1; +} + +static int dev_urandom_read(unsigned char *buffer, int size) { + int fd; + int n; + + fd = dev_urandom_fd(); + if (fd < 0) { + return 0; + } + + while (size > 0) { + do { + n = (int)read(fd, buffer, (size_t)size); + } while (n < 0 && errno == EINTR); + + if (n <= 0) { + ERR_Cryptography_OSRandom_error( + CRYPTOGRAPHY_OSRANDOM_F_DEV_URANDOM_READ, + CRYPTOGRAPHY_OSRANDOM_R_DEV_URANDOM_READ_FAILED, + __FILE__, __LINE__ + ); + return 0; + } + buffer += n; + size -= n; + } + return 1; +} + +static void dev_urandom_close(void) { + if (urandom_cache.fd >= 0) { + int fd; + struct stat st; + + if (fstat(urandom_cache.fd, &st) + && st.st_dev == urandom_cache.st_dev + && st.st_ino == urandom_cache.st_ino) { + fd = urandom_cache.fd; + urandom_cache.fd = -1; + close(fd); + } + } +} +#endif /* CRYPTOGRAPHY_OSRANDOM_NEEDS_DEV_URANDOM */ + +/**************************************************************************** + * BSD getentropy + */ +#if CRYPTOGRAPHY_OSRANDOM_ENGINE == CRYPTOGRAPHY_OSRANDOM_ENGINE_GETENTROPY +static const char *Cryptography_osrandom_engine_name = "osrandom_engine getentropy()"; + +static int getentropy_works = CRYPTOGRAPHY_OSRANDOM_GETENTROPY_NOT_INIT; + +static int osrandom_init(ENGINE *e) { +#if !defined(__APPLE__) + getentropy_works = CRYPTOGRAPHY_OSRANDOM_GETENTROPY_WORKS; +#else + if (__builtin_available(macOS 10.12, *)) { + getentropy_works = CRYPTOGRAPHY_OSRANDOM_GETENTROPY_WORKS; + } else { + getentropy_works = CRYPTOGRAPHY_OSRANDOM_GETENTROPY_FALLBACK; + int fd = dev_urandom_fd(); + if (fd < 0) { + return 0; + } + } +#endif + return 1; +} + +static int osrandom_rand_bytes(unsigned char *buffer, int size) { + int len; + int res; + + switch(getentropy_works) { +#if defined(__APPLE__) + case CRYPTOGRAPHY_OSRANDOM_GETENTROPY_FALLBACK: + return dev_urandom_read(buffer, size); +#endif + case CRYPTOGRAPHY_OSRANDOM_GETENTROPY_WORKS: + while (size > 0) { + /* OpenBSD and macOS restrict maximum buffer size to 256. */ + len = size > 256 ? 256 : size; +/* on mac, availability is already checked using `__builtin_available` above */ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunguarded-availability" + res = getentropy(buffer, (size_t)len); +#pragma clang diagnostic pop + if (res < 0) { + ERR_Cryptography_OSRandom_error( + CRYPTOGRAPHY_OSRANDOM_F_RAND_BYTES, + CRYPTOGRAPHY_OSRANDOM_R_GETENTROPY_FAILED, + __FILE__, __LINE__ + ); + return 0; + } + buffer += len; + size -= len; + } + return 1; + } + __builtin_unreachable(); +} + +static int osrandom_finish(ENGINE *e) { + return 1; +} + +static int osrandom_rand_status(void) { + return 1; +} + +static const char *osurandom_get_implementation(void) { + switch(getentropy_works) { + case CRYPTOGRAPHY_OSRANDOM_GETENTROPY_FALLBACK: + return "/dev/urandom"; + case CRYPTOGRAPHY_OSRANDOM_GETENTROPY_WORKS: + return "getentropy"; + } + __builtin_unreachable(); +} +#endif /* CRYPTOGRAPHY_OSRANDOM_ENGINE_GETENTROPY */ + +/**************************************************************************** + * Linux getrandom engine with fallback to dev_urandom + */ + +#if CRYPTOGRAPHY_OSRANDOM_ENGINE == CRYPTOGRAPHY_OSRANDOM_ENGINE_GETRANDOM +static const char *Cryptography_osrandom_engine_name = "osrandom_engine getrandom()"; + +static int getrandom_works = CRYPTOGRAPHY_OSRANDOM_GETRANDOM_NOT_INIT; + +static int osrandom_init(ENGINE *e) { + /* We try to detect working getrandom until we succeed. */ + if (getrandom_works != CRYPTOGRAPHY_OSRANDOM_GETRANDOM_WORKS) { + long n; + char dest[1]; + /* if the kernel CSPRNG is not initialized this will block */ + n = syscall(SYS_getrandom, dest, sizeof(dest), 0); + if (n == sizeof(dest)) { + getrandom_works = CRYPTOGRAPHY_OSRANDOM_GETRANDOM_WORKS; + } else { + int e = errno; + switch(e) { + case ENOSYS: + /* Fallback: Kernel does not support the syscall. */ + getrandom_works = CRYPTOGRAPHY_OSRANDOM_GETRANDOM_FALLBACK; + break; + case EPERM: + /* Fallback: seccomp prevents syscall */ + getrandom_works = CRYPTOGRAPHY_OSRANDOM_GETRANDOM_FALLBACK; + break; + default: + /* EINTR cannot occur for buflen < 256. */ + ERR_Cryptography_OSRandom_error( + CRYPTOGRAPHY_OSRANDOM_F_INIT, + CRYPTOGRAPHY_OSRANDOM_R_GETRANDOM_INIT_FAILED_UNEXPECTED, + "errno", e + ); + getrandom_works = CRYPTOGRAPHY_OSRANDOM_GETRANDOM_INIT_FAILED; + break; + } + } + } + + /* fallback to dev urandom */ + if (getrandom_works == CRYPTOGRAPHY_OSRANDOM_GETRANDOM_FALLBACK) { + int fd = dev_urandom_fd(); + if (fd < 0) { + return 0; + } + } + return 1; +} + +static int osrandom_rand_bytes(unsigned char *buffer, int size) { + long n; + + switch(getrandom_works) { + case CRYPTOGRAPHY_OSRANDOM_GETRANDOM_INIT_FAILED: + ERR_Cryptography_OSRandom_error( + CRYPTOGRAPHY_OSRANDOM_F_RAND_BYTES, + CRYPTOGRAPHY_OSRANDOM_R_GETRANDOM_INIT_FAILED, + __FILE__, __LINE__ + ); + return 0; + case CRYPTOGRAPHY_OSRANDOM_GETRANDOM_NOT_INIT: + ERR_Cryptography_OSRandom_error( + CRYPTOGRAPHY_OSRANDOM_F_RAND_BYTES, + CRYPTOGRAPHY_OSRANDOM_R_GETRANDOM_NOT_INIT, + __FILE__, __LINE__ + ); + return 0; + case CRYPTOGRAPHY_OSRANDOM_GETRANDOM_FALLBACK: + return dev_urandom_read(buffer, size); + case CRYPTOGRAPHY_OSRANDOM_GETRANDOM_WORKS: + while (size > 0) { + do { + n = syscall(SYS_getrandom, buffer, size, 0); + } while (n < 0 && errno == EINTR); + + if (n <= 0) { + ERR_Cryptography_OSRandom_error( + CRYPTOGRAPHY_OSRANDOM_F_RAND_BYTES, + CRYPTOGRAPHY_OSRANDOM_R_GETRANDOM_FAILED, + __FILE__, __LINE__ + ); + return 0; + } + buffer += n; + size -= (int)n; + } + return 1; + } + __builtin_unreachable(); +} + +static int osrandom_finish(ENGINE *e) { + dev_urandom_close(); + return 1; +} + +static int osrandom_rand_status(void) { + switch(getrandom_works) { + case CRYPTOGRAPHY_OSRANDOM_GETRANDOM_INIT_FAILED: + return 0; + case CRYPTOGRAPHY_OSRANDOM_GETRANDOM_NOT_INIT: + return 0; + case CRYPTOGRAPHY_OSRANDOM_GETRANDOM_FALLBACK: + return urandom_cache.fd >= 0; + case CRYPTOGRAPHY_OSRANDOM_GETRANDOM_WORKS: + return 1; + } + __builtin_unreachable(); +} + +static const char *osurandom_get_implementation(void) { + switch(getrandom_works) { + case CRYPTOGRAPHY_OSRANDOM_GETRANDOM_INIT_FAILED: + return "<failed>"; + case CRYPTOGRAPHY_OSRANDOM_GETRANDOM_NOT_INIT: + return "<not initialized>"; + case CRYPTOGRAPHY_OSRANDOM_GETRANDOM_FALLBACK: + return "/dev/urandom"; + case CRYPTOGRAPHY_OSRANDOM_GETRANDOM_WORKS: + return "getrandom"; + } + __builtin_unreachable(); +} +#endif /* CRYPTOGRAPHY_OSRANDOM_ENGINE_GETRANDOM */ + +/**************************************************************************** + * dev_urandom engine for all remaining platforms + */ + +#if CRYPTOGRAPHY_OSRANDOM_ENGINE == CRYPTOGRAPHY_OSRANDOM_ENGINE_DEV_URANDOM +static const char *Cryptography_osrandom_engine_name = "osrandom_engine /dev/urandom"; + +static int osrandom_init(ENGINE *e) { + int fd = dev_urandom_fd(); + if (fd < 0) { + return 0; + } + return 1; +} + +static int osrandom_rand_bytes(unsigned char *buffer, int size) { + return dev_urandom_read(buffer, size); +} + +static int osrandom_finish(ENGINE *e) { + dev_urandom_close(); + return 1; +} + +static int osrandom_rand_status(void) { + return urandom_cache.fd >= 0; +} + +static const char *osurandom_get_implementation(void) { + return "/dev/urandom"; +} +#endif /* CRYPTOGRAPHY_OSRANDOM_ENGINE_DEV_URANDOM */ + +/**************************************************************************** + * ENGINE boiler plate + */ + +/* This replicates the behavior of the OpenSSL FIPS RNG, which returns a + -1 in the event that there is an error when calling RAND_pseudo_bytes. */ +static int osrandom_pseudo_rand_bytes(unsigned char *buffer, int size) { + int res = osrandom_rand_bytes(buffer, size); + if (res == 0) { + return -1; + } else { + return res; + } +} + +static RAND_METHOD osrandom_rand = { + NULL, + osrandom_rand_bytes, + NULL, + NULL, + osrandom_pseudo_rand_bytes, + osrandom_rand_status, +}; + +static const ENGINE_CMD_DEFN osrandom_cmd_defns[] = { + {CRYPTOGRAPHY_OSRANDOM_GET_IMPLEMENTATION, + "get_implementation", + "Get CPRNG implementation.", + ENGINE_CMD_FLAG_NO_INPUT}, + {0, NULL, NULL, 0} +}; + +static int osrandom_ctrl(ENGINE *e, int cmd, long i, void *p, void (*f) (void)) { + const char *name; + size_t len; + + switch (cmd) { + case CRYPTOGRAPHY_OSRANDOM_GET_IMPLEMENTATION: + /* i: buffer size, p: char* buffer */ + name = osurandom_get_implementation(); + len = strlen(name); + if ((p == NULL) && (i == 0)) { + /* return required buffer len */ + return (int)len; + } + if ((p == NULL) || i < 0 || ((size_t)i <= len)) { + /* no buffer or buffer too small */ + ENGINEerr(ENGINE_F_ENGINE_CTRL, ENGINE_R_INVALID_ARGUMENT); + return 0; + } + strncpy((char *)p, name, len); + return (int)len; + default: + ENGINEerr(ENGINE_F_ENGINE_CTRL, ENGINE_R_CTRL_COMMAND_NOT_IMPLEMENTED); + return 0; + } +} + +/* error reporting */ +#define ERR_FUNC(func) ERR_PACK(0, func, 0) +#define ERR_REASON(reason) ERR_PACK(0, 0, reason) + +static ERR_STRING_DATA CRYPTOGRAPHY_OSRANDOM_lib_name[] = { + {0, "osrandom_engine"}, + {0, NULL} +}; + +static ERR_STRING_DATA CRYPTOGRAPHY_OSRANDOM_str_funcs[] = { + {ERR_FUNC(CRYPTOGRAPHY_OSRANDOM_F_INIT), + "osrandom_init"}, + {ERR_FUNC(CRYPTOGRAPHY_OSRANDOM_F_RAND_BYTES), + "osrandom_rand_bytes"}, + {ERR_FUNC(CRYPTOGRAPHY_OSRANDOM_F_FINISH), + "osrandom_finish"}, + {ERR_FUNC(CRYPTOGRAPHY_OSRANDOM_F_DEV_URANDOM_FD), + "dev_urandom_fd"}, + {ERR_FUNC(CRYPTOGRAPHY_OSRANDOM_F_DEV_URANDOM_READ), + "dev_urandom_read"}, + {0, NULL} +}; + +static ERR_STRING_DATA CRYPTOGRAPHY_OSRANDOM_str_reasons[] = { + {ERR_REASON(CRYPTOGRAPHY_OSRANDOM_R_CRYPTACQUIRECONTEXT), + "CryptAcquireContext() failed."}, + {ERR_REASON(CRYPTOGRAPHY_OSRANDOM_R_CRYPTGENRANDOM), + "CryptGenRandom() failed."}, + {ERR_REASON(CRYPTOGRAPHY_OSRANDOM_R_CRYPTRELEASECONTEXT), + "CryptReleaseContext() failed."}, + {ERR_REASON(CRYPTOGRAPHY_OSRANDOM_R_GETENTROPY_FAILED), + "getentropy() failed"}, + {ERR_REASON(CRYPTOGRAPHY_OSRANDOM_R_DEV_URANDOM_OPEN_FAILED), + "open('/dev/urandom') failed."}, + {ERR_REASON(CRYPTOGRAPHY_OSRANDOM_R_DEV_URANDOM_READ_FAILED), + "Reading from /dev/urandom fd failed."}, + {ERR_REASON(CRYPTOGRAPHY_OSRANDOM_R_GETRANDOM_INIT_FAILED), + "getrandom() initialization failed."}, + {ERR_REASON(CRYPTOGRAPHY_OSRANDOM_R_GETRANDOM_INIT_FAILED_UNEXPECTED), + "getrandom() initialization failed with unexpected errno."}, + {ERR_REASON(CRYPTOGRAPHY_OSRANDOM_R_GETRANDOM_FAILED), + "getrandom() syscall failed."}, + {ERR_REASON(CRYPTOGRAPHY_OSRANDOM_R_GETRANDOM_NOT_INIT), + "getrandom() engine was not properly initialized."}, + {0, NULL} +}; + +static int Cryptography_OSRandom_lib_error_code = 0; + +static void ERR_load_Cryptography_OSRandom_strings(void) +{ + if (Cryptography_OSRandom_lib_error_code == 0) { + Cryptography_OSRandom_lib_error_code = ERR_get_next_error_library(); + ERR_load_strings(Cryptography_OSRandom_lib_error_code, + CRYPTOGRAPHY_OSRANDOM_lib_name); + ERR_load_strings(Cryptography_OSRandom_lib_error_code, + CRYPTOGRAPHY_OSRANDOM_str_funcs); + ERR_load_strings(Cryptography_OSRandom_lib_error_code, + CRYPTOGRAPHY_OSRANDOM_str_reasons); + } +} + +static void ERR_Cryptography_OSRandom_error(int function, int reason, + char *file, int line) +{ + ERR_PUT_error(Cryptography_OSRandom_lib_error_code, function, reason, + file, line); +} + +/* Returns 1 if successfully added, 2 if engine has previously been added, + and 0 for error. */ +int Cryptography_add_osrandom_engine(void) { + ENGINE *e; + + ERR_load_Cryptography_OSRandom_strings(); + + e = ENGINE_by_id(Cryptography_osrandom_engine_id); + if (e != NULL) { + ENGINE_free(e); + return 2; + } else { + ERR_clear_error(); + } + + e = ENGINE_new(); + if (e == NULL) { + return 0; + } + if (!ENGINE_set_id(e, Cryptography_osrandom_engine_id) || + !ENGINE_set_name(e, Cryptography_osrandom_engine_name) || + !ENGINE_set_RAND(e, &osrandom_rand) || + !ENGINE_set_init_function(e, osrandom_init) || + !ENGINE_set_finish_function(e, osrandom_finish) || + !ENGINE_set_cmd_defns(e, osrandom_cmd_defns) || + !ENGINE_set_ctrl_function(e, osrandom_ctrl)) { + ENGINE_free(e); + return 0; + } + if (!ENGINE_add(e)) { + ENGINE_free(e); + return 0; + } + if (!ENGINE_free(e)) { + return 0; + } + + return 1; +} + +#else +/* If OpenSSL has no ENGINE support then we don't want + * to compile the osrandom engine, but we do need some + * placeholders */ +static const char *Cryptography_osrandom_engine_id = "no-engine-support"; +static const char *Cryptography_osrandom_engine_name = "osrandom_engine disabled due to no engine support"; + +int Cryptography_add_osrandom_engine(void) { + return 0; +} + +#endif diff --git a/src/_cffi_src/openssl/src/osrandom_engine.h b/src/_cffi_src/openssl/src/osrandom_engine.h new file mode 100644 index 00000000..cf394f22 --- /dev/null +++ b/src/_cffi_src/openssl/src/osrandom_engine.h @@ -0,0 +1,114 @@ +#ifndef OPENSSL_NO_ENGINE +/* OpenSSL has ENGINE support so include all of this. */ +#ifdef _WIN32 + #include <Wincrypt.h> +#else + #include <fcntl.h> + #include <unistd.h> + /* for defined(BSD) */ + #include <sys/param.h> + + #ifdef BSD + /* for SYS_getentropy */ + #include <sys/syscall.h> + #endif + + #ifdef __APPLE__ + #include <sys/random.h> + /* To support weak linking we need to declare this as a weak import even if + * it's not present in sys/random (e.g. macOS < 10.12). */ + extern int getentropy(void *buffer, size_t size) __attribute((weak_import)); + #endif + + #ifdef __linux__ + /* for SYS_getrandom */ + #include <sys/syscall.h> + #ifndef GRND_NONBLOCK + #define GRND_NONBLOCK 0x0001 + #endif /* GRND_NONBLOCK */ + + #ifndef SYS_getrandom + /* We only bother to define the constants for platforms where we ship + * wheels, since that's the predominant way you get a situation where + * you don't have SYS_getrandom at compile time but do have the syscall + * at runtime */ + #if defined __x86_64__ + #define SYS_getrandom 318 + #elif defined(__i386__) + #define SYS_getrandom 355 + #endif + #endif + #endif /* __linux__ */ +#endif /* _WIN32 */ + +#define CRYPTOGRAPHY_OSRANDOM_ENGINE_CRYPTGENRANDOM 1 +#define CRYPTOGRAPHY_OSRANDOM_ENGINE_GETENTROPY 2 +#define CRYPTOGRAPHY_OSRANDOM_ENGINE_GETRANDOM 3 +#define CRYPTOGRAPHY_OSRANDOM_ENGINE_DEV_URANDOM 4 + +#ifndef CRYPTOGRAPHY_OSRANDOM_ENGINE + #if defined(_WIN32) + /* Windows */ + #define CRYPTOGRAPHY_OSRANDOM_ENGINE CRYPTOGRAPHY_OSRANDOM_ENGINE_CRYPTGENRANDOM + #elif defined(BSD) && defined(SYS_getentropy) + /* OpenBSD 5.6+ & macOS with SYS_getentropy defined, although < 10.12 will fallback + * to urandom */ + #define CRYPTOGRAPHY_OSRANDOM_ENGINE CRYPTOGRAPHY_OSRANDOM_ENGINE_GETENTROPY + #elif defined(__linux__) && defined(SYS_getrandom) + /* Linux 3.17+ */ + #define CRYPTOGRAPHY_OSRANDOM_ENGINE CRYPTOGRAPHY_OSRANDOM_ENGINE_GETRANDOM + #else + /* Keep this as last entry, fall back to /dev/urandom */ + #define CRYPTOGRAPHY_OSRANDOM_ENGINE CRYPTOGRAPHY_OSRANDOM_ENGINE_DEV_URANDOM + #endif +#endif /* CRYPTOGRAPHY_OSRANDOM_ENGINE */ + +/* Fallbacks need /dev/urandom helper functions. */ +#if CRYPTOGRAPHY_OSRANDOM_ENGINE == CRYPTOGRAPHY_OSRANDOM_ENGINE_GETRANDOM || \ + CRYPTOGRAPHY_OSRANDOM_ENGINE == CRYPTOGRAPHY_OSRANDOM_ENGINE_DEV_URANDOM || \ + (CRYPTOGRAPHY_OSRANDOM_ENGINE == CRYPTOGRAPHY_OSRANDOM_ENGINE_GETENTROPY && \ + defined(__APPLE__)) + #define CRYPTOGRAPHY_OSRANDOM_NEEDS_DEV_URANDOM 1 +#endif + +enum { + CRYPTOGRAPHY_OSRANDOM_GETRANDOM_INIT_FAILED = -2, + CRYPTOGRAPHY_OSRANDOM_GETRANDOM_NOT_INIT, + CRYPTOGRAPHY_OSRANDOM_GETRANDOM_FALLBACK, + CRYPTOGRAPHY_OSRANDOM_GETRANDOM_WORKS +}; + +enum { + CRYPTOGRAPHY_OSRANDOM_GETENTROPY_NOT_INIT, + CRYPTOGRAPHY_OSRANDOM_GETENTROPY_FALLBACK, + CRYPTOGRAPHY_OSRANDOM_GETENTROPY_WORKS +}; + +/* engine ctrl */ +#define CRYPTOGRAPHY_OSRANDOM_GET_IMPLEMENTATION ENGINE_CMD_BASE + +/* error reporting */ +static void ERR_load_Cryptography_OSRandom_strings(void); +static void ERR_Cryptography_OSRandom_error(int function, int reason, + char *file, int line); + +#define CRYPTOGRAPHY_OSRANDOM_F_INIT 100 +#define CRYPTOGRAPHY_OSRANDOM_F_RAND_BYTES 101 +#define CRYPTOGRAPHY_OSRANDOM_F_FINISH 102 +#define CRYPTOGRAPHY_OSRANDOM_F_DEV_URANDOM_FD 300 +#define CRYPTOGRAPHY_OSRANDOM_F_DEV_URANDOM_READ 301 + +#define CRYPTOGRAPHY_OSRANDOM_R_CRYPTACQUIRECONTEXT 100 +#define CRYPTOGRAPHY_OSRANDOM_R_CRYPTGENRANDOM 101 +#define CRYPTOGRAPHY_OSRANDOM_R_CRYPTRELEASECONTEXT 102 + +#define CRYPTOGRAPHY_OSRANDOM_R_GETENTROPY_FAILED 200 + +#define CRYPTOGRAPHY_OSRANDOM_R_DEV_URANDOM_OPEN_FAILED 300 +#define CRYPTOGRAPHY_OSRANDOM_R_DEV_URANDOM_READ_FAILED 301 + +#define CRYPTOGRAPHY_OSRANDOM_R_GETRANDOM_INIT_FAILED 400 +#define CRYPTOGRAPHY_OSRANDOM_R_GETRANDOM_INIT_FAILED_UNEXPECTED 402 +#define CRYPTOGRAPHY_OSRANDOM_R_GETRANDOM_FAILED 403 +#define CRYPTOGRAPHY_OSRANDOM_R_GETRANDOM_NOT_INIT 404 +#endif diff --git a/src/_cffi_src/openssl/ssl.py b/src/_cffi_src/openssl/ssl.py index fa0aefc8..4ba86693 100644 --- a/src/_cffi_src/openssl/ssl.py +++ b/src/_cffi_src/openssl/ssl.py @@ -11,20 +11,26 @@ typedef STACK_OF(SSL_CIPHER) Cryptography_STACK_OF_SSL_CIPHER; """ TYPES = """ -/* - * Internally invented symbols to tell which versions of SSL/TLS are supported. -*/ +static const long Cryptography_HAS_SSL_ST; +static const long Cryptography_HAS_TLS_ST; static const long Cryptography_HAS_SSL2; static const long Cryptography_HAS_SSL3_METHOD; static const long Cryptography_HAS_TLSv1_1; static const long Cryptography_HAS_TLSv1_2; +static const long Cryptography_HAS_TLSv1_3; static const long Cryptography_HAS_SECURE_RENEGOTIATION; static const long Cryptography_HAS_COMPRESSION; static const long Cryptography_HAS_TLSEXT_STATUS_REQ_CB; static const long Cryptography_HAS_STATUS_REQ_OCSP_RESP; static const long Cryptography_HAS_TLSEXT_STATUS_REQ_TYPE; -static const long Cryptography_HAS_GET_SERVER_TMP_KEY; static const long Cryptography_HAS_SSL_CTX_SET_CLIENT_CERT_ENGINE; +static const long Cryptography_HAS_SSL_CTX_CLEAR_OPTIONS; +static const long Cryptography_HAS_DTLS; +static const long Cryptography_HAS_SIGALGS; +static const long Cryptography_HAS_PSK; +static const long Cryptography_HAS_CIPHER_DETAILS; +static const long Cryptography_HAS_VERIFIED_CHAIN; +static const long Cryptography_HAS_KEYLOG; /* Internally invented symbol to tell us if SNI is supported */ static const long Cryptography_HAS_TLSEXT_HOSTNAME; @@ -38,13 +44,13 @@ static const long Cryptography_HAS_RELEASE_BUFFERS; * supported */ static const long Cryptography_HAS_OP_NO_COMPRESSION; - static const long Cryptography_HAS_SSL_OP_MSIE_SSLV2_RSA_PADDING; static const long Cryptography_HAS_SSL_SET_SSL_CTX; static const long Cryptography_HAS_SSL_OP_NO_TICKET; -static const long Cryptography_HAS_NETBSD_D1_METH; -static const long Cryptography_HAS_NEXTPROTONEG; static const long Cryptography_HAS_ALPN; +static const long Cryptography_HAS_NEXTPROTONEG; +static const long Cryptography_HAS_SET_CERT_CB; +static const long Cryptography_HAS_CUSTOM_EXT; static const long SSL_FILETYPE_PEM; static const long SSL_FILETYPE_ASN1; @@ -53,6 +59,7 @@ static const long SSL_ERROR_ZERO_RETURN; static const long SSL_ERROR_WANT_READ; static const long SSL_ERROR_WANT_WRITE; static const long SSL_ERROR_WANT_X509_LOOKUP; +static const long SSL_ERROR_WANT_CONNECT; static const long SSL_ERROR_SYSCALL; static const long SSL_ERROR_SSL; static const long SSL_SENT_SHUTDOWN; @@ -62,6 +69,9 @@ static const long SSL_OP_NO_SSLv3; static const long SSL_OP_NO_TLSv1; static const long SSL_OP_NO_TLSv1_1; static const long SSL_OP_NO_TLSv1_2; +static const long SSL_OP_NO_TLSv1_3; +static const long SSL_OP_NO_DTLSv1; +static const long SSL_OP_NO_DTLSv1_2; static const long SSL_OP_NO_COMPRESSION; static const long SSL_OP_SINGLE_DH_USE; static const long SSL_OP_EPHEMERAL_RSA; @@ -92,6 +102,7 @@ static const long SSL_VERIFY_PEER; static const long SSL_VERIFY_FAIL_IF_NO_PEER_CERT; static const long SSL_VERIFY_CLIENT_ONCE; static const long SSL_VERIFY_NONE; +static const long SSL_VERIFY_POST_HANDSHAKE; static const long SSL_SESS_CACHE_OFF; static const long SSL_SESS_CACHE_CLIENT; static const long SSL_SESS_CACHE_SERVER; @@ -125,41 +136,32 @@ static const long SSL_MODE_ENABLE_PARTIAL_WRITE; static const long SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER; static const long SSL_MODE_AUTO_RETRY; static const long SSL3_RANDOM_SIZE; +static const long TLS_ST_BEFORE; +static const long TLS_ST_OK; + +static const long OPENSSL_NPN_NEGOTIATED; typedef ... SSL_METHOD; typedef ... SSL_CTX; -typedef struct { - int master_key_length; - unsigned char master_key[...]; - ...; -} SSL_SESSION; - -typedef struct { - unsigned char server_random[...]; - unsigned char client_random[...]; - ...; -} SSL3_STATE; +typedef ... SSL_SESSION; -typedef struct { - int version; - int type; - SSL3_STATE *s3; - SSL_SESSION *session; - ...; -} SSL; +typedef ... SSL; static const long TLSEXT_NAMETYPE_host_name; +static const long TLSEXT_STATUSTYPE_ocsp; typedef ... SSL_CIPHER; typedef ... Cryptography_STACK_OF_SSL_CIPHER; typedef ... COMP_METHOD; + +typedef struct { + const char *name; + unsigned long id; +} SRTP_PROTECTION_PROFILE; """ FUNCTIONS = """ -void SSL_load_error_strings(void); -int SSL_library_init(void); - /* SSL */ const char *SSL_state_string_long(const SSL *); SSL_SESSION *SSL_get1_session(SSL *); @@ -174,6 +176,10 @@ void (*SSL_get_info_callback(const SSL *))(const SSL *, int, int); SSL *SSL_new(SSL_CTX *); void SSL_free(SSL *); int SSL_set_fd(SSL *, int); +SSL_CTX *SSL_get_SSL_CTX(const SSL *); +SSL_CTX *SSL_set_SSL_CTX(SSL *, SSL_CTX *); +BIO *SSL_get_rbio(const SSL *); +BIO *SSL_get_wbio(const SSL *); void SSL_set_bio(SSL *, BIO *, BIO *); void SSL_set_connect_state(SSL *); void SSL_set_accept_state(SSL *); @@ -182,9 +188,15 @@ int SSL_get_shutdown(const SSL *); int SSL_pending(const SSL *); int SSL_write(SSL *, const void *, int); int SSL_read(SSL *, void *, int); +int SSL_peek(SSL *, void *, int); +X509 *SSL_get_certificate(const SSL *); X509 *SSL_get_peer_certificate(const SSL *); int SSL_get_ex_data_X509_STORE_CTX_idx(void); +/* Added in 1.0.2 */ +X509_VERIFY_PARAM *SSL_get0_param(SSL *); +X509_VERIFY_PARAM *SSL_CTX_get0_param(SSL_CTX *); + int SSL_use_certificate(SSL *, X509 *); int SSL_use_certificate_ASN1(SSL *, const unsigned char *, int); int SSL_use_certificate_file(SSL *, const char *, int); @@ -193,12 +205,19 @@ int SSL_use_PrivateKey_ASN1(int, SSL *, const unsigned char *, long); int SSL_use_PrivateKey_file(SSL *, const char *, int); int SSL_check_private_key(const SSL *); +int SSL_get_sigalgs(SSL *, int, int *, int *, int *, unsigned char *, + unsigned char *); + Cryptography_STACK_OF_X509 *SSL_get_peer_cert_chain(const SSL *); +Cryptography_STACK_OF_X509 *SSL_get0_verified_chain(const SSL *); Cryptography_STACK_OF_X509_NAME *SSL_get_client_CA_list(const SSL *); int SSL_get_error(const SSL *, int); +long SSL_get_verify_result(const SSL *ssl); int SSL_do_handshake(SSL *); int SSL_shutdown(SSL *); +int SSL_renegotiate(SSL *); +int SSL_renegotiate_pending(SSL *); const char *SSL_get_cipher_list(const SSL *, int); Cryptography_STACK_OF_SSL_CIPHER *SSL_get_ciphers(const SSL *); @@ -224,40 +243,116 @@ int SSL_CTX_use_PrivateKey_ASN1(int, SSL_CTX *, const unsigned char *, long); int SSL_CTX_use_PrivateKey_file(SSL_CTX *, const char *, int); int SSL_CTX_check_private_key(const SSL_CTX *); void SSL_CTX_set_cert_verify_callback(SSL_CTX *, - int (*)(X509_STORE_CTX *,void *), + int (*)(X509_STORE_CTX *, void *), void *); +void SSL_CTX_set_cookie_generate_cb(SSL_CTX *, + int (*)( + SSL *, + unsigned char *, + unsigned int * + )); +long SSL_CTX_get_read_ahead(SSL_CTX *); +long SSL_CTX_set_read_ahead(SSL_CTX *, long); + +int SSL_CTX_use_psk_identity_hint(SSL_CTX *, const char *); +void SSL_CTX_set_psk_server_callback(SSL_CTX *, + unsigned int (*)( + SSL *, + const char *, + unsigned char *, + unsigned int + )); +void SSL_CTX_set_psk_client_callback(SSL_CTX *, + unsigned int (*)( + SSL *, + const char *, + char *, + unsigned int, + unsigned char *, + unsigned int + )); + +int SSL_CTX_set_session_id_context(SSL_CTX *, const unsigned char *, + unsigned int); + void SSL_CTX_set_cert_store(SSL_CTX *, X509_STORE *); X509_STORE *SSL_CTX_get_cert_store(const SSL_CTX *); int SSL_CTX_add_client_CA(SSL_CTX *, X509 *); void SSL_CTX_set_client_CA_list(SSL_CTX *, Cryptography_STACK_OF_X509_NAME *); +void SSL_CTX_set_info_callback(SSL_CTX *, void (*)(const SSL *, int, int)); +void (*SSL_CTX_get_info_callback(SSL_CTX *))(const SSL *, int, int); + +void SSL_CTX_set_keylog_callback(SSL_CTX *, + void (*)(const SSL *, const char *)); +void (*SSL_CTX_get_keylog_callback(SSL_CTX *))(const SSL *, const char *); + +long SSL_CTX_set1_sigalgs_list(SSL_CTX *, const char *); + /* SSL_SESSION */ void SSL_SESSION_free(SSL_SESSION *); /* Information about actually used cipher */ const char *SSL_CIPHER_get_name(const SSL_CIPHER *); int SSL_CIPHER_get_bits(const SSL_CIPHER *, int *); -char *SSL_CIPHER_get_version(const SSL_CIPHER *); +/* the modern signature of this is uint32_t, but older openssl declared it + as unsigned long. To make our compiler flags happy we'll declare it as a + 64-bit wide value, which should always be safe */ +uint64_t SSL_CIPHER_get_id(const SSL_CIPHER *); +int SSL_CIPHER_is_aead(const SSL_CIPHER *); +int SSL_CIPHER_get_cipher_nid(const SSL_CIPHER *); +int SSL_CIPHER_get_digest_nid(const SSL_CIPHER *); +int SSL_CIPHER_get_kx_nid(const SSL_CIPHER *); +int SSL_CIPHER_get_auth_nid(const SSL_CIPHER *); size_t SSL_get_finished(const SSL *, void *, size_t); size_t SSL_get_peer_finished(const SSL *, void *, size_t); -""" +Cryptography_STACK_OF_X509_NAME *SSL_load_client_CA_file(const char *); + +const char *SSL_get_servername(const SSL *, const int); +/* Function signature changed to const char * in 1.1.0 */ +const char *SSL_CIPHER_get_version(const SSL_CIPHER *); +/* These became macros in 1.1.0 */ +int SSL_library_init(void); +void SSL_load_error_strings(void); + +/* these CRYPTO_EX_DATA functions became macros in 1.1.0 */ +int SSL_get_ex_new_index(long, void *, CRYPTO_EX_new *, CRYPTO_EX_dup *, + CRYPTO_EX_free *); +int SSL_set_ex_data(SSL *, int, void *); +int SSL_CTX_get_ex_new_index(long, void *, CRYPTO_EX_new *, CRYPTO_EX_dup *, + CRYPTO_EX_free *); +int SSL_CTX_set_ex_data(SSL_CTX *, int, void *); + +SSL_SESSION *SSL_get_session(const SSL *); +const unsigned char *SSL_SESSION_get_id(const SSL_SESSION *, unsigned int *); +long SSL_SESSION_get_time(const SSL_SESSION *); +long SSL_SESSION_get_timeout(const SSL_SESSION *); +int SSL_SESSION_has_ticket(const SSL_SESSION *); +long SSL_SESSION_get_ticket_lifetime_hint(const SSL_SESSION *); + +/* not a macro, but older OpenSSLs don't pass the args as const */ +char *SSL_CIPHER_description(const SSL_CIPHER *, char *, int); +int SSL_SESSION_print(BIO *, const SSL_SESSION *); -MACROS = """ /* not macros, but will be conditionally bound so can't live in functions */ const COMP_METHOD *SSL_get_current_compression(SSL *); const COMP_METHOD *SSL_get_current_expansion(SSL *); const char *SSL_COMP_get_name(const COMP_METHOD *); -int SSL_CTX_set_client_cert_engine(SSL_CTX *, ENGINE *); unsigned long SSL_set_mode(SSL *, unsigned long); +unsigned long SSL_clear_mode(SSL *, unsigned long); unsigned long SSL_get_mode(SSL *); unsigned long SSL_set_options(SSL *, unsigned long); unsigned long SSL_get_options(SSL *); +void SSL_set_app_data(SSL *, char *); +char * SSL_get_app_data(SSL *); +void SSL_set_read_ahead(SSL *, int); + int SSL_want_read(const SSL *); int SSL_want_write(const SSL *); @@ -267,8 +362,10 @@ long SSL_get_secure_renegotiation_support(SSL *); /* Defined as unsigned long because SSL_OP_ALL is greater than signed 32-bit and Windows defines long as 32-bit. */ unsigned long SSL_CTX_set_options(SSL_CTX *, unsigned long); +unsigned long SSL_CTX_clear_options(SSL_CTX *, unsigned long); unsigned long SSL_CTX_get_options(SSL_CTX *); unsigned long SSL_CTX_set_mode(SSL_CTX *, unsigned long); +unsigned long SSL_CTX_clear_mode(SSL_CTX *, unsigned long); unsigned long SSL_CTX_get_mode(SSL_CTX *); unsigned long SSL_CTX_set_session_cache_mode(SSL_CTX *, unsigned long); unsigned long SSL_CTX_get_session_cache_mode(SSL_CTX *); @@ -280,15 +377,6 @@ unsigned long SSL_CTX_add_extra_chain_cert(SSL_CTX *, X509 *); /* methods */ -/* SSLv2 support is compiled out of some versions of OpenSSL. These will - * get special support when we generate the bindings so that if they are - * available they will be wrapped, but if they are not they won't cause - * problems (like link errors). - */ -const SSL_METHOD *SSLv2_method(void); -const SSL_METHOD *SSLv2_server_method(void); -const SSL_METHOD *SSLv2_client_method(void); - /* * TLSv1_1 and TLSv1_2 are recent additions. Only sufficiently new versions of * OpenSSL support them. @@ -313,6 +401,11 @@ const SSL_METHOD *DTLSv1_method(void); const SSL_METHOD *DTLSv1_server_method(void); const SSL_METHOD *DTLSv1_client_method(void); +/* Added in 1.0.2 */ +const SSL_METHOD *DTLS_method(void); +const SSL_METHOD *DTLS_server_method(void); +const SSL_METHOD *DTLS_client_method(void); + const SSL_METHOD *SSLv23_method(void); const SSL_METHOD *SSLv23_server_method(void); const SSL_METHOD *SSLv23_client_method(void); @@ -325,39 +418,28 @@ const SSL_CIPHER *SSL_get_current_cipher(const SSL *); const char *SSL_get_version(const SSL *); int SSL_version(const SSL *); -/* SNI APIs were introduced in OpenSSL 1.0.0. To continue to support - * earlier versions some special handling of these is necessary. - */ -const char *SSL_get_servername(const SSL *, const int); +void *SSL_CTX_get_ex_data(const SSL_CTX *, int); +void *SSL_get_ex_data(const SSL *, int); + void SSL_set_tlsext_host_name(SSL *, char *); void SSL_CTX_set_tlsext_servername_callback( SSL_CTX *, - int (*)(const SSL *, int *, void *)); + int (*)(SSL *, int *, void *)); +void SSL_CTX_set_tlsext_servername_arg( + SSL_CTX *, void *); -/* These were added in OpenSSL 0.9.8h, but since version testing in OpenSSL - is fraught with peril thanks to OS distributions we check some constants - to determine if they are supported or not */ long SSL_set_tlsext_status_ocsp_resp(SSL *, unsigned char *, int); long SSL_get_tlsext_status_ocsp_resp(SSL *, const unsigned char **); long SSL_set_tlsext_status_type(SSL *, long); long SSL_CTX_set_tlsext_status_cb(SSL_CTX *, int(*)(SSL *, void *)); long SSL_CTX_set_tlsext_status_arg(SSL_CTX *, void *); -long SSL_session_reused(SSL *); - -/* The following were macros in 0.9.8e. Once we drop support for RHEL/CentOS 5 - we should move these back to FUNCTIONS. */ -void SSL_CTX_set_info_callback(SSL_CTX *, void (*)(const SSL *, int, int)); -void (*SSL_CTX_get_info_callback(SSL_CTX *))(const SSL *, int, int); -/* This function does not exist in 0.9.8e. Once we drop support for - RHEL/CentOS 5 this can be moved back to FUNCTIONS. */ -SSL_CTX *SSL_set_SSL_CTX(SSL *, SSL_CTX *); +int SSL_CTX_set_tlsext_use_srtp(SSL_CTX *, const char *); +int SSL_set_tlsext_use_srtp(SSL *, const char *); +SRTP_PROTECTION_PROFILE *SSL_get_selected_srtp_profile(SSL *); -const SSL_METHOD *Cryptography_SSL_CTX_get_method(const SSL_CTX *); +long SSL_session_reused(SSL *); -/* NPN APIs were introduced in OpenSSL 1.0.1. To continue to support earlier - * versions some special handling of these is necessary. - */ void SSL_CTX_set_next_protos_advertised_cb(SSL_CTX *, int (*)(SSL *, const unsigned char **, @@ -379,7 +461,7 @@ void SSL_get0_next_proto_negotiated(const SSL *, const unsigned char **, unsigned *); int sk_SSL_CIPHER_num(Cryptography_STACK_OF_SSL_CIPHER *); -SSL_CIPHER *sk_SSL_CIPHER_value(Cryptography_STACK_OF_SSL_CIPHER *, int); +const SSL_CIPHER *sk_SSL_CIPHER_value(Cryptography_STACK_OF_SSL_CIPHER *, int); /* ALPN APIs were introduced in OpenSSL 1.0.2. To continue to support earlier * versions some special handling of these is necessary. @@ -397,29 +479,172 @@ void SSL_CTX_set_alpn_select_cb(SSL_CTX *, void SSL_get0_alpn_selected(const SSL *, const unsigned char **, unsigned *); long SSL_get_server_tmp_key(SSL *, EVP_PKEY **); + +/* SSL_CTX_set_cert_cb is introduced in OpenSSL 1.0.2. To continue to support + * earlier versions some special handling of these is necessary. + */ +void SSL_CTX_set_cert_cb(SSL_CTX *, int (*)(SSL *, void *), void *); +void SSL_set_cert_cb(SSL *, int (*)(SSL *, void *), void *); + +/* Added in 1.0.2 */ +const SSL_METHOD *SSL_CTX_get_ssl_method(SSL_CTX *); + +int SSL_SESSION_set1_id_context(SSL_SESSION *, const unsigned char *, + unsigned int); +/* Added in 1.1.0 for the great opaquing of structs */ +size_t SSL_SESSION_get_master_key(const SSL_SESSION *, unsigned char *, + size_t); +size_t SSL_get_client_random(const SSL *, unsigned char *, size_t); +size_t SSL_get_server_random(const SSL *, unsigned char *, size_t); +int SSL_export_keying_material(SSL *, unsigned char *, size_t, const char *, + size_t, const unsigned char *, size_t, int); + +long SSL_CTX_sess_number(SSL_CTX *); +long SSL_CTX_sess_connect(SSL_CTX *); +long SSL_CTX_sess_connect_good(SSL_CTX *); +long SSL_CTX_sess_connect_renegotiate(SSL_CTX *); +long SSL_CTX_sess_accept(SSL_CTX *); +long SSL_CTX_sess_accept_good(SSL_CTX *); +long SSL_CTX_sess_accept_renegotiate(SSL_CTX *); +long SSL_CTX_sess_hits(SSL_CTX *); +long SSL_CTX_sess_cb_hits(SSL_CTX *); +long SSL_CTX_sess_misses(SSL_CTX *); +long SSL_CTX_sess_timeouts(SSL_CTX *); +long SSL_CTX_sess_cache_full(SSL_CTX *); + +/* DTLS support */ +long Cryptography_DTLSv1_get_timeout(SSL *, time_t *, long *); +long DTLSv1_handle_timeout(SSL *); +long DTLS_set_link_mtu(SSL *, long); +long DTLS_get_link_min_mtu(SSL *); + +/* Custom extensions. */ +typedef int (*custom_ext_add_cb)(SSL *, unsigned int, + const unsigned char **, + size_t *, int *, + void *); + +typedef void (*custom_ext_free_cb)(SSL *, unsigned int, + const unsigned char *, + void *); + +typedef int (*custom_ext_parse_cb)(SSL *, unsigned int, + const unsigned char *, + size_t, int *, + void *); + +int SSL_CTX_add_client_custom_ext(SSL_CTX *, unsigned int, + custom_ext_add_cb, + custom_ext_free_cb, void *, + custom_ext_parse_cb, + void *); + +int SSL_CTX_add_server_custom_ext(SSL_CTX *, unsigned int, + custom_ext_add_cb, + custom_ext_free_cb, void *, + custom_ext_parse_cb, + void *); + +int SSL_extension_supported(unsigned int); + +int SSL_CTX_set_ciphersuites(SSL_CTX *, const char *); +int SSL_verify_client_post_handshake(SSL *); +void SSL_CTX_set_post_handshake_auth(SSL_CTX *, int); +void SSL_set_post_handshake_auth(SSL *, int); + +uint32_t SSL_SESSION_get_max_early_data(const SSL_SESSION *); +int SSL_write_early_data(SSL *, const void *, size_t, size_t *); +int SSL_read_early_data(SSL *, void *, size_t, size_t *); +int SSL_CTX_set_max_early_data(SSL_CTX *, uint32_t); """ CUSTOMIZATIONS = """ -/** Secure renegotiation is supported in OpenSSL >= 0.9.8m - * But some Linux distributions have back ported some features. - */ -#ifndef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION -static const long Cryptography_HAS_SECURE_RENEGOTIATION = 0; -long (*SSL_get_secure_renegotiation_support)(SSL *) = NULL; -const long SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION = 0; -const long SSL_OP_LEGACY_SERVER_CONNECT = 0; +#if CRYPTOGRAPHY_IS_LIBRESSL +const SSL_METHOD *SSL_CTX_get_ssl_method(SSL_CTX *ctx) { + return ctx->method; +} +#endif + +#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 +static const long Cryptography_HAS_VERIFIED_CHAIN = 0; +Cryptography_STACK_OF_X509 *(*SSL_get0_verified_chain)(const SSL *) = NULL; #else -static const long Cryptography_HAS_SECURE_RENEGOTIATION = 1; +static const long Cryptography_HAS_VERIFIED_CHAIN = 1; #endif -#ifdef OPENSSL_NO_SSL2 -static const long Cryptography_HAS_SSL2 = 0; -SSL_METHOD* (*SSLv2_method)(void) = NULL; -SSL_METHOD* (*SSLv2_client_method)(void) = NULL; -SSL_METHOD* (*SSLv2_server_method)(void) = NULL; + +#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_111 +static const long Cryptography_HAS_KEYLOG = 0; +void (*SSL_CTX_set_keylog_callback)(SSL_CTX *, + void (*) (const SSL *, const char *) + ) = NULL; +void (*(*SSL_CTX_get_keylog_callback)(SSL_CTX *))( + const SSL *, + const char * + ) = NULL; #else -static const long Cryptography_HAS_SSL2 = 1; +static const long Cryptography_HAS_KEYLOG = 1; +#endif + +/* Added in 1.1.0 in the great opaquing, but we need to define it for older + OpenSSLs. Such is our burden. */ +#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 && !CRYPTOGRAPHY_IS_LIBRESSL +/* from ssl/ssl_lib.c */ +size_t SSL_get_client_random(const SSL *ssl, unsigned char *out, size_t outlen) +{ + if (outlen == 0) + return sizeof(ssl->s3->client_random); + if (outlen > sizeof(ssl->s3->client_random)) + outlen = sizeof(ssl->s3->client_random); + memcpy(out, ssl->s3->client_random, outlen); + return outlen; +} +/* Added in 1.1.0 as well */ +/* from ssl/ssl_lib.c */ +size_t SSL_get_server_random(const SSL *ssl, unsigned char *out, size_t outlen) +{ + if (outlen == 0) + return sizeof(ssl->s3->server_random); + if (outlen > sizeof(ssl->s3->server_random)) + outlen = sizeof(ssl->s3->server_random); + memcpy(out, ssl->s3->server_random, outlen); + return outlen; +} +/* Added in 1.1.0 as well */ +/* from ssl/ssl_lib.c */ +size_t SSL_SESSION_get_master_key(const SSL_SESSION *session, + unsigned char *out, size_t outlen) +{ + if (session->master_key_length < 0) { + /* Should never happen */ + return 0; + } + if (outlen == 0) + return session->master_key_length; + if (outlen > (size_t)session->master_key_length) + outlen = session->master_key_length; + memcpy(out, session->master_key, outlen); + return outlen; +} +/* from ssl/ssl_sess.c */ +int SSL_SESSION_has_ticket(const SSL_SESSION *s) +{ + return (s->tlsext_ticklen > 0) ? 1 : 0; +} +/* from ssl/ssl_sess.c */ +unsigned long SSL_SESSION_get_ticket_lifetime_hint(const SSL_SESSION *s) +{ + return s->tlsext_tick_lifetime_hint; +} #endif +static const long Cryptography_HAS_SECURE_RENEGOTIATION = 1; + +/* Cryptography now compiles out all SSLv2 bindings. This exists to allow + * clients that use it to check for SSLv2 support to keep functioning as + * expected. + */ +static const long Cryptography_HAS_SSL2 = 0; + #ifdef OPENSSL_NO_SSL3_METHOD static const long Cryptography_HAS_SSL3_METHOD = 0; SSL_METHOD* (*SSLv3_method)(void) = NULL; @@ -429,308 +654,172 @@ SSL_METHOD* (*SSLv3_server_method)(void) = NULL; static const long Cryptography_HAS_SSL3_METHOD = 1; #endif -#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME static const long Cryptography_HAS_TLSEXT_HOSTNAME = 1; -#else -static const long Cryptography_HAS_TLSEXT_HOSTNAME = 0; -void (*SSL_set_tlsext_host_name)(SSL *, char *) = NULL; -const char* (*SSL_get_servername)(const SSL *, const int) = NULL; -void (*SSL_CTX_set_tlsext_servername_callback)( - SSL_CTX *, - int (*)(const SSL *, int *, void *)) = NULL; -#endif - -#ifdef SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB static const long Cryptography_HAS_TLSEXT_STATUS_REQ_CB = 1; -#else -static const long Cryptography_HAS_TLSEXT_STATUS_REQ_CB = 0; -long (*SSL_CTX_set_tlsext_status_cb)(SSL_CTX *, int(*)(SSL *, void *)) = NULL; -long (*SSL_CTX_set_tlsext_status_arg)(SSL_CTX *, void *) = NULL; -#endif - -#ifdef SSL_CTRL_SET_TLSEXT_STATUS_REQ_OCSP_RESP static const long Cryptography_HAS_STATUS_REQ_OCSP_RESP = 1; -#else -static const long Cryptography_HAS_STATUS_REQ_OCSP_RESP = 0; -long (*SSL_set_tlsext_status_ocsp_resp)(SSL *, unsigned char *, int) = NULL; -long (*SSL_get_tlsext_status_ocsp_resp)(SSL *, const unsigned char **) = NULL; -#endif - -#ifdef SSL_CTRL_SET_TLSEXT_STATUS_REQ_TYPE static const long Cryptography_HAS_TLSEXT_STATUS_REQ_TYPE = 1; -#else -static const long Cryptography_HAS_TLSEXT_STATUS_REQ_TYPE = 0; -long (*SSL_set_tlsext_status_type)(SSL *, long) = NULL; -#endif - -#ifdef SSL_MODE_RELEASE_BUFFERS static const long Cryptography_HAS_RELEASE_BUFFERS = 1; -#else -static const long Cryptography_HAS_RELEASE_BUFFERS = 0; -const long SSL_MODE_RELEASE_BUFFERS = 0; -#endif - -#ifdef SSL_OP_NO_COMPRESSION static const long Cryptography_HAS_OP_NO_COMPRESSION = 1; -#else -static const long Cryptography_HAS_OP_NO_COMPRESSION = 0; -const long SSL_OP_NO_COMPRESSION = 0; -#endif - -#ifdef SSL_OP_NO_TLSv1_1 static const long Cryptography_HAS_TLSv1_1 = 1; -#else -static const long Cryptography_HAS_TLSv1_1 = 0; -static const long SSL_OP_NO_TLSv1_1 = 0; -SSL_METHOD* (*TLSv1_1_method)(void) = NULL; -SSL_METHOD* (*TLSv1_1_client_method)(void) = NULL; -SSL_METHOD* (*TLSv1_1_server_method)(void) = NULL; -#endif - -#ifdef SSL_OP_NO_TLSv1_2 static const long Cryptography_HAS_TLSv1_2 = 1; +static const long Cryptography_HAS_SSL_OP_MSIE_SSLV2_RSA_PADDING = 1; +static const long Cryptography_HAS_SSL_OP_NO_TICKET = 1; +static const long Cryptography_HAS_SSL_SET_SSL_CTX = 1; +static const long Cryptography_HAS_NEXTPROTONEG = 1; +static const long Cryptography_HAS_ALPN = 1; + +#if CRYPTOGRAPHY_IS_LIBRESSL +void (*SSL_CTX_set_cert_cb)(SSL_CTX *, int (*)(SSL *, void *), void *) = NULL; +void (*SSL_set_cert_cb)(SSL *, int (*)(SSL *, void *), void *) = NULL; +static const long Cryptography_HAS_SET_CERT_CB = 0; #else -static const long Cryptography_HAS_TLSv1_2 = 0; -static const long SSL_OP_NO_TLSv1_2 = 0; -SSL_METHOD* (*TLSv1_2_method)(void) = NULL; -SSL_METHOD* (*TLSv1_2_client_method)(void) = NULL; -SSL_METHOD* (*TLSv1_2_server_method)(void) = NULL; +static const long Cryptography_HAS_SET_CERT_CB = 1; #endif -#ifdef SSL_OP_MSIE_SSLV2_RSA_PADDING -static const long Cryptography_HAS_SSL_OP_MSIE_SSLV2_RSA_PADDING = 1; +/* In OpenSSL 1.0.2i+ the handling of COMP_METHOD when OPENSSL_NO_COMP was + changed and we no longer need to typedef void */ +#if (defined(OPENSSL_NO_COMP) && CRYPTOGRAPHY_OPENSSL_LESS_THAN_102I) || \ + CRYPTOGRAPHY_IS_LIBRESSL +static const long Cryptography_HAS_COMPRESSION = 0; +typedef void COMP_METHOD; #else -static const long Cryptography_HAS_SSL_OP_MSIE_SSLV2_RSA_PADDING = 0; -const long SSL_OP_MSIE_SSLV2_RSA_PADDING = 0; +static const long Cryptography_HAS_COMPRESSION = 1; #endif -#ifdef OPENSSL_NO_EC -long (*SSL_CTX_set_tmp_ecdh)(SSL_CTX *, EC_KEY *) = NULL; -#endif +static const long Cryptography_HAS_SSL_CTX_SET_CLIENT_CERT_ENGINE = 1; -#ifdef SSL_OP_NO_TICKET -static const long Cryptography_HAS_SSL_OP_NO_TICKET = 1; +static const long Cryptography_HAS_SSL_CTX_CLEAR_OPTIONS = 1; + +/* in OpenSSL 1.1.0 the SSL_ST values were renamed to TLS_ST and several were + removed */ +#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 +static const long Cryptography_HAS_SSL_ST = 1; #else -static const long Cryptography_HAS_SSL_OP_NO_TICKET = 0; -const long SSL_OP_NO_TICKET = 0; +static const long Cryptography_HAS_SSL_ST = 0; +static const long SSL_ST_BEFORE = 0; +static const long SSL_ST_OK = 0; +static const long SSL_ST_INIT = 0; +static const long SSL_ST_RENEGOTIATE = 0; #endif - -/* OpenSSL 0.9.8f+ */ -#if OPENSSL_VERSION_NUMBER >= 0x00908070L -static const long Cryptography_HAS_SSL_SET_SSL_CTX = 1; +#if CRYPTOGRAPHY_OPENSSL_110_OR_GREATER +static const long Cryptography_HAS_TLS_ST = 1; #else -static const long Cryptography_HAS_SSL_SET_SSL_CTX = 0; -static const long TLSEXT_NAMETYPE_host_name = 0; -SSL_CTX *(*SSL_set_SSL_CTX)(SSL *, SSL_CTX *) = NULL; +static const long Cryptography_HAS_TLS_ST = 0; +static const long TLS_ST_BEFORE = 0; +static const long TLS_ST_OK = 0; #endif -/* NetBSD shipped without including d1_meth.c. This workaround checks to see - if the version of NetBSD we're currently running on is old enough to - have the bug and provides an empty implementation so we can link and - then remove the function from the ffi object. */ -#ifdef __NetBSD__ -# include <sys/param.h> -# if (__NetBSD_Version__ < 699003800) -static const long Cryptography_HAS_NETBSD_D1_METH = 0; -const SSL_METHOD *DTLSv1_method(void) { - return NULL; -} -# else -static const long Cryptography_HAS_NETBSD_D1_METH = 1; -# endif -#else -static const long Cryptography_HAS_NETBSD_D1_METH = 1; +#if CRYPTOGRAPHY_IS_LIBRESSL +static const long SSL_OP_NO_DTLSv1 = 0; +static const long SSL_OP_NO_DTLSv1_2 = 0; +long (*DTLS_set_link_mtu)(SSL *, long) = NULL; +long (*DTLS_get_link_min_mtu)(SSL *) = NULL; #endif -/* Workaround for #794 caused by cffi const** bug. */ -const SSL_METHOD *Cryptography_SSL_CTX_get_method(const SSL_CTX *ctx) { - return ctx->method; +static const long Cryptography_HAS_DTLS = 1; +/* Wrap DTLSv1_get_timeout to avoid cffi to handle a 'struct timeval'. */ +long Cryptography_DTLSv1_get_timeout(SSL *ssl, time_t *ptv_sec, + long *ptv_usec) { + struct timeval tv = { 0 }; + long r = DTLSv1_get_timeout(ssl, &tv); + + if (r == 1) { + if (ptv_sec) { + *ptv_sec = tv.tv_sec; + } + + if (ptv_usec) { + *ptv_usec = tv.tv_usec; + } + } + + return r; } -/* Because OPENSSL defines macros that claim lack of support for things, rather - * than macros that claim support for things, we need to do a version check in - * addition to a definition check. NPN was added in 1.0.1: for any version - * before that, there is no compatibility. - */ -#if defined(OPENSSL_NO_NEXTPROTONEG) || OPENSSL_VERSION_NUMBER < 0x1000100fL -static const long Cryptography_HAS_NEXTPROTONEG = 0; -void (*SSL_CTX_set_next_protos_advertised_cb)(SSL_CTX *, - int (*)(SSL *, - const unsigned char **, - unsigned int *, - void *), - void *) = NULL; -void (*SSL_CTX_set_next_proto_select_cb)(SSL_CTX *, - int (*)(SSL *, - unsigned char **, - unsigned char *, - const unsigned char *, - unsigned int, - void *), - void *) = NULL; -int (*SSL_select_next_proto)(unsigned char **, unsigned char *, - const unsigned char *, unsigned int, - const unsigned char *, unsigned int) = NULL; -void (*SSL_get0_next_proto_negotiated)(const SSL *, - const unsigned char **, - unsigned *) = NULL; +#if CRYPTOGRAPHY_IS_LIBRESSL +static const long Cryptography_HAS_SIGALGS = 0; +const int (*SSL_get_sigalgs)(SSL *, int, int *, int *, int *, unsigned char *, + unsigned char *) = NULL; +const long (*SSL_CTX_set1_sigalgs_list)(SSL_CTX *, const char *) = NULL; #else -static const long Cryptography_HAS_NEXTPROTONEG = 1; +static const long Cryptography_HAS_SIGALGS = 1; #endif -/* ALPN was added in OpenSSL 1.0.2. */ -#if OPENSSL_VERSION_NUMBER < 0x10002001L && !defined(LIBRESSL_VERSION_NUMBER) -int (*SSL_CTX_set_alpn_protos)(SSL_CTX *, - const unsigned char *, - unsigned) = NULL; -int (*SSL_set_alpn_protos)(SSL *, const unsigned char *, unsigned) = NULL; -void (*SSL_CTX_set_alpn_select_cb)(SSL_CTX *, - int (*) (SSL *, - const unsigned char **, +#if CRYPTOGRAPHY_IS_LIBRESSL || defined(OPENSSL_NO_PSK) +static const long Cryptography_HAS_PSK = 0; +int (*SSL_CTX_use_psk_identity_hint)(SSL_CTX *, const char *) = NULL; +void (*SSL_CTX_set_psk_server_callback)(SSL_CTX *, + unsigned int (*)( + SSL *, + const char *, unsigned char *, - const unsigned char *, + unsigned int + )) = NULL; +void (*SSL_CTX_set_psk_client_callback)(SSL_CTX *, + unsigned int (*)( + SSL *, + const char *, + char *, unsigned int, - void *), - void *) = NULL; -void (*SSL_get0_alpn_selected)(const SSL *, - const unsigned char **, - unsigned *) = NULL; -static const long Cryptography_HAS_ALPN = 0; + unsigned char *, + unsigned int + )) = NULL; #else -static const long Cryptography_HAS_ALPN = 1; +static const long Cryptography_HAS_PSK = 1; #endif -#if defined(OPENSSL_NO_COMP) || defined(LIBRESSL_VERSION_NUMBER) -static const long Cryptography_HAS_COMPRESSION = 0; -typedef void COMP_METHOD; +#if !CRYPTOGRAPHY_IS_LIBRESSL +static const long Cryptography_HAS_CUSTOM_EXT = 1; #else -static const long Cryptography_HAS_COMPRESSION = 1; +static const long Cryptography_HAS_CUSTOM_EXT = 0; +typedef int (*custom_ext_add_cb)(SSL *, unsigned int, + const unsigned char **, + size_t *, int *, + void *); +typedef void (*custom_ext_free_cb)(SSL *, unsigned int, + const unsigned char *, + void *); +typedef int (*custom_ext_parse_cb)(SSL *, unsigned int, + const unsigned char *, + size_t, int *, + void *); +int (*SSL_CTX_add_client_custom_ext)(SSL_CTX *, unsigned int, + custom_ext_add_cb, + custom_ext_free_cb, void *, + custom_ext_parse_cb, + void *) = NULL; +int (*SSL_CTX_add_server_custom_ext)(SSL_CTX *, unsigned int, + custom_ext_add_cb, + custom_ext_free_cb, void *, + custom_ext_parse_cb, + void *) = NULL; +int (*SSL_extension_supported)(unsigned int) = NULL; #endif -#if defined(SSL_CTRL_GET_SERVER_TMP_KEY) -static const long Cryptography_HAS_GET_SERVER_TMP_KEY = 1; +#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 && !CRYPTOGRAPHY_IS_LIBRESSL +int (*SSL_CIPHER_is_aead)(const SSL_CIPHER *) = NULL; +int (*SSL_CIPHER_get_cipher_nid)(const SSL_CIPHER *) = NULL; +int (*SSL_CIPHER_get_digest_nid)(const SSL_CIPHER *) = NULL; +int (*SSL_CIPHER_get_kx_nid)(const SSL_CIPHER *) = NULL; +int (*SSL_CIPHER_get_auth_nid)(const SSL_CIPHER *) = NULL; +static const long Cryptography_HAS_CIPHER_DETAILS = 0; #else -static const long Cryptography_HAS_GET_SERVER_TMP_KEY = 0; -long (*SSL_get_server_tmp_key)(SSL *, EVP_PKEY **) = NULL; +static const long Cryptography_HAS_CIPHER_DETAILS = 1; #endif -/* Added in 0.9.8i */ -#if OPENSSL_VERSION_NUMBER < 0x0090809fL -int (*SSL_CTX_set_client_cert_engine)(SSL_CTX *, ENGINE *) = NULL; -static const long Cryptography_HAS_SSL_CTX_SET_CLIENT_CERT_ENGINE = 0; -# else -static const long Cryptography_HAS_SSL_CTX_SET_CLIENT_CERT_ENGINE = 1; +#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_111 +static const long Cryptography_HAS_TLSv1_3 = 0; +static const long SSL_OP_NO_TLSv1_3 = 0; +static const long SSL_VERIFY_POST_HANDSHAKE = 0; +int (*SSL_CTX_set_ciphersuites)(SSL_CTX *, const char *) = NULL; +int (*SSL_verify_client_post_handshake)(SSL *) = NULL; +void (*SSL_CTX_set_post_handshake_auth)(SSL_CTX *, int) = NULL; +void (*SSL_set_post_handshake_auth)(SSL *, int) = NULL; +uint32_t (*SSL_SESSION_get_max_early_data)(const SSL_SESSION *) = NULL; +int (*SSL_write_early_data)(SSL *, const void *, size_t, size_t *) = NULL; +int (*SSL_read_early_data)(SSL *, void *, size_t, size_t *) = NULL; +int (*SSL_CTX_set_max_early_data)(SSL_CTX *, uint32_t) = NULL; +#else +static const long Cryptography_HAS_TLSv1_3 = 1; #endif - """ - -CONDITIONAL_NAMES = { - "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_SSL2": [ - "SSLv2_method", - "SSLv2_client_method", - "SSLv2_server_method", - ], - - "Cryptography_HAS_SSL3_METHOD": [ - "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_EC": [ - "SSL_CTX_set_tmp_ecdh", - ], - - "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": [ - "SSL_CTX_set_alpn_protos", - "SSL_set_alpn_protos", - "SSL_CTX_set_alpn_select_cb", - "SSL_get0_alpn_selected", - ], - - "Cryptography_HAS_COMPRESSION": [ - "SSL_get_current_compression", - "SSL_get_current_expansion", - "SSL_COMP_get_name", - ], - - "Cryptography_HAS_GET_SERVER_TMP_KEY": [ - "SSL_get_server_tmp_key", - ], - - "Cryptography_HAS_SSL_CTX_SET_CLIENT_CERT_ENGINE": [ - "SSL_CTX_set_client_cert_engine", - ], -} diff --git a/src/_cffi_src/openssl/x509.py b/src/_cffi_src/openssl/x509.py index 6bd117b0..0135a89a 100644 --- a/src/_cffi_src/openssl/x509.py +++ b/src/_cffi_src/openssl/x509.py @@ -30,86 +30,18 @@ typedef struct { } X509_ALGOR; typedef ... X509_ATTRIBUTE; - -typedef struct { - X509_ALGOR *signature; - ...; -} X509_CINF; - -typedef struct { - ASN1_OBJECT *object; - ASN1_BOOLEAN critical; - ASN1_OCTET_STRING *value; -} X509_EXTENSION; - +typedef ... X509_EXTENSION; typedef ... X509_EXTENSIONS; - -typedef struct { - X509_ALGOR *sig_alg; - ...; -} X509_REQ; - -typedef struct { - ASN1_INTEGER *serialNumber; - ASN1_TIME *revocationDate; - X509_EXTENSIONS *extensions; - int sequence; - ...; -} X509_REVOKED; - -typedef struct { - Cryptography_STACK_OF_X509_REVOKED *revoked; - ...; -} X509_CRL_INFO; - -typedef struct { - X509_CRL_INFO *crl; - X509_ALGOR *sig_alg; - ...; -} X509_CRL; - -typedef struct { - X509_ALGOR *sig_alg; - X509_CINF *cert_info; - ...; -} X509; +typedef ... X509_REQ; +typedef ... X509_REVOKED; +typedef ... X509_CRL; +typedef ... X509; typedef ... NETSCAPE_SPKI; typedef ... PKCS8_PRIV_KEY_INFO; -static const int X509_FLAG_COMPAT; -static const int X509_FLAG_NO_HEADER; -static const int X509_FLAG_NO_VERSION; -static const int X509_FLAG_NO_SERIAL; -static const int X509_FLAG_NO_SIGNAME; -static const int X509_FLAG_NO_ISSUER; -static const int X509_FLAG_NO_VALIDITY; -static const int X509_FLAG_NO_SUBJECT; -static const int X509_FLAG_NO_PUBKEY; -static const int X509_FLAG_NO_EXTENSIONS; -static const int X509_FLAG_NO_SIGDUMP; -static const int X509_FLAG_NO_AUX; -static const int X509_FLAG_NO_ATTRIBUTES; - -static const int XN_FLAG_SEP_MASK; -static const int XN_FLAG_COMPAT; -static const int XN_FLAG_SEP_COMMA_PLUS; -static const int XN_FLAG_SEP_CPLUS_SPC; -static const int XN_FLAG_SEP_SPLUS_SPC; -static const int XN_FLAG_SEP_MULTILINE; -static const int XN_FLAG_DN_REV; -static const int XN_FLAG_FN_MASK; -static const int XN_FLAG_FN_SN; -static const int XN_FLAG_FN_LN; -static const int XN_FLAG_FN_OID; -static const int XN_FLAG_FN_NONE; -static const int XN_FLAG_SPC_EQ; -static const int XN_FLAG_DUMP_UNKNOWN_FIELDS; -static const int XN_FLAG_FN_ALIGN; -static const int XN_FLAG_RFC2253; -static const int XN_FLAG_ONELINE; -static const int XN_FLAG_MULTILINE; +typedef void (*sk_X509_EXTENSION_freefunc)(X509_EXTENSION *); """ FUNCTIONS = """ @@ -117,6 +49,7 @@ X509 *X509_new(void); void X509_free(X509 *); X509 *X509_dup(X509 *); int X509_cmp(const X509 *, const X509 *); +int X509_up_ref(X509 *); int X509_print_ex(BIO *, X509 *, unsigned long, unsigned long); @@ -134,27 +67,15 @@ ASN1_TIME *X509_gmtime_adj(ASN1_TIME *, long); unsigned long X509_subject_name_hash(X509 *); -X509_NAME *X509_get_subject_name(X509 *); int X509_set_subject_name(X509 *, X509_NAME *); -X509_NAME *X509_get_issuer_name(X509 *); int X509_set_issuer_name(X509 *, X509_NAME *); -int X509_get_ext_count(X509 *); int X509_add_ext(X509 *, X509_EXTENSION *, int); -X509_EXTENSION *X509_delete_ext(X509 *, int); X509_EXTENSION *X509_EXTENSION_dup(X509_EXTENSION *); -X509_EXTENSION *X509_get_ext(X509 *, int); -int X509_get_ext_by_NID(X509 *, int, int); -int X509_EXTENSION_get_critical(X509_EXTENSION *); ASN1_OBJECT *X509_EXTENSION_get_object(X509_EXTENSION *); void X509_EXTENSION_free(X509_EXTENSION *); -X509_EXTENSION *X509_EXTENSION_create_by_OBJ(X509_EXTENSION **, - ASN1_OBJECT *, int, - ASN1_OCTET_STRING *); - -int i2d_X509(X509 *, unsigned char **); int X509_REQ_set_version(X509_REQ *, long); X509_REQ *X509_REQ_new(void); @@ -163,11 +84,16 @@ int X509_REQ_set_pubkey(X509_REQ *, EVP_PKEY *); int X509_REQ_set_subject_name(X509_REQ *, X509_NAME *); int X509_REQ_sign(X509_REQ *, EVP_PKEY *, const EVP_MD *); int X509_REQ_verify(X509_REQ *, EVP_PKEY *); -int X509_REQ_digest(const X509_REQ *, const EVP_MD *, - unsigned char *, unsigned int *); EVP_PKEY *X509_REQ_get_pubkey(X509_REQ *); -int X509_REQ_print(BIO *, X509_REQ *); int X509_REQ_print_ex(BIO *, X509_REQ *, unsigned long, unsigned long); +int X509_REQ_add_extensions(X509_REQ *, X509_EXTENSIONS *); +X509_EXTENSIONS *X509_REQ_get_extensions(X509_REQ *); +X509_ATTRIBUTE *X509_REQ_get_attr(const X509_REQ *, int); +int X509_REQ_get_attr_by_OBJ(const X509_REQ *, const ASN1_OBJECT *, int); +void *X509_ATTRIBUTE_get0_data(X509_ATTRIBUTE *, int, int, void *); +ASN1_TYPE *X509_ATTRIBUTE_get0_type(X509_ATTRIBUTE *, int); +int X509_REQ_add1_attr_by_txt(X509_REQ *, const char *, int, + const unsigned char *, int); int X509V3_EXT_print(BIO *, X509_EXTENSION *, unsigned long, int); ASN1_OCTET_STRING *X509_EXTENSION_get_data(X509_EXTENSION *); @@ -177,18 +103,18 @@ void X509_REVOKED_free(X509_REVOKED *); int X509_REVOKED_set_serialNumber(X509_REVOKED *, ASN1_INTEGER *); -int X509_REVOKED_get_ext_count(X509_REVOKED *); -X509_EXTENSION *X509_REVOKED_get_ext(X509_REVOKED *, int); int X509_REVOKED_add_ext(X509_REVOKED *, X509_EXTENSION*, int); int X509_REVOKED_add1_ext_i2d(X509_REVOKED *, int, void *, int, unsigned long); +X509_EXTENSION *X509_REVOKED_delete_ext(X509_REVOKED *, int); + +int X509_REVOKED_set_revocationDate(X509_REVOKED *, ASN1_TIME *); X509_CRL *X509_CRL_new(void); +X509_CRL *X509_CRL_dup(X509_CRL *); X509_CRL *d2i_X509_CRL_bio(BIO *, X509_CRL **); -X509_EXTENSION *X509_CRL_get_ext(X509_CRL *, int); int X509_CRL_add0_revoked(X509_CRL *, X509_REVOKED *); int X509_CRL_add_ext(X509_CRL *, X509_EXTENSION *, int); int X509_CRL_cmp(const X509_CRL *, const X509_CRL *); -int X509_CRL_get_ext_count(X509_CRL *); int X509_CRL_print(BIO *, X509_CRL *); int X509_CRL_set_issuer_name(X509_CRL *, X509_NAME *); int X509_CRL_set_version(X509_CRL *, long); @@ -224,43 +150,59 @@ int X509_set_serialNumber(X509 *, ASN1_INTEGER *); const char *X509_verify_cert_error_string(long); -const char *X509_get_default_cert_area(void); const char *X509_get_default_cert_dir(void); const char *X509_get_default_cert_file(void); const char *X509_get_default_cert_dir_env(void); const char *X509_get_default_cert_file_env(void); -const char *X509_get_default_private_dir(void); - -int i2d_RSA_PUBKEY(RSA *, unsigned char **); -RSA *d2i_RSA_PUBKEY(RSA **, const unsigned char **, long); -RSA *d2i_RSAPublicKey(RSA **, const unsigned char **, long); -RSA *d2i_RSAPrivateKey(RSA **, const unsigned char **, long); -int i2d_DSA_PUBKEY(DSA *, unsigned char **); -DSA *d2i_DSA_PUBKEY(DSA **, const unsigned char **, long); -DSA *d2i_DSAPublicKey(DSA **, const unsigned char **, long); -DSA *d2i_DSAPrivateKey(DSA **, const unsigned char **, long); - -RSA *d2i_RSAPrivateKey_bio(BIO *, RSA **); + int i2d_RSAPrivateKey_bio(BIO *, RSA *); RSA *d2i_RSAPublicKey_bio(BIO *, RSA **); int i2d_RSAPublicKey_bio(BIO *, RSA *); -RSA *d2i_RSA_PUBKEY_bio(BIO *, RSA **); -int i2d_RSA_PUBKEY_bio(BIO *, RSA *); -DSA *d2i_DSA_PUBKEY_bio(BIO *, DSA **); -int i2d_DSA_PUBKEY_bio(BIO *, DSA *); -DSA *d2i_DSAPrivateKey_bio(BIO *, DSA **); int i2d_DSAPrivateKey_bio(BIO *, DSA *); -PKCS8_PRIV_KEY_INFO *d2i_PKCS8_PRIV_KEY_INFO_bio(BIO *, - PKCS8_PRIV_KEY_INFO **); -void PKCS8_PRIV_KEY_INFO_free(PKCS8_PRIV_KEY_INFO *); -""" +/* These became const X509 in 1.1.0 */ +int X509_get_ext_count(X509 *); +X509_EXTENSION *X509_get_ext(X509 *, int); +X509_NAME *X509_get_subject_name(X509 *); +X509_NAME *X509_get_issuer_name(X509 *); + +/* This became const ASN1_OBJECT * in 1.1.0 */ +X509_EXTENSION *X509_EXTENSION_create_by_OBJ(X509_EXTENSION **, + ASN1_OBJECT *, int, + ASN1_OCTET_STRING *); + + +/* This became const X509_EXTENSION * in 1.1.0 */ +int X509_EXTENSION_get_critical(X509_EXTENSION *); + +/* This became const X509_REVOKED * in 1.1.0 */ +int X509_REVOKED_get_ext_count(X509_REVOKED *); +X509_EXTENSION *X509_REVOKED_get_ext(X509_REVOKED *, int); + +/* This became const X509_CRL * in 1.1.0 */ +X509_EXTENSION *X509_CRL_get_ext(X509_CRL *, int); +int X509_CRL_get_ext_count(X509_CRL *); + +int X509_CRL_get0_by_serial(X509_CRL *, X509_REVOKED **, ASN1_INTEGER *); + +X509_REVOKED *X509_REVOKED_dup(X509_REVOKED *); +X509_REVOKED *Cryptography_X509_REVOKED_dup(X509_REVOKED *); + +/* new in 1.0.2 */ +int i2d_re_X509_tbs(X509 *, unsigned char **); +int X509_get_signature_nid(const X509 *); + +const X509_ALGOR *X509_get0_tbs_sigalg(const X509 *); + +void X509_get0_signature(const ASN1_BIT_STRING **, + const X509_ALGOR **, const X509 *); -MACROS = """ long X509_get_version(X509 *); ASN1_TIME *X509_get_notBefore(X509 *); ASN1_TIME *X509_get_notAfter(X509 *); +ASN1_TIME *X509_getm_notBefore(X509 *); +ASN1_TIME *X509_getm_notAfter(X509 *); long X509_REQ_get_version(X509_REQ *); X509_NAME *X509_REQ_get_subject_name(X509_REQ *); @@ -275,8 +217,10 @@ X509_EXTENSIONS *sk_X509_EXTENSION_new_null(void); int sk_X509_EXTENSION_num(X509_EXTENSIONS *); X509_EXTENSION *sk_X509_EXTENSION_value(X509_EXTENSIONS *, int); int sk_X509_EXTENSION_push(X509_EXTENSIONS *, X509_EXTENSION *); +int sk_X509_EXTENSION_insert(X509_EXTENSIONS *, X509_EXTENSION *, int); X509_EXTENSION *sk_X509_EXTENSION_delete(X509_EXTENSIONS *, int); void sk_X509_EXTENSION_free(X509_EXTENSIONS *); +void sk_X509_EXTENSION_pop_free(X509_EXTENSIONS *, sk_X509_EXTENSION_freefunc); int sk_X509_REVOKED_num(Cryptography_STACK_OF_X509_REVOKED *); X509_REVOKED *sk_X509_REVOKED_value(Cryptography_STACK_OF_X509_REVOKED *, int); @@ -287,12 +231,7 @@ int sk_X509_CRL_num(Cryptography_STACK_OF_X509_CRL *); int sk_X509_CRL_push(Cryptography_STACK_OF_X509_CRL *, X509_CRL *); X509_CRL *sk_X509_CRL_value(Cryptography_STACK_OF_X509_CRL *, int); -int i2d_RSAPublicKey(RSA *, unsigned char **); -int i2d_RSAPrivateKey(RSA *, unsigned char **); -int i2d_DSAPublicKey(DSA *, unsigned char **); -int i2d_DSAPrivateKey(DSA *, unsigned char **); - -int X509_CRL_get_version(X509_CRL *); +long X509_CRL_get_version(X509_CRL *); ASN1_TIME *X509_CRL_get_lastUpdate(X509_CRL *); ASN1_TIME *X509_CRL_get_nextUpdate(X509_CRL *); X509_NAME *X509_CRL_get_issuer(X509_CRL *); @@ -303,61 +242,102 @@ int X509_CRL_set_lastUpdate(X509_CRL *, ASN1_TIME *); int X509_CRL_set_nextUpdate(X509_CRL *, ASN1_TIME *); int X509_set_notBefore(X509 *, ASN1_TIME *); int X509_set_notAfter(X509 *, ASN1_TIME *); +int X509_set1_notBefore(X509 *, ASN1_TIME *); +int X509_set1_notAfter(X509 *, ASN1_TIME *); -/* These use STACK_OF(X509_EXTENSION) in 0.9.8e. Once we drop support for - RHEL/CentOS 5 we should move these back to FUNCTIONS. */ -int X509_REQ_add_extensions(X509_REQ *, X509_EXTENSIONS *); -X509_EXTENSIONS *X509_REQ_get_extensions(X509_REQ *); - -int i2d_EC_PUBKEY(EC_KEY *, unsigned char **); -EC_KEY *d2i_EC_PUBKEY(EC_KEY **, const unsigned char **, long); EC_KEY *d2i_EC_PUBKEY_bio(BIO *, EC_KEY **); int i2d_EC_PUBKEY_bio(BIO *, EC_KEY *); -EC_KEY *d2i_ECPrivateKey(EC_KEY **, const unsigned char **, long); EC_KEY *d2i_ECPrivateKey_bio(BIO *, EC_KEY **); -int i2d_ECPrivateKey(EC_KEY *, unsigned char **); int i2d_ECPrivateKey_bio(BIO *, EC_KEY *); -EC_KEY *o2i_ECPublicKey(EC_KEY **, const unsigned char **, long); -int i2o_ECPublicKey(EC_KEY *, unsigned char **); - // declared in safestack int sk_ASN1_OBJECT_num(Cryptography_STACK_OF_ASN1_OBJECT *); ASN1_OBJECT *sk_ASN1_OBJECT_value(Cryptography_STACK_OF_ASN1_OBJECT *, int); void sk_ASN1_OBJECT_free(Cryptography_STACK_OF_ASN1_OBJECT *); +Cryptography_STACK_OF_ASN1_OBJECT *sk_ASN1_OBJECT_new_null(void); +int sk_ASN1_OBJECT_push(Cryptography_STACK_OF_ASN1_OBJECT *, ASN1_OBJECT *); + +/* these functions were added in 1.1.0 */ +const ASN1_INTEGER *X509_REVOKED_get0_serialNumber(const X509_REVOKED *); +const ASN1_TIME *X509_REVOKED_get0_revocationDate(const X509_REVOKED *); +void X509_CRL_get0_signature(const X509_CRL *, const ASN1_BIT_STRING **, + const X509_ALGOR **); +int i2d_re_X509_REQ_tbs(X509_REQ *, unsigned char **); +int i2d_re_X509_CRL_tbs(X509_CRL *, unsigned char **); +void X509_REQ_get0_signature(const X509_REQ *, const ASN1_BIT_STRING **, + const X509_ALGOR **); """ CUSTOMIZATIONS = """ -/* OpenSSL 0.9.8e does not have this definition. */ -#if OPENSSL_VERSION_NUMBER <= 0x0090805fL -typedef STACK_OF(X509_EXTENSION) X509_EXTENSIONS; -#endif -#ifdef OPENSSL_NO_EC -int (*i2d_EC_PUBKEY)(EC_KEY *, unsigned char **) = NULL; -EC_KEY *(*d2i_EC_PUBKEY)(EC_KEY **, const unsigned char **, long) = NULL; -EC_KEY *(*d2i_EC_PUBKEY_bio)(BIO *, EC_KEY **) = NULL; -int (*i2d_EC_PUBKEY_bio)(BIO *, EC_KEY *) = NULL; -EC_KEY *(*d2i_ECPrivateKey)(EC_KEY **, const unsigned char **, long) = NULL; -EC_KEY *(*d2i_ECPrivateKey_bio)(BIO *, EC_KEY **) = NULL; -int (*i2d_ECPrivateKey)(EC_KEY *, unsigned char **) = NULL; -int (*i2d_ECPrivateKey_bio)(BIO *, EC_KEY *) = NULL; - -EC_KEY *(*o2i_ECPublicKey)(EC_KEY **, const unsigned char **, long) = NULL; -int (*i2o_ECPublicKey)(EC_KEY *, unsigned char **) = NULL; +#if CRYPTOGRAPHY_IS_LIBRESSL +int i2d_re_X509_tbs(X509 *x, unsigned char **pp) +{ + /* in 1.0.2+ this function also sets x->cert_info->enc.modified = 1 + but older OpenSSLs don't have the enc ASN1_ENCODING member in the + X509 struct. Setting modified to 1 marks the encoding + (x->cert_info->enc.enc) as invalid, but since the entire struct isn't + present we don't care. */ + return i2d_X509_CINF(x->cert_info, pp); +} #endif -""" -CONDITIONAL_NAMES = { - "Cryptography_HAS_EC": [ - "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", - ] +/* Being kept around for pyOpenSSL */ +X509_REVOKED *Cryptography_X509_REVOKED_dup(X509_REVOKED *rev) { + return X509_REVOKED_dup(rev); } +/* Added in 1.1.0 but we need it in all versions now due to the great + opaquing. */ +#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 +int i2d_re_X509_REQ_tbs(X509_REQ *req, unsigned char **pp) +{ + req->req_info->enc.modified = 1; + return i2d_X509_REQ_INFO(req->req_info, pp); +} +int i2d_re_X509_CRL_tbs(X509_CRL *crl, unsigned char **pp) { + crl->crl->enc.modified = 1; + return i2d_X509_CRL_INFO(crl->crl, pp); +} + +#if !CRYPTOGRAPHY_IS_LIBRESSL +int X509_up_ref(X509 *x) { + return CRYPTO_add(&x->references, 1, CRYPTO_LOCK_X509); +} + +const X509_ALGOR *X509_get0_tbs_sigalg(const X509 *x) +{ + return x->cert_info->signature; +} + +/* from x509/x509_req.c */ +void X509_REQ_get0_signature(const X509_REQ *req, const ASN1_BIT_STRING **psig, + const X509_ALGOR **palg) +{ + if (psig != NULL) + *psig = req->signature; + if (palg != NULL) + *palg = req->sig_alg; +} +void X509_CRL_get0_signature(const X509_CRL *crl, const ASN1_BIT_STRING **psig, + const X509_ALGOR **palg) +{ + if (psig != NULL) + *psig = crl->signature; + if (palg != NULL) + *palg = crl->sig_alg; +} +const ASN1_TIME *X509_REVOKED_get0_revocationDate(const X509_REVOKED *x) +{ + return x->revocationDate; +} +const ASN1_INTEGER *X509_REVOKED_get0_serialNumber(const X509_REVOKED *x) +{ + return x->serialNumber; +} + +#define X509_set1_notBefore X509_set_notBefore +#define X509_set1_notAfter X509_set_notAfter +#define X509_getm_notAfter X509_get_notAfter +#define X509_getm_notBefore X509_get_notBefore +#endif +#endif +""" diff --git a/src/_cffi_src/openssl/x509_vfy.py b/src/_cffi_src/openssl/x509_vfy.py index 23ac8483..d2bc5f4e 100644 --- a/src/_cffi_src/openssl/x509_vfy.py +++ b/src/_cffi_src/openssl/x509_vfy.py @@ -15,36 +15,23 @@ INCLUDES = """ * Note that the result is an opaque type. */ typedef STACK_OF(ASN1_OBJECT) Cryptography_STACK_OF_ASN1_OBJECT; +typedef STACK_OF(X509_OBJECT) Cryptography_STACK_OF_X509_OBJECT; """ TYPES = """ -static const long Cryptography_HAS_102_VERIFICATION_ERROR_CODES; -static const long Cryptography_HAS_102_VERIFICATION_PARAMS; -static const long Cryptography_HAS_X509_V_FLAG_TRUSTED_FIRST; -static const long Cryptography_HAS_X509_V_FLAG_PARTIAL_CHAIN; -static const long Cryptography_HAS_100_VERIFICATION_ERROR_CODES; -static const long Cryptography_HAS_100_VERIFICATION_PARAMS; -static const long Cryptography_HAS_X509_V_FLAG_CHECK_SS_SIGNATURE; +static const long Cryptography_HAS_102_VERIFICATION; +static const long Cryptography_HAS_110_VERIFICATION_PARAMS; +static const long Cryptography_HAS_X509_STORE_CTX_GET_ISSUER; typedef ... Cryptography_STACK_OF_ASN1_OBJECT; +typedef ... Cryptography_STACK_OF_X509_OBJECT; +typedef ... X509_OBJECT; typedef ... X509_STORE; typedef ... X509_VERIFY_PARAM; +typedef ... X509_STORE_CTX; -typedef struct x509_store_ctx_st X509_STORE_CTX; -struct x509_store_ctx_st { - X509_STORE *ctx; - int current_method; - X509 *cert; - Cryptography_STACK_OF_X509 *untrusted; - Cryptography_STACK_OF_X509_CRL *crls; - X509_VERIFY_PARAM *param; - void *other_ctx; - int (*verify)(X509_STORE_CTX *); - int (*verify_cb)(int, X509_STORE_CTX *); - int (*get_issuer)(X509 **, X509_STORE_CTX *, X509 *); - ...; -}; +typedef int (*X509_STORE_CTX_get_issuer_fn)(X509 **, X509_STORE_CTX *, X509 *); /* While these are defined in the source as ints, they're tagged here as longs, just in case they ever grow to large, such as what we saw @@ -136,6 +123,16 @@ static const long X509_V_FLAG_SUITEB_128_LOS_ONLY; static const long X509_V_FLAG_SUITEB_192_LOS; static const long X509_V_FLAG_SUITEB_128_LOS; static const long X509_V_FLAG_PARTIAL_CHAIN; + +static const long X509_LU_X509; +static const long X509_LU_CRL; + +static const long X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT; +static const long X509_CHECK_FLAG_NO_WILDCARDS; +static const long X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS; +static const long X509_CHECK_FLAG_MULTI_LABEL_WILDCARDS; +static const long X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS; +static const long X509_CHECK_FLAG_NEVER_CHECK_SUBJECT; """ FUNCTIONS = """ @@ -151,7 +148,6 @@ int X509_STORE_set_default_paths(X509_STORE *); int X509_STORE_set_flags(X509_STORE *, unsigned long); void X509_STORE_free(X509_STORE *); - /* X509_STORE_CTX */ X509_STORE_CTX *X509_STORE_CTX_new(void); void X509_STORE_CTX_cleanup(X509_STORE_CTX *); @@ -175,6 +171,7 @@ int X509_STORE_CTX_get_error_depth(X509_STORE_CTX *); X509 *X509_STORE_CTX_get_current_cert(X509_STORE_CTX *); int X509_STORE_CTX_set_ex_data(X509_STORE_CTX *, int, void *); void *X509_STORE_CTX_get_ex_data(X509_STORE_CTX *, int); +int X509_STORE_CTX_get1_issuer(X509 **, X509_STORE_CTX *, X509 *); /* X509_VERIFY_PARAM */ X509_VERIFY_PARAM *X509_VERIFY_PARAM_new(void); @@ -189,9 +186,11 @@ int X509_VERIFY_PARAM_set1_policies(X509_VERIFY_PARAM *, Cryptography_STACK_OF_ASN1_OBJECT *); void X509_VERIFY_PARAM_set_depth(X509_VERIFY_PARAM *, int); int X509_VERIFY_PARAM_get_depth(const X509_VERIFY_PARAM *); -""" +void X509_VERIFY_PARAM_free(X509_VERIFY_PARAM *); +/* this CRYPTO_EX_DATA function became a macro in 1.1.0 */ +int X509_STORE_CTX_get_ex_new_index(long, void *, CRYPTO_EX_new *, + CRYPTO_EX_dup *, CRYPTO_EX_free *); -MACROS = """ /* X509_STORE_CTX */ void X509_STORE_CTX_set0_crls(X509_STORE_CTX *, Cryptography_STACK_OF_X509_CRL *); @@ -205,143 +204,74 @@ int X509_VERIFY_PARAM_set1_email(X509_VERIFY_PARAM *, const char *, int X509_VERIFY_PARAM_set1_ip(X509_VERIFY_PARAM *, const unsigned char *, size_t); int X509_VERIFY_PARAM_set1_ip_asc(X509_VERIFY_PARAM *, const char *); + +int sk_X509_OBJECT_num(Cryptography_STACK_OF_X509_OBJECT *); +X509_OBJECT *sk_X509_OBJECT_value(Cryptography_STACK_OF_X509_OBJECT *, int); +X509_VERIFY_PARAM *X509_STORE_get0_param(X509_STORE *); +Cryptography_STACK_OF_X509_OBJECT *X509_STORE_get0_objects(X509_STORE *); +X509 *X509_OBJECT_get0_X509(X509_OBJECT *); +int X509_OBJECT_get_type(const X509_OBJECT *); + +/* added in 1.1.0 */ +X509 *X509_STORE_CTX_get0_cert(X509_STORE_CTX *); +X509_STORE_CTX_get_issuer_fn X509_STORE_get_get_issuer(X509_STORE *); +void X509_STORE_set_get_issuer(X509_STORE *, X509_STORE_CTX_get_issuer_fn); """ CUSTOMIZATIONS = """ -/* OpenSSL 1.0.2+ verification error codes */ -#if OPENSSL_VERSION_NUMBER >= 0x10002000L && !defined(LIBRESSL_VERSION_NUMBER) -static const long Cryptography_HAS_102_VERIFICATION_ERROR_CODES = 1; +#if !CRYPTOGRAPHY_IS_LIBRESSL +static const long Cryptography_HAS_102_VERIFICATION = 1; #else -static const long Cryptography_HAS_102_VERIFICATION_ERROR_CODES = 0; +static const long Cryptography_HAS_102_VERIFICATION = 0; static const long X509_V_ERR_SUITE_B_INVALID_VERSION = 0; static const long X509_V_ERR_SUITE_B_INVALID_ALGORITHM = 0; static const long X509_V_ERR_SUITE_B_INVALID_CURVE = 0; static const long X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM = 0; static const long X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED = 0; static const long X509_V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256 = 0; -static const long X509_V_ERR_HOSTNAME_MISMATCH = 0; -static const long X509_V_ERR_EMAIL_MISMATCH = 0; -static const long X509_V_ERR_IP_ADDRESS_MISMATCH = 0; -#endif - -/* OpenSSL 1.0.2+ verification parameters */ -#if OPENSSL_VERSION_NUMBER >= 0x10002000L && !defined(LIBRESSL_VERSION_NUMBER) -static const long Cryptography_HAS_102_VERIFICATION_PARAMS = 1; -#else -static const long Cryptography_HAS_102_VERIFICATION_PARAMS = 0; -/* X509_V_FLAG_TRUSTED_FIRST is also new in 1.0.2+, but it is added separately - below because it shows up in some earlier 3rd party OpenSSL packages. */ static const long X509_V_FLAG_SUITEB_128_LOS_ONLY = 0; static const long X509_V_FLAG_SUITEB_192_LOS = 0; static const long X509_V_FLAG_SUITEB_128_LOS = 0; - -int (*X509_VERIFY_PARAM_set1_host)(X509_VERIFY_PARAM *, const char *, - size_t) = NULL; -int (*X509_VERIFY_PARAM_set1_email)(X509_VERIFY_PARAM *, const char *, - size_t) = NULL; -int (*X509_VERIFY_PARAM_set1_ip)(X509_VERIFY_PARAM *, const unsigned char *, - size_t) = NULL; -int (*X509_VERIFY_PARAM_set1_ip_asc)(X509_VERIFY_PARAM *, const char *) = NULL; -void (*X509_VERIFY_PARAM_set_hostflags)(X509_VERIFY_PARAM *, - unsigned int) = NULL; #endif -/* OpenSSL 1.0.2+ or Solaris's backport */ -#ifdef X509_V_FLAG_PARTIAL_CHAIN -static const long Cryptography_HAS_X509_V_FLAG_PARTIAL_CHAIN = 1; -#else -static const long Cryptography_HAS_X509_V_FLAG_PARTIAL_CHAIN = 0; -static const long X509_V_FLAG_PARTIAL_CHAIN = 0; +#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 || CRYPTOGRAPHY_IS_LIBRESSL +static const long Cryptography_HAS_110_VERIFICATION_PARAMS = 0; +#ifndef X509_CHECK_FLAG_NEVER_CHECK_SUBJECT +static const long X509_CHECK_FLAG_NEVER_CHECK_SUBJECT = 0; #endif - -/* OpenSSL 1.0.2+, *or* Fedora 20's flavor of OpenSSL 1.0.1e... */ -#ifdef X509_V_FLAG_TRUSTED_FIRST -static const long Cryptography_HAS_X509_V_FLAG_TRUSTED_FIRST = 1; #else -static const long Cryptography_HAS_X509_V_FLAG_TRUSTED_FIRST = 0; -static const long X509_V_FLAG_TRUSTED_FIRST = 0; +static const long Cryptography_HAS_110_VERIFICATION_PARAMS = 1; #endif -/* OpenSSL 1.0.0+ verification error codes */ -#if OPENSSL_VERSION_NUMBER >= 0x10000000L -static const long Cryptography_HAS_100_VERIFICATION_ERROR_CODES = 1; -#else -static const long Cryptography_HAS_100_VERIFICATION_ERROR_CODES = 0; -static const long X509_V_ERR_DIFFERENT_CRL_SCOPE = 0; -static const long X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE = 0; -static const long X509_V_ERR_PERMITTED_VIOLATION = 0; -static const long X509_V_ERR_EXCLUDED_VIOLATION = 0; -static const long X509_V_ERR_SUBTREE_MINMAX = 0; -static const long X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE = 0; -static const long X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX = 0; -static const long X509_V_ERR_UNSUPPORTED_NAME_SYNTAX = 0; -static const long X509_V_ERR_CRL_PATH_VALIDATION_ERROR = 0; -#endif +#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 && !CRYPTOGRAPHY_IS_LIBRESSL +Cryptography_STACK_OF_X509_OBJECT *X509_STORE_get0_objects(X509_STORE *ctx) { + return ctx->objs; +} +X509_VERIFY_PARAM *X509_STORE_get0_param(X509_STORE *store) { + return store->param; +} +int X509_OBJECT_get_type(const X509_OBJECT *x) { + return x->type; +} -/* OpenSSL 1.0.0+ verification parameters */ -#if OPENSSL_VERSION_NUMBER >= 0x10000000L -static const long Cryptography_HAS_100_VERIFICATION_PARAMS = 1; -#else -static const long Cryptography_HAS_100_VERIFICATION_PARAMS = 0; -static const long X509_V_FLAG_EXTENDED_CRL_SUPPORT = 0; -static const long X509_V_FLAG_USE_DELTAS = 0; +/* from x509/x509_vfy.c */ +X509 *X509_STORE_CTX_get0_cert(X509_STORE_CTX *ctx) +{ + return ctx->cert; +} + +X509 *X509_OBJECT_get0_X509(X509_OBJECT *x) { + return x->data.x509; +} #endif -/* OpenSSL 0.9.8recent+ */ -#ifdef X509_V_FLAG_CHECK_SS_SIGNATURE -static const long Cryptography_HAS_X509_V_FLAG_CHECK_SS_SIGNATURE = 1; +#if CRYPTOGRAPHY_OPENSSL_LESS_THAN_110 +static const long Cryptography_HAS_X509_STORE_CTX_GET_ISSUER = 0; +typedef void *X509_STORE_CTX_get_issuer_fn; +X509_STORE_CTX_get_issuer_fn (*X509_STORE_get_get_issuer)(X509_STORE *) = NULL; +void (*X509_STORE_set_get_issuer)(X509_STORE *, + X509_STORE_CTX_get_issuer_fn) = NULL; #else -static const long Cryptography_HAS_X509_V_FLAG_CHECK_SS_SIGNATURE = 0; -static const long X509_V_FLAG_CHECK_SS_SIGNATURE = 0; +static const long Cryptography_HAS_X509_STORE_CTX_GET_ISSUER = 1; #endif """ - -CONDITIONAL_NAMES = { - "Cryptography_HAS_102_VERIFICATION_ERROR_CODES": [ - 'X509_V_ERR_SUITE_B_INVALID_VERSION', - 'X509_V_ERR_SUITE_B_INVALID_ALGORITHM', - 'X509_V_ERR_SUITE_B_INVALID_CURVE', - 'X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM', - 'X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED', - 'X509_V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256', - 'X509_V_ERR_HOSTNAME_MISMATCH', - 'X509_V_ERR_EMAIL_MISMATCH', - 'X509_V_ERR_IP_ADDRESS_MISMATCH' - ], - "Cryptography_HAS_102_VERIFICATION_PARAMS": [ - "X509_V_FLAG_SUITEB_128_LOS_ONLY", - "X509_V_FLAG_SUITEB_192_LOS", - "X509_V_FLAG_SUITEB_128_LOS", - "X509_VERIFY_PARAM_set1_host", - "X509_VERIFY_PARAM_set1_email", - "X509_VERIFY_PARAM_set1_ip", - "X509_VERIFY_PARAM_set1_ip_asc", - "X509_VERIFY_PARAM_set_hostflags", - ], - "Cryptography_HAS_X509_V_FLAG_TRUSTED_FIRST": [ - "X509_V_FLAG_TRUSTED_FIRST", - ], - "Cryptography_HAS_X509_V_FLAG_PARTIAL_CHAIN": [ - "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", - ] -} diff --git a/src/_cffi_src/openssl/x509name.py b/src/_cffi_src/openssl/x509name.py index be5b3a75..f88c8b06 100644 --- a/src/_cffi_src/openssl/x509name.py +++ b/src/_cffi_src/openssl/x509name.py @@ -15,49 +15,73 @@ typedef STACK_OF(X509_NAME_ENTRY) Cryptography_STACK_OF_X509_NAME_ENTRY; """ TYPES = """ +typedef ... Cryptography_STACK_OF_X509_NAME_ENTRY; typedef ... X509_NAME; typedef ... X509_NAME_ENTRY; typedef ... Cryptography_STACK_OF_X509_NAME; -typedef ... Cryptography_STACK_OF_X509_NAME_ENTRY; """ FUNCTIONS = """ X509_NAME *X509_NAME_new(void); void X509_NAME_free(X509_NAME *); -int X509_NAME_entry_count(X509_NAME *); -X509_NAME_ENTRY *X509_NAME_get_entry(X509_NAME *, int); -ASN1_OBJECT *X509_NAME_ENTRY_get_object(X509_NAME_ENTRY *); -ASN1_STRING *X509_NAME_ENTRY_get_data(X509_NAME_ENTRY *); unsigned long X509_NAME_hash(X509_NAME *); int i2d_X509_NAME(X509_NAME *, unsigned char **); int X509_NAME_add_entry_by_txt(X509_NAME *, const char *, int, const unsigned char *, int, int, int); -int X509_NAME_add_entry_by_OBJ(X509_NAME *, ASN1_OBJECT *, int, - unsigned char *, int, int, int); -int X509_NAME_add_entry_by_NID(X509_NAME *, int, int, unsigned char *, - int, int, int); X509_NAME_ENTRY *X509_NAME_delete_entry(X509_NAME *, int); void X509_NAME_ENTRY_free(X509_NAME_ENTRY *); int X509_NAME_get_index_by_NID(X509_NAME *, int, int); int X509_NAME_cmp(const X509_NAME *, const X509_NAME *); -char *X509_NAME_oneline(X509_NAME *, char *, int); X509_NAME *X509_NAME_dup(X509_NAME *); -""" +int Cryptography_X509_NAME_ENTRY_set(X509_NAME_ENTRY *); +/* These became const X509_NAME * in 1.1.0 */ +int X509_NAME_entry_count(X509_NAME *); +X509_NAME_ENTRY *X509_NAME_get_entry(X509_NAME *, int); +char *X509_NAME_oneline(X509_NAME *, char *, int); +int X509_NAME_print_ex(BIO *, X509_NAME *, int, unsigned long); + +/* These became const X509_NAME_ENTRY * in 1.1.0 */ +ASN1_OBJECT *X509_NAME_ENTRY_get_object(X509_NAME_ENTRY *); +ASN1_STRING *X509_NAME_ENTRY_get_data(X509_NAME_ENTRY *); +int X509_NAME_add_entry(X509_NAME *, X509_NAME_ENTRY *, int, int); + +/* this became const unsigned char * in 1.1.0 */ +int X509_NAME_add_entry_by_NID(X509_NAME *, int, int, unsigned char *, + int, int, int); + +/* These became const ASN1_OBJECT * in 1.1.0 */ +X509_NAME_ENTRY *X509_NAME_ENTRY_create_by_OBJ(X509_NAME_ENTRY **, + ASN1_OBJECT *, int, + const unsigned char *, int); +int X509_NAME_add_entry_by_OBJ(X509_NAME *, ASN1_OBJECT *, int, + unsigned char *, int, int, int); -MACROS = """ Cryptography_STACK_OF_X509_NAME *sk_X509_NAME_new_null(void); int sk_X509_NAME_num(Cryptography_STACK_OF_X509_NAME *); int sk_X509_NAME_push(Cryptography_STACK_OF_X509_NAME *, X509_NAME *); X509_NAME *sk_X509_NAME_value(Cryptography_STACK_OF_X509_NAME *, int); void sk_X509_NAME_free(Cryptography_STACK_OF_X509_NAME *); int sk_X509_NAME_ENTRY_num(Cryptography_STACK_OF_X509_NAME_ENTRY *); +Cryptography_STACK_OF_X509_NAME_ENTRY *sk_X509_NAME_ENTRY_new_null(void); +int sk_X509_NAME_ENTRY_push(Cryptography_STACK_OF_X509_NAME_ENTRY *, + X509_NAME_ENTRY *); X509_NAME_ENTRY *sk_X509_NAME_ENTRY_value( Cryptography_STACK_OF_X509_NAME_ENTRY *, int); +Cryptography_STACK_OF_X509_NAME_ENTRY *sk_X509_NAME_ENTRY_dup( + Cryptography_STACK_OF_X509_NAME_ENTRY * +); """ CUSTOMIZATIONS = """ +#if CRYPTOGRAPHY_OPENSSL_110_OR_GREATER +int Cryptography_X509_NAME_ENTRY_set(X509_NAME_ENTRY *ne) { + return X509_NAME_ENTRY_set(ne); +} +#else +int Cryptography_X509_NAME_ENTRY_set(X509_NAME_ENTRY *ne) { + return ne->set; +} +#endif """ - -CONDITIONAL_NAMES = {} diff --git a/src/_cffi_src/openssl/x509v3.py b/src/_cffi_src/openssl/x509v3.py index 166fa59d..59681206 100644 --- a/src/_cffi_src/openssl/x509v3.py +++ b/src/_cffi_src/openssl/x509v3.py @@ -14,11 +14,8 @@ INCLUDES = """ * together with another opaque typedef for the same name in the TYPES section. * Note that the result is an opaque type. */ -#if OPENSSL_VERSION_NUMBER >= 0x10000000 typedef LHASH_OF(CONF_VALUE) Cryptography_LHASH_OF_CONF_VALUE; -#else -typedef LHASH Cryptography_LHASH_OF_CONF_VALUE; -#endif + typedef STACK_OF(ACCESS_DESCRIPTION) Cryptography_STACK_OF_ACCESS_DESCRIPTION; typedef STACK_OF(DIST_POINT) Cryptography_STACK_OF_DIST_POINT; typedef STACK_OF(POLICYQUALINFO) Cryptography_STACK_OF_POLICYQUALINFO; @@ -33,6 +30,8 @@ typedef ... Cryptography_STACK_OF_POLICYQUALINFO; typedef ... Cryptography_STACK_OF_POLICYINFO; typedef ... Cryptography_STACK_OF_ASN1_INTEGER; typedef ... Cryptography_STACK_OF_GENERAL_SUBTREE; +typedef ... EXTENDED_KEY_USAGE; +typedef ... CONF; typedef struct { X509 *issuer_cert; @@ -42,12 +41,6 @@ typedef struct { typedef void * (*X509V3_EXT_D2I)(void *, const unsigned char **, long); -typedef struct { - ASN1_ITEM_EXP *it; - X509V3_EXT_D2I d2i; - ...; -} X509V3_EXT_METHOD; - static const int GEN_OTHERNAME; static const int GEN_EMAIL; static const int GEN_X400; @@ -59,7 +52,8 @@ static const int GEN_IPADD; static const int GEN_RID; typedef struct { - ...; + ASN1_OBJECT *type_id; + ASN1_TYPE *value; } OTHERNAME; typedef struct { @@ -76,6 +70,11 @@ typedef struct { Cryptography_STACK_OF_GENERAL_SUBTREE *excludedSubtrees; } NAME_CONSTRAINTS; +typedef struct { + ASN1_INTEGER *requireExplicitPolicy; + ASN1_INTEGER *inhibitPolicyMapping; +} POLICY_CONSTRAINTS; + typedef struct { int type; @@ -143,6 +142,15 @@ typedef struct { } DIST_POINT; typedef struct { + DIST_POINT_NAME *distpoint; + int onlyuser; + int onlyCA; + ASN1_BIT_STRING *onlysomereasons; + int indirectCRL; + int onlyattr; +} ISSUING_DIST_POINT; + +typedef struct { ASN1_STRING *organization; Cryptography_STACK_OF_ASN1_INTEGER *noticenos; } NOTICEREF; @@ -165,35 +173,55 @@ typedef struct { ASN1_OBJECT *policyid; Cryptography_STACK_OF_POLICYQUALINFO *qualifiers; } POLICYINFO; + +typedef void (*sk_GENERAL_NAME_freefunc)(GENERAL_NAME *); +typedef void (*sk_DIST_POINT_freefunc)(DIST_POINT *); +typedef void (*sk_POLICYINFO_freefunc)(POLICYINFO *); +typedef void (*sk_ACCESS_DESCRIPTION_freefunc)(ACCESS_DESCRIPTION *); """ FUNCTIONS = """ int X509V3_EXT_add_alias(int, int); void X509V3_set_ctx(X509V3_CTX *, X509 *, X509 *, X509_REQ *, X509_CRL *, int); -X509_EXTENSION *X509V3_EXT_nconf(CONF *, X509V3_CTX *, char *, char *); int GENERAL_NAME_print(BIO *, GENERAL_NAME *); +GENERAL_NAMES *GENERAL_NAMES_new(void); void GENERAL_NAMES_free(GENERAL_NAMES *); void *X509V3_EXT_d2i(X509_EXTENSION *); -""" - -MACROS = """ +int X509_check_ca(X509 *); +/* X509 became a const arg in 1.1.0 */ +void *X509_get_ext_d2i(X509 *, int, int *, int *); +/* The last two char * args became const char * in 1.1.0 */ +X509_EXTENSION *X509V3_EXT_nconf(CONF *, X509V3_CTX *, char *, char *); /* This is a macro defined by a call to DECLARE_ASN1_FUNCTIONS in the x509v3.h header. */ -int i2d_BASIC_CONSTRAINTS(BASIC_CONSTRAINTS *, unsigned char **); BASIC_CONSTRAINTS *BASIC_CONSTRAINTS_new(void); void BASIC_CONSTRAINTS_free(BASIC_CONSTRAINTS *); /* This is a macro defined by a call to DECLARE_ASN1_FUNCTIONS in the x509v3.h header. */ +AUTHORITY_KEYID *AUTHORITY_KEYID_new(void); void AUTHORITY_KEYID_free(AUTHORITY_KEYID *); NAME_CONSTRAINTS *NAME_CONSTRAINTS_new(void); void NAME_CONSTRAINTS_free(NAME_CONSTRAINTS *); +OTHERNAME *OTHERNAME_new(void); +void OTHERNAME_free(OTHERNAME *); + +POLICY_CONSTRAINTS *POLICY_CONSTRAINTS_new(void); +void POLICY_CONSTRAINTS_free(POLICY_CONSTRAINTS *); + void *X509V3_set_ctx_nodb(X509V3_CTX *); + +int i2d_GENERAL_NAMES(GENERAL_NAMES *, unsigned char **); +GENERAL_NAMES *d2i_GENERAL_NAMES(GENERAL_NAMES **, const unsigned char **, + long); + int sk_GENERAL_NAME_num(struct stack_st_GENERAL_NAME *); int sk_GENERAL_NAME_push(struct stack_st_GENERAL_NAME *, GENERAL_NAME *); GENERAL_NAME *sk_GENERAL_NAME_value(struct stack_st_GENERAL_NAME *, int); +void sk_GENERAL_NAME_pop_free(struct stack_st_GENERAL_NAME *, + sk_GENERAL_NAME_freefunc); Cryptography_STACK_OF_ACCESS_DESCRIPTION *sk_ACCESS_DESCRIPTION_new_null(void); int sk_ACCESS_DESCRIPTION_num(Cryptography_STACK_OF_ACCESS_DESCRIPTION *); @@ -201,29 +229,56 @@ ACCESS_DESCRIPTION *sk_ACCESS_DESCRIPTION_value( Cryptography_STACK_OF_ACCESS_DESCRIPTION *, int ); void sk_ACCESS_DESCRIPTION_free(Cryptography_STACK_OF_ACCESS_DESCRIPTION *); +void sk_ACCESS_DESCRIPTION_pop_free(Cryptography_STACK_OF_ACCESS_DESCRIPTION *, + sk_ACCESS_DESCRIPTION_freefunc); int sk_ACCESS_DESCRIPTION_push(Cryptography_STACK_OF_ACCESS_DESCRIPTION *, ACCESS_DESCRIPTION *); +ACCESS_DESCRIPTION *ACCESS_DESCRIPTION_new(void); +void ACCESS_DESCRIPTION_free(ACCESS_DESCRIPTION *); + X509_EXTENSION *X509V3_EXT_conf_nid(Cryptography_LHASH_OF_CONF_VALUE *, X509V3_CTX *, int, char *); -/* These aren't macros these functions are all const X on openssl > 1.0.x */ -const X509V3_EXT_METHOD *X509V3_EXT_get(X509_EXTENSION *); -const X509V3_EXT_METHOD *X509V3_EXT_get_nid(int); - +Cryptography_STACK_OF_DIST_POINT *sk_DIST_POINT_new_null(void); void sk_DIST_POINT_free(Cryptography_STACK_OF_DIST_POINT *); int sk_DIST_POINT_num(Cryptography_STACK_OF_DIST_POINT *); DIST_POINT *sk_DIST_POINT_value(Cryptography_STACK_OF_DIST_POINT *, int); +int sk_DIST_POINT_push(Cryptography_STACK_OF_DIST_POINT *, DIST_POINT *); +void sk_DIST_POINT_pop_free(Cryptography_STACK_OF_DIST_POINT *, + sk_DIST_POINT_freefunc); +void CRL_DIST_POINTS_free(Cryptography_STACK_OF_DIST_POINT *); void sk_POLICYINFO_free(Cryptography_STACK_OF_POLICYINFO *); int sk_POLICYINFO_num(Cryptography_STACK_OF_POLICYINFO *); POLICYINFO *sk_POLICYINFO_value(Cryptography_STACK_OF_POLICYINFO *, int); +int sk_POLICYINFO_push(Cryptography_STACK_OF_POLICYINFO *, POLICYINFO *); +Cryptography_STACK_OF_POLICYINFO *sk_POLICYINFO_new_null(void); +void sk_POLICYINFO_pop_free(Cryptography_STACK_OF_POLICYINFO *, + sk_POLICYINFO_freefunc); +void CERTIFICATEPOLICIES_free(Cryptography_STACK_OF_POLICYINFO *); + +POLICYINFO *POLICYINFO_new(void); +void POLICYINFO_free(POLICYINFO *); + +POLICYQUALINFO *POLICYQUALINFO_new(void); +void POLICYQUALINFO_free(POLICYQUALINFO *); + +NOTICEREF *NOTICEREF_new(void); +void NOTICEREF_free(NOTICEREF *); + +USERNOTICE *USERNOTICE_new(void); +void USERNOTICE_free(USERNOTICE *); void sk_POLICYQUALINFO_free(Cryptography_STACK_OF_POLICYQUALINFO *); int sk_POLICYQUALINFO_num(Cryptography_STACK_OF_POLICYQUALINFO *); POLICYQUALINFO *sk_POLICYQUALINFO_value(Cryptography_STACK_OF_POLICYQUALINFO *, int); +int sk_POLICYQUALINFO_push(Cryptography_STACK_OF_POLICYQUALINFO *, + POLICYQUALINFO *); +Cryptography_STACK_OF_POLICYQUALINFO *sk_POLICYQUALINFO_new_null(void); +Cryptography_STACK_OF_GENERAL_SUBTREE *sk_GENERAL_SUBTREE_new_null(void); void sk_GENERAL_SUBTREE_free(Cryptography_STACK_OF_GENERAL_SUBTREE *); int sk_GENERAL_SUBTREE_num(Cryptography_STACK_OF_GENERAL_SUBTREE *); GENERAL_SUBTREE *sk_GENERAL_SUBTREE_value( @@ -232,14 +287,28 @@ GENERAL_SUBTREE *sk_GENERAL_SUBTREE_value( int sk_GENERAL_SUBTREE_push(Cryptography_STACK_OF_GENERAL_SUBTREE *, GENERAL_SUBTREE *); +GENERAL_SUBTREE *GENERAL_SUBTREE_new(void); + void sk_ASN1_INTEGER_free(Cryptography_STACK_OF_ASN1_INTEGER *); int sk_ASN1_INTEGER_num(Cryptography_STACK_OF_ASN1_INTEGER *); ASN1_INTEGER *sk_ASN1_INTEGER_value(Cryptography_STACK_OF_ASN1_INTEGER *, int); +int sk_ASN1_INTEGER_push(Cryptography_STACK_OF_ASN1_INTEGER *, ASN1_INTEGER *); +Cryptography_STACK_OF_ASN1_INTEGER *sk_ASN1_INTEGER_new_null(void); X509_EXTENSION *X509V3_EXT_i2d(int, int, void *); + +DIST_POINT *DIST_POINT_new(void); +void DIST_POINT_free(DIST_POINT *); + +DIST_POINT_NAME *DIST_POINT_NAME_new(void); +void DIST_POINT_NAME_free(DIST_POINT_NAME *); + +GENERAL_NAME *GENERAL_NAME_new(void); +void GENERAL_NAME_free(GENERAL_NAME *); + +ISSUING_DIST_POINT *ISSUING_DIST_POINT_new(void); +void ISSUING_DIST_POINT_free(ISSUING_DIST_POINT *); """ CUSTOMIZATIONS = """ """ - -CONDITIONAL_NAMES = {} diff --git a/src/_cffi_src/utils.py b/src/_cffi_src/utils.py index 65f9f120..eecd6ea1 100644 --- a/src/_cffi_src/utils.py +++ b/src/_cffi_src/utils.py @@ -4,61 +4,53 @@ from __future__ import absolute_import, division, print_function +import os import sys +from distutils.ccompiler import new_compiler +from distutils.dist import Distribution from cffi import FFI -def build_ffi_for_binding(module_name, module_prefix, modules, pre_include="", - post_include="", libraries=[], extra_compile_args=[], - extra_link_args=[]): +# Load the cryptography __about__ to get the current package version +base_src = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +about = {} +with open(os.path.join(base_src, "cryptography", "__about__.py")) as f: + exec(f.read(), about) + + +def build_ffi_for_binding(module_name, module_prefix, modules, libraries=[], + extra_compile_args=[], extra_link_args=[]): """ Modules listed in ``modules`` should have the following attributes: * ``INCLUDES``: A string containing C includes. * ``TYPES``: A string containing C declarations for types. - * ``FUNCTIONS``: A string containing C declarations for functions. - * ``MACROS``: A string containing C declarations for any macros. + * ``FUNCTIONS``: A string containing C declarations for functions & macros. * ``CUSTOMIZATIONS``: A string containing arbitrary top-level C code, this can be used to do things like test for a define and provide an alternate implementation based on that. - * ``CONDITIONAL_NAMES``: A dict mapping strings of condition names from the - library to a list of names which will not be present without the - condition. """ types = [] includes = [] functions = [] - macros = [] customizations = [] for name in modules: __import__(module_prefix + name) module = sys.modules[module_prefix + name] types.append(module.TYPES) - macros.append(module.MACROS) functions.append(module.FUNCTIONS) includes.append(module.INCLUDES) customizations.append(module.CUSTOMIZATIONS) - # We include functions here so that if we got any of their definitions - # wrong, the underlying C compiler will explode. In C you are allowed - # to re-declare a function if it has the same signature. That is: - # int foo(int); - # int foo(int); - # is legal, but the following will fail to compile: - # int foo(int); - # int foo(short); verify_source = "\n".join( - [pre_include] + includes + - [post_include] + - functions + customizations ) ffi = build_ffi( module_name, - cdef_source="\n".join(types + functions + macros), + cdef_source="\n".join(types + functions), verify_source=verify_source, libraries=libraries, extra_compile_args=extra_compile_args, @@ -71,6 +63,11 @@ def build_ffi_for_binding(module_name, module_prefix, modules, pre_include="", def build_ffi(module_name, cdef_source, verify_source, libraries=[], extra_compile_args=[], extra_link_args=[]): ffi = FFI() + # Always add the CRYPTOGRAPHY_PACKAGE_VERSION to the shared object + cdef_source += "\nstatic const char *const CRYPTOGRAPHY_PACKAGE_VERSION;" + verify_source += '\n#define CRYPTOGRAPHY_PACKAGE_VERSION "{}"'.format( + about["__version__"] + ) ffi.cdef(cdef_source) ffi.set_source( module_name, @@ -82,10 +79,23 @@ def build_ffi(module_name, cdef_source, verify_source, libraries=[], return ffi -def extra_link_args(platform): - if platform != "win32": - return [] +def extra_link_args(compiler_type): + if compiler_type == 'msvc': + # Enable NX and ASLR for Windows builds on MSVC. These are enabled by + # default on Python 3.3+ but not on 2.x. + return ['/NXCOMPAT', '/DYNAMICBASE'] else: - # Enable NX and ASLR for Windows builds. These are enabled by default - # on Python 3.3+ but not on 2.x. - return ["/NXCOMPAT", "/DYNAMICBASE"] + return [] + + +def compiler_type(): + """ + Gets the compiler type from distutils. On Windows with MSVC it will be + "msvc". On macOS and linux it is "unix". + """ + dist = Distribution() + dist.parse_config_files() + cmd = dist.get_command_obj('build') + cmd.ensure_finalized() + compiler = new_compiler(compiler=cmd.compiler) + return compiler.compiler_type diff --git a/src/cryptography/__about__.py b/src/cryptography/__about__.py index 9c28be2e..92d5042e 100644 --- a/src/cryptography/__about__.py +++ b/src/cryptography/__about__.py @@ -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.0.dev1" +__version__ = "3.0.dev1" __author__ = "The cryptography developers" __email__ = "cryptography-dev@python.org" __license__ = "BSD or Apache License, Version 2.0" -__copyright__ = "Copyright 2013-2015 {0}".format(__author__) +__copyright__ = "Copyright 2013-2019 {}".format(__author__) diff --git a/src/cryptography/__init__.py b/src/cryptography/__init__.py index 985ebd6f..bd2a8102 100644 --- a/src/cryptography/__init__.py +++ b/src/cryptography/__init__.py @@ -11,6 +11,7 @@ from cryptography.__about__ import ( __author__, __copyright__, __email__, __license__, __summary__, __title__, __uri__, __version__ ) +from cryptography.utils import CryptographyDeprecationWarning __all__ = [ @@ -18,9 +19,10 @@ __all__ = [ "__email__", "__license__", "__copyright__", ] -if sys.version_info[:2] == (2, 6): +if sys.version_info[0] == 2: warnings.warn( - "Python 2.6 is no longer supported by the Python core team, please " - "upgrade your Python.", - DeprecationWarning + "Python 2 is no longer supported by the Python core team. Support for " + "it is now deprecated in cryptography, and will be removed in a " + "future release.", + CryptographyDeprecationWarning ) diff --git a/src/cryptography/exceptions.py b/src/cryptography/exceptions.py index a4292eb8..1d52d7dc 100644 --- a/src/cryptography/exceptions.py +++ b/src/cryptography/exceptions.py @@ -6,9 +6,6 @@ from __future__ import absolute_import, division, print_function from enum import Enum -from cryptography import utils -from cryptography.hazmat.primitives import twofactor - class _Reasons(Enum): BACKEND_MISSING_INTERFACE = 0 @@ -20,6 +17,9 @@ class _Reasons(Enum): UNSUPPORTED_ELLIPTIC_CURVE = 6 UNSUPPORTED_SERIALIZATION = 7 UNSUPPORTED_X509 = 8 + UNSUPPORTED_EXCHANGE_ALGORITHM = 9 + UNSUPPORTED_DIFFIE_HELLMAN = 10 + UNSUPPORTED_MAC = 11 class UnsupportedAlgorithm(Exception): @@ -49,19 +49,10 @@ class InvalidSignature(Exception): class InternalError(Exception): - pass + def __init__(self, msg, err_code): + super(InternalError, self).__init__(msg) + self.err_code = err_code class InvalidKey(Exception): pass - - -InvalidToken = utils.deprecated( - twofactor.InvalidToken, - __name__, - ( - "The InvalidToken exception has moved to the " - "cryptography.hazmat.primitives.twofactor module" - ), - utils.DeprecatedIn09 -) diff --git a/src/cryptography/fernet.py b/src/cryptography/fernet.py index 6fbe9f27..862d9466 100644 --- a/src/cryptography/fernet.py +++ b/src/cryptography/fernet.py @@ -12,6 +12,7 @@ import time import six +from cryptography import utils from cryptography.exceptions import InvalidSignature from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import hashes, padding @@ -46,13 +47,14 @@ class Fernet(object): return base64.urlsafe_b64encode(os.urandom(32)) def encrypt(self, data): - current_time = int(time.time()) + return self.encrypt_at_time(data, int(time.time())) + + def encrypt_at_time(self, data, current_time): iv = os.urandom(16) return self._encrypt_from_parts(data, current_time, iv) def _encrypt_from_parts(self, data, current_time, iv): - if not isinstance(data, bytes): - raise TypeError("data must be bytes.") + utils._check_bytes("data", data) padder = padding.PKCS7(algorithms.AES.block_size).padder() padded_data = padder.update(data) + padder.finalize() @@ -71,11 +73,21 @@ class Fernet(object): return base64.urlsafe_b64encode(basic_parts + hmac) def decrypt(self, token, ttl=None): - if not isinstance(token, bytes): - raise TypeError("token must be bytes.") + return self.decrypt_at_time(token, ttl, int(time.time())) + + def decrypt_at_time(self, token, ttl, current_time): + timestamp, data = Fernet._get_unverified_token_data(token) + return self._decrypt_data(data, timestamp, ttl, current_time) - current_time = int(time.time()) + 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): + utils._check_bytes("token", token) try: data = base64.urlsafe_b64decode(token) except (TypeError, binascii.Error): @@ -88,11 +100,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 +110,16 @@ class Fernet(object): except InvalidSignature: raise InvalidToken + def _decrypt_data(self, data, timestamp, ttl, current_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( @@ -130,12 +150,32 @@ class MultiFernet(object): self._fernets = fernets def encrypt(self, msg): - return self._fernets[0].encrypt(msg) + return self.encrypt_at_time(msg, int(time.time())) + + def encrypt_at_time(self, msg, current_time): + return self._fernets[0].encrypt_at_time(msg, current_time) + + 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, 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): + return self.decrypt_at_time(msg, ttl, int(time.time())) + + def decrypt_at_time(self, msg, ttl, current_time): for f in self._fernets: try: - return f.decrypt(msg, ttl) + return f.decrypt_at_time(msg, ttl, current_time) except InvalidToken: pass raise InvalidToken diff --git a/src/cryptography/hazmat/__init__.py b/src/cryptography/hazmat/__init__.py index 4b540884..9f06a994 100644 --- a/src/cryptography/hazmat/__init__.py +++ b/src/cryptography/hazmat/__init__.py @@ -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 diff --git a/src/cryptography/hazmat/_der.py b/src/cryptography/hazmat/_der.py new file mode 100644 index 00000000..51518d64 --- /dev/null +++ b/src/cryptography/hazmat/_der.py @@ -0,0 +1,156 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import absolute_import, division, print_function + +import six + +from cryptography.utils import int_from_bytes, int_to_bytes + + +# This module contains a lightweight DER encoder and decoder. See X.690 for the +# specification. This module intentionally does not implement the more complex +# BER encoding, only DER. +# +# Note this implementation treats an element's constructed bit as part of the +# tag. This is fine for DER, where the bit is always computable from the type. + + +CONSTRUCTED = 0x20 +CONTEXT_SPECIFIC = 0x80 + +INTEGER = 0x02 +BIT_STRING = 0x03 +OCTET_STRING = 0x04 +NULL = 0x05 +OBJECT_IDENTIFIER = 0x06 +SEQUENCE = 0x10 | CONSTRUCTED +SET = 0x11 | CONSTRUCTED +PRINTABLE_STRING = 0x13 +UTC_TIME = 0x17 +GENERALIZED_TIME = 0x18 + + +class DERReader(object): + def __init__(self, data): + self.data = memoryview(data) + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, tb): + if exc_value is None: + self.check_empty() + + def is_empty(self): + return len(self.data) == 0 + + def check_empty(self): + if not self.is_empty(): + raise ValueError("Invalid DER input: trailing data") + + def read_byte(self): + if len(self.data) < 1: + raise ValueError("Invalid DER input: insufficient data") + ret = six.indexbytes(self.data, 0) + self.data = self.data[1:] + return ret + + def read_bytes(self, n): + if len(self.data) < n: + raise ValueError("Invalid DER input: insufficient data") + ret = self.data[:n] + self.data = self.data[n:] + return ret + + def read_any_element(self): + tag = self.read_byte() + # Tag numbers 31 or higher are stored in multiple bytes. No supported + # ASN.1 types use such tags, so reject these. + if tag & 0x1f == 0x1f: + raise ValueError("Invalid DER input: unexpected high tag number") + length_byte = self.read_byte() + if length_byte & 0x80 == 0: + # If the high bit is clear, the first length byte is the length. + length = length_byte + else: + # If the high bit is set, the first length byte encodes the length + # of the length. + length_byte &= 0x7f + if length_byte == 0: + raise ValueError( + "Invalid DER input: indefinite length form is not allowed " + "in DER" + ) + length = 0 + for i in range(length_byte): + length <<= 8 + length |= self.read_byte() + if length == 0: + raise ValueError( + "Invalid DER input: length was not minimally-encoded" + ) + if length < 0x80: + # If the length could have been encoded in short form, it must + # not use long form. + raise ValueError( + "Invalid DER input: length was not minimally-encoded" + ) + body = self.read_bytes(length) + return tag, DERReader(body) + + def read_element(self, expected_tag): + tag, body = self.read_any_element() + if tag != expected_tag: + raise ValueError("Invalid DER input: unexpected tag") + return body + + def read_single_element(self, expected_tag): + with self: + return self.read_element(expected_tag) + + def read_optional_element(self, expected_tag): + if len(self.data) > 0 and six.indexbytes(self.data, 0) == expected_tag: + return self.read_element(expected_tag) + return None + + def as_integer(self): + if len(self.data) == 0: + raise ValueError("Invalid DER input: empty integer contents") + first = six.indexbytes(self.data, 0) + if first & 0x80 == 0x80: + raise ValueError("Negative DER integers are not supported") + # The first 9 bits must not all be zero or all be ones. Otherwise, the + # encoding should have been one byte shorter. + if len(self.data) > 1: + second = six.indexbytes(self.data, 1) + if first == 0 and second & 0x80 == 0: + raise ValueError( + "Invalid DER input: integer not minimally-encoded" + ) + return int_from_bytes(self.data, "big") + + +def encode_der_integer(x): + if not isinstance(x, six.integer_types): + raise ValueError("Value must be an integer") + if x < 0: + raise ValueError("Negative integers are not supported") + n = x.bit_length() // 8 + 1 + return int_to_bytes(x, n) + + +def encode_der(tag, *children): + length = 0 + for child in children: + length += len(child) + chunks = [six.int2byte(tag)] + if length < 0x80: + chunks.append(six.int2byte(length)) + else: + length_bytes = int_to_bytes(length) + chunks.append(six.int2byte(0x80 | len(length_bytes))) + chunks.append(length_bytes) + chunks.extend(children) + return b"".join(chunks) diff --git a/src/cryptography/hazmat/_oid.py b/src/cryptography/hazmat/_oid.py new file mode 100644 index 00000000..f98912f9 --- /dev/null +++ b/src/cryptography/hazmat/_oid.py @@ -0,0 +1,72 @@ +# 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: + node_value = int(node, 10) + except ValueError: + raise ValueError( + "Malformed OID: %s (non-integer nodes)" % ( + self._dotted_string)) + if node_value < 0: + raise ValueError( + "Malformed OID: %s (negative-integer nodes)" % ( + self._dotted_string)) + intnodes.append(node_value) + + if len(nodes) < 2: + raise ValueError( + "Malformed OID: %s (insufficient number of nodes)" % ( + self._dotted_string)) + + 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={}, name={})>".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") diff --git a/src/cryptography/hazmat/backends/__init__.py b/src/cryptography/hazmat/backends/__init__.py index 256fee39..565bde78 100644 --- a/src/cryptography/hazmat/backends/__init__.py +++ b/src/cryptography/hazmat/backends/__init__.py @@ -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 diff --git a/src/cryptography/hazmat/backends/commoncrypto/backend.py b/src/cryptography/hazmat/backends/commoncrypto/backend.py deleted file mode 100644 index 091fbb7c..00000000 --- a/src/cryptography/hazmat/backends/commoncrypto/backend.py +++ /dev/null @@ -1,244 +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) - ) - - 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() diff --git a/src/cryptography/hazmat/backends/commoncrypto/ciphers.py b/src/cryptography/hazmat/backends/commoncrypto/ciphers.py deleted file mode 100644 index 1ce8aec5..00000000 --- a/src/cryptography/hazmat/backends/commoncrypto/ciphers.py +++ /dev/null @@ -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") diff --git a/src/cryptography/hazmat/backends/commoncrypto/hashes.py b/src/cryptography/hazmat/backends/commoncrypto/hashes.py deleted file mode 100644 index a54e9833..00000000 --- a/src/cryptography/hazmat/backends/commoncrypto/hashes.py +++ /dev/null @@ -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)[:] diff --git a/src/cryptography/hazmat/backends/commoncrypto/hmac.py b/src/cryptography/hazmat/backends/commoncrypto/hmac.py deleted file mode 100644 index ae623d84..00000000 --- a/src/cryptography/hazmat/backends/commoncrypto/hmac.py +++ /dev/null @@ -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.") diff --git a/src/cryptography/hazmat/backends/interfaces.py b/src/cryptography/hazmat/backends/interfaces.py index 4d378e6b..20f4164e 100644 --- a/src/cryptography/hazmat/backends/interfaces.py +++ b/src/cryptography/hazmat/backends/interfaces.py @@ -57,7 +57,7 @@ class HMACBackend(object): @abc.abstractmethod def create_hmac_ctx(self, key, algorithm): """ - Create a MACContext for calculating a message authentication code. + Create a context for calculating a message authentication code. """ @@ -72,7 +72,7 @@ class CMACBackend(object): @abc.abstractmethod def create_cmac_ctx(self, algorithm): """ - Create a MACContext for calculating a message authentication code. + Create a context for calculating a message authentication code. """ @@ -212,7 +212,19 @@ class EllipticCurveBackend(object): @abc.abstractmethod def load_elliptic_curve_private_numbers(self, numbers): """ - Return an EllipticCurvePublicKey provider using the given numbers. + Return an EllipticCurvePrivateKey provider using the given numbers. + """ + + @abc.abstractmethod + def elliptic_curve_exchange_algorithm_supported(self, algorithm, curve): + """ + 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. """ @@ -231,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): @@ -247,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): @@ -280,13 +304,40 @@ class X509Backend(object): Create and sign an X.509 CSR from a CSR builder object. """ + @abc.abstractmethod + def create_x509_certificate(self, builder, private_key, algorithm): + """ + Create and sign an X.509 certificate from a CertificateBuilder object. + """ + + @abc.abstractmethod + def create_x509_crl(self, builder, private_key, algorithm): + """ + Create and sign an X.509 CertificateRevocationList from a + CertificateRevocationListBuilder object. + """ + + @abc.abstractmethod + def create_x509_revoked_certificate(self, builder): + """ + Create a RevokedCertificate object from a RevokedCertificateBuilder + 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 @@ -297,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): + def dh_parameters_supported(self, p, g, q=None): """ - Returns whether the exchange algorithm is supported by this backend. + Returns whether the backend supports DH with these parameter values. """ @abc.abstractmethod - def dh_parameters_supported(self, p, g): + def dh_x942_serialization_supported(self): """ - Returns whether the backend supports DH with these parameter values. + 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. """ diff --git a/src/cryptography/hazmat/backends/multibackend.py b/src/cryptography/hazmat/backends/multibackend.py deleted file mode 100644 index 6e911fd5..00000000 --- a/src/cryptography/hazmat/backends/multibackend.py +++ /dev/null @@ -1,353 +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 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_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 - ) diff --git a/src/cryptography/hazmat/backends/openssl/aead.py b/src/cryptography/hazmat/backends/openssl/aead.py new file mode 100644 index 00000000..0cad15cc --- /dev/null +++ b/src/cryptography/hazmat/backends/openssl/aead.py @@ -0,0 +1,162 @@ +# 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-{}-ccm".format(len(cipher._key) * 8).encode("ascii") + else: + assert isinstance(cipher, AESGCM) + return "aes-{}-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) + elif cipher_name.endswith(b"-ccm"): + res = backend._lib.EVP_CIPHER_CTX_ctrl( + ctx, backend._lib.EVP_CTRL_AEAD_SET_TAG, tag_len, backend._ffi.NULL + ) + backend.openssl_assert(res != 0) + + nonce_ptr = backend._ffi.from_buffer(nonce) + key_ptr = backend._ffi.from_buffer(key) + res = backend._lib.EVP_CipherInit_ex( + ctx, + backend._ffi.NULL, + backend._ffi.NULL, + key_ptr, + nonce_ptr, + 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 diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index 85f65972..952c0f65 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -4,151 +4,105 @@ from __future__ import absolute_import, division, print_function +import base64 import collections +import contextlib import itertools from contextlib import contextmanager import six +from six.moves import range from cryptography import utils, x509 -from cryptography.exceptions import ( - InternalError, UnsupportedAlgorithm, _Reasons +from cryptography.exceptions import UnsupportedAlgorithm, _Reasons +from cryptography.hazmat._der import ( + INTEGER, NULL, SEQUENCE, encode_der, encode_der_integer ) from cryptography.hazmat.backends.interfaces import ( - CMACBackend, CipherBackend, DERSerializationBackend, DSABackend, + CMACBackend, CipherBackend, DERSerializationBackend, DHBackend, DSABackend, EllipticCurveBackend, HMACBackend, HashBackend, PBKDF2HMACBackend, - PEMSerializationBackend, RSABackend, X509Backend -) -from cryptography.hazmat.backends.openssl.ciphers import ( - _AESCTRCipherContext, _CipherContext + PEMSerializationBackend, RSABackend, ScryptBackend, X509Backend ) +from cryptography.hazmat.backends.openssl import aead +from cryptography.hazmat.backends.openssl.ciphers import _CipherContext from cryptography.hazmat.backends.openssl.cmac import _CMACContext +from cryptography.hazmat.backends.openssl.decode_asn1 import ( + _CRL_ENTRY_REASON_ENUM_TO_CODE +) +from cryptography.hazmat.backends.openssl.dh import ( + _DHParameters, _DHPrivateKey, _DHPublicKey, _dh_params_dup +) from cryptography.hazmat.backends.openssl.dsa import ( _DSAParameters, _DSAPrivateKey, _DSAPublicKey ) from cryptography.hazmat.backends.openssl.ec import ( _EllipticCurvePrivateKey, _EllipticCurvePublicKey ) +from cryptography.hazmat.backends.openssl.ed25519 import ( + _Ed25519PrivateKey, _Ed25519PublicKey +) +from cryptography.hazmat.backends.openssl.ed448 import ( + _ED448_KEY_SIZE, _Ed448PrivateKey, _Ed448PublicKey +) +from cryptography.hazmat.backends.openssl.encode_asn1 import ( + _CRL_ENTRY_EXTENSION_ENCODE_HANDLERS, + _CRL_EXTENSION_ENCODE_HANDLERS, _EXTENSION_ENCODE_HANDLERS, + _OCSP_BASICRESP_EXTENSION_ENCODE_HANDLERS, + _OCSP_REQUEST_EXTENSION_ENCODE_HANDLERS, + _encode_asn1_int_gc, _encode_asn1_str_gc, _encode_name_gc, _txt2obj_gc, +) from cryptography.hazmat.backends.openssl.hashes import _HashContext from cryptography.hazmat.backends.openssl.hmac import _HMACContext +from cryptography.hazmat.backends.openssl.ocsp import ( + _OCSPRequest, _OCSPResponse +) +from cryptography.hazmat.backends.openssl.poly1305 import ( + _POLY1305_KEY_SIZE, _Poly1305Context +) from cryptography.hazmat.backends.openssl.rsa import ( _RSAPrivateKey, _RSAPublicKey ) +from cryptography.hazmat.backends.openssl.x25519 import ( + _X25519PrivateKey, _X25519PublicKey +) +from cryptography.hazmat.backends.openssl.x448 import ( + _X448PrivateKey, _X448PublicKey +) from cryptography.hazmat.backends.openssl.x509 import ( - _Certificate, _CertificateSigningRequest + _Certificate, _CertificateRevocationList, + _CertificateSigningRequest, _RevokedCertificate ) -from cryptography.hazmat.bindings.openssl.binding import Binding +from cryptography.hazmat.bindings.openssl import binding from cryptography.hazmat.primitives import hashes, serialization -from cryptography.hazmat.primitives.asymmetric import dsa, ec, rsa +from cryptography.hazmat.primitives.asymmetric import ( + dsa, ec, ed25519, ed448, rsa +) from cryptography.hazmat.primitives.asymmetric.padding import ( MGF1, OAEP, PKCS1v15, PSS ) from cryptography.hazmat.primitives.ciphers.algorithms import ( - AES, ARC4, Blowfish, CAST5, Camellia, IDEA, SEED, TripleDES + AES, ARC4, Blowfish, CAST5, Camellia, ChaCha20, IDEA, SEED, TripleDES ) from cryptography.hazmat.primitives.ciphers.modes import ( - CBC, CFB, CFB8, CTR, ECB, GCM, OFB + CBC, CFB, CFB8, CTR, ECB, GCM, OFB, XTS ) +from cryptography.hazmat.primitives.kdf import scrypt +from cryptography.hazmat.primitives.serialization import ssh +from cryptography.x509 import ocsp _MemoryBIO = collections.namedtuple("_MemoryBIO", ["bio", "char_ptr"]) -_OpenSSLError = collections.namedtuple("_OpenSSLError", - ["code", "lib", "func", "reason"]) - - -def _encode_asn1_int(backend, x): - """ - Converts a python integer to a 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 a ASN.1 integer. Don't GC -- as documented. - i = backend._lib.BN_to_ASN1_INTEGER(i, backend._ffi.NULL) - assert i != backend._ffi.NULL - return i -def _encode_asn1_str(backend, data, length): - """ - Create an ASN1_OCTET_STRING from a Python byte string. - """ - s = backend._lib.ASN1_OCTET_STRING_new() - s = backend._ffi.gc(s, backend._lib.ASN1_OCTET_STRING_free) - backend._lib.ASN1_OCTET_STRING_set(s, data, length) - return s - - -def _encode_name(backend, attributes): - subject = backend._lib.X509_NAME_new() - subject = backend._ffi.gc(subject, backend._lib.X509_NAME_free) - for attribute in attributes: - value = attribute.value.encode('utf8') - obj = _txt2obj(backend, attribute.oid.dotted_string) - res = backend._lib.X509_NAME_add_entry_by_OBJ( - subject, - obj, - backend._lib.MBSTRING_UTF8, - value, - -1, -1, 0, - ) - assert res == 1 - return subject - - -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) - assert obj != backend._ffi.NULL - obj = backend._ffi.gc(obj, backend._lib.ASN1_OBJECT_free) - return obj - - -def _encode_basic_constraints(backend, basic_constraints, critical): - obj = _txt2obj(backend, x509.OID_BASIC_CONSTRAINTS.dotted_string) - assert obj is not None - constraints = backend._lib.BASIC_CONSTRAINTS_new() - constraints.ca = 255 if basic_constraints.ca else 0 - if basic_constraints.ca: - constraints.pathlen = _encode_asn1_int( - backend, basic_constraints.path_length - ) - - # Fetch the encoded payload. - pp = backend._ffi.new('unsigned char **') - r = backend._lib.i2d_BASIC_CONSTRAINTS(constraints, pp) - assert r > 0 - pp = backend._ffi.gc( - pp, lambda pointer: backend._lib.OPENSSL_free(pointer[0]) - ) - - # Wrap that in an X509 extension object. - extension = backend._lib.X509_EXTENSION_create_by_OBJ( - backend._ffi.NULL, - obj, - 1 if critical else 0, - _encode_asn1_str(backend, pp[0], r), - ) - assert extension != backend._ffi.NULL - - # Return the wrapped extension. - return extension +# Not actually supported, just used as a marker for some serialization tests. +class _RC2(object): + pass @utils.register_interface(CipherBackend) @utils.register_interface(CMACBackend) @utils.register_interface(DERSerializationBackend) +@utils.register_interface(DHBackend) @utils.register_interface(DSABackend) @utils.register_interface(EllipticCurveBackend) @utils.register_interface(HashBackend) @@ -157,6 +111,9 @@ def _encode_basic_constraints(backend, basic_constraints, critical): @utils.register_interface(RSABackend) @utils.register_interface(PEMSerializationBackend) @utils.register_interface(X509Backend) +@utils.register_interface_if( + binding.Binding().lib.Cryptography_HAS_SCRYPT, ScryptBackend +) class Backend(object): """ OpenSSL API binding interfaces. @@ -164,73 +121,110 @@ class Backend(object): name = "openssl" def __init__(self): - self._binding = Binding() + self._binding = binding.Binding() self._ffi = self._binding.ffi self._lib = self._binding.lib - self._binding.init_static_locks() - - # adds all ciphers/digests for EVP - self._lib.OpenSSL_add_all_algorithms() - # registers available SSL/TLS ciphers and digests - self._lib.SSL_library_init() - # loads error strings for libcrypto and libssl functions - self._lib.SSL_load_error_strings() - self._cipher_registry = {} self._register_default_ciphers() self.activate_osrandom_engine() + self._dh_types = [self._lib.EVP_PKEY_DH] + if self._lib.Cryptography_HAS_EVP_PKEY_DHX: + self._dh_types.append(self._lib.EVP_PKEY_DHX) - def activate_builtin_random(self): - # Obtain a new structural reference. - e = self._lib.ENGINE_get_default_RAND() - if e != self._ffi.NULL: - self._lib.ENGINE_unregister_RAND(e) - # Reset the RNG to use the new engine. - self._lib.RAND_cleanup() - # decrement the structural reference from get_default_RAND - res = self._lib.ENGINE_finish(e) - assert res == 1 + def openssl_assert(self, ok): + return binding._openssl_assert(self._lib, ok) - def activate_osrandom_engine(self): - # Unregister and free the current engine. - self.activate_builtin_random() + def activate_builtin_random(self): + if self._lib.Cryptography_HAS_ENGINE: + # Obtain a new structural reference. + e = self._lib.ENGINE_get_default_RAND() + if e != self._ffi.NULL: + self._lib.ENGINE_unregister_RAND(e) + # Reset the RNG to use the built-in. + res = self._lib.RAND_set_rand_method(self._ffi.NULL) + self.openssl_assert(res == 1) + # decrement the structural reference from get_default_RAND + res = self._lib.ENGINE_finish(e) + self.openssl_assert(res == 1) + + @contextlib.contextmanager + def _get_osurandom_engine(self): # Fetches an engine by id and returns it. This creates a structural # reference. - e = self._lib.ENGINE_by_id(self._binding._osrandom_engine_id) - assert e != self._ffi.NULL + e = self._lib.ENGINE_by_id(self._lib.Cryptography_osrandom_engine_id) + self.openssl_assert(e != self._ffi.NULL) # Initialize the engine for use. This adds a functional reference. res = self._lib.ENGINE_init(e) - assert res == 1 - # Set the engine as the default RAND provider. - res = self._lib.ENGINE_set_default_RAND(e) - assert res == 1 - # Decrement the structural ref incremented by ENGINE_by_id. - res = self._lib.ENGINE_free(e) - assert res == 1 - # Decrement the functional ref incremented by ENGINE_init. - res = self._lib.ENGINE_finish(e) - assert res == 1 - # Reset the RNG to use the new engine. - self._lib.RAND_cleanup() + self.openssl_assert(res == 1) + + try: + yield e + finally: + # Decrement the structural ref incremented by ENGINE_by_id. + res = self._lib.ENGINE_free(e) + self.openssl_assert(res == 1) + # Decrement the functional ref incremented by ENGINE_init. + res = self._lib.ENGINE_finish(e) + self.openssl_assert(res == 1) + + def activate_osrandom_engine(self): + if self._lib.Cryptography_HAS_ENGINE: + # Unregister and free the current engine. + self.activate_builtin_random() + with self._get_osurandom_engine() as e: + # Set the engine as the default RAND provider. + res = self._lib.ENGINE_set_default_RAND(e) + self.openssl_assert(res == 1) + # Reset the RNG to use the engine + res = self._lib.RAND_set_rand_method(self._ffi.NULL) + self.openssl_assert(res == 1) + + def osrandom_engine_implementation(self): + buf = self._ffi.new("char[]", 64) + with self._get_osurandom_engine() as e: + res = self._lib.ENGINE_ctrl_cmd(e, b"get_implementation", + len(buf), buf, + self._ffi.NULL, 0) + self.openssl_assert(res > 0) + return self._ffi.string(buf).decode('ascii') def openssl_version_text(self): """ Friendly string name of the loaded OpenSSL library. This is not necessarily the same version as it was compiled against. - Example: OpenSSL 1.0.1e 11 Feb 2013 + Example: OpenSSL 1.1.1d 10 Sep 2019 """ return self._ffi.string( - self._lib.SSLeay_version(self._lib.SSLEAY_VERSION) + self._lib.OpenSSL_version(self._lib.OPENSSL_VERSION) ).decode("ascii") + def openssl_version_number(self): + return self._lib.OpenSSL_version_num() + def create_hmac_ctx(self, key, algorithm): return _HMACContext(self, key, algorithm) + def _evp_md_from_algorithm(self, algorithm): + if algorithm.name == "blake2b" or algorithm.name == "blake2s": + alg = "{}{}".format( + algorithm.name, algorithm.digest_size * 8 + ).encode("ascii") + else: + alg = algorithm.name.encode("ascii") + + evp_md = self._lib.EVP_get_digestbyname(alg) + return evp_md + + def _evp_md_non_null_from_algorithm(self, algorithm): + evp_md = self._evp_md_from_algorithm(algorithm) + self.openssl_assert(evp_md != self._ffi.NULL) + return evp_md + def hash_supported(self, algorithm): - digest = self._lib.EVP_get_digestbyname(algorithm.name.encode("ascii")) - return digest != self._ffi.NULL + evp_md = self._evp_md_from_algorithm(algorithm) + return evp_md != self._ffi.NULL def hmac_supported(self, algorithm): return self.hash_supported(algorithm) @@ -239,14 +233,6 @@ class Backend(object): return _HashContext(self, algorithm) def cipher_supported(self, cipher, mode): - if self._evp_cipher_supported(cipher, mode): - return True - elif isinstance(mode, CTR) and isinstance(cipher, AES): - return True - else: - return False - - def _evp_cipher_supported(self, cipher, mode): try: adapter = self._cipher_registry[type(cipher), type(mode)] except KeyError: @@ -256,13 +242,13 @@ class Backend(object): def register_cipher_adapter(self, cipher_cls, mode_cls, adapter): if (cipher_cls, mode_cls) in self._cipher_registry: - raise ValueError("Duplicate registration for: {0} {1}.".format( + raise ValueError("Duplicate registration for: {} {}.".format( cipher_cls, mode_cls) ) self._cipher_registry[cipher_cls, mode_cls] = adapter def _register_default_ciphers(self): - for mode_cls in [CBC, CTR, ECB, OFB, CFB, CFB8]: + for mode_cls in [CBC, CTR, ECB, OFB, CFB, CFB8, GCM]: self.register_cipher_adapter( AES, mode_cls, @@ -311,124 +297,65 @@ class Backend(object): type(None), GetCipherByName("rc4") ) + # We don't actually support RC2, this is just used by some tests. self.register_cipher_adapter( - AES, - GCM, - GetCipherByName("{cipher.name}-{cipher.key_size}-{mode.name}") + _RC2, type(None), GetCipherByName("rc2") ) + self.register_cipher_adapter( + ChaCha20, + type(None), + GetCipherByName("chacha20") + ) + self.register_cipher_adapter(AES, XTS, _get_xts_cipher) def create_symmetric_encryption_ctx(self, cipher, mode): - if (isinstance(mode, CTR) and isinstance(cipher, AES) and - not self._evp_cipher_supported(cipher, mode)): - # 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 (RHEL 5 - # extended life ends 2020). - return _AESCTRCipherContext(self, cipher, mode) - else: - return _CipherContext(self, cipher, mode, _CipherContext._ENCRYPT) + return _CipherContext(self, cipher, mode, _CipherContext._ENCRYPT) def create_symmetric_decryption_ctx(self, cipher, mode): - if (isinstance(mode, CTR) and isinstance(cipher, AES) and - not self._evp_cipher_supported(cipher, mode)): - # 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 (RHEL 5 - # extended life ends 2020). - return _AESCTRCipherContext(self, cipher, mode) - else: - return _CipherContext(self, cipher, mode, _CipherContext._DECRYPT) + return _CipherContext(self, cipher, mode, _CipherContext._DECRYPT) def pbkdf2_hmac_supported(self, algorithm): - if self._lib.Cryptography_HAS_PBKDF2_HMAC: - return self.hmac_supported(algorithm) - else: - # OpenSSL < 1.0.0 has an explicit PBKDF2-HMAC-SHA1 function, - # so if the PBKDF2_HMAC function is missing we only support - # SHA1 via PBKDF2_HMAC_SHA1. - return isinstance(algorithm, hashes.SHA1) + return self.hmac_supported(algorithm) def derive_pbkdf2_hmac(self, algorithm, length, salt, iterations, key_material): - buf = self._ffi.new("char[]", length) - if self._lib.Cryptography_HAS_PBKDF2_HMAC: - evp_md = self._lib.EVP_get_digestbyname( - algorithm.name.encode("ascii")) - assert evp_md != self._ffi.NULL - res = self._lib.PKCS5_PBKDF2_HMAC( - key_material, - len(key_material), - salt, - len(salt), - iterations, - evp_md, - length, - buf - ) - assert res == 1 - else: - if not isinstance(algorithm, hashes.SHA1): - raise UnsupportedAlgorithm( - "This version of OpenSSL only supports PBKDF2HMAC with " - "SHA1.", - _Reasons.UNSUPPORTED_HASH - ) - res = self._lib.PKCS5_PBKDF2_HMAC_SHA1( - key_material, - len(key_material), - salt, - len(salt), - iterations, - length, - buf - ) - assert res == 1 - + buf = self._ffi.new("unsigned char[]", length) + evp_md = self._evp_md_non_null_from_algorithm(algorithm) + key_material_ptr = self._ffi.from_buffer(key_material) + res = self._lib.PKCS5_PBKDF2_HMAC( + key_material_ptr, + len(key_material), + salt, + len(salt), + iterations, + evp_md, + length, + buf + ) + self.openssl_assert(res == 1) return self._ffi.buffer(buf)[:] - def _err_string(self, code): - err_buf = self._ffi.new("char[]", 256) - self._lib.ERR_error_string_n(code, err_buf, 256) - return self._ffi.string(err_buf, 256)[:] - def _consume_errors(self): - errors = [] - while True: - code = self._lib.ERR_get_error() - if code == 0: - break - - lib = self._lib.ERR_GET_LIB(code) - func = self._lib.ERR_GET_FUNC(code) - reason = self._lib.ERR_GET_REASON(code) - - errors.append(_OpenSSLError(code, lib, func, reason)) - return errors - - def _unknown_error(self, error): - return InternalError( - "Unknown error code {0} from OpenSSL, " - "you should probably file a bug. {1}.".format( - error.code, self._err_string(error.code) - ) - ) + return binding._consume_errors(self._lib) def _bn_to_int(self, bn): assert bn != self._ffi.NULL - if six.PY3: - # Python 3 has constant time from_bytes, so use that. - bn_num_bytes = (self._lib.BN_num_bits(bn) + 7) // 8 + if not six.PY2: + # Python 3 has constant time from_bytes, so use that. + bn_num_bytes = self._lib.BN_num_bytes(bn) bin_ptr = self._ffi.new("unsigned char[]", bn_num_bytes) bin_len = self._lib.BN_bn2bin(bn, bin_ptr) # A zero length means the BN has value 0 - assert bin_len >= 0 - assert bin_ptr != self._ffi.NULL - return int.from_bytes(self._ffi.buffer(bin_ptr)[:bin_len], "big") - + self.openssl_assert(bin_len >= 0) + val = int.from_bytes(self._ffi.buffer(bin_ptr)[:bin_len], "big") + if self._lib.BN_is_negative(bn): + val = -val + return val else: # Under Python 2 the best we can do is hex() - hex_cdata = self._lib.BN_bn2hex(bn) - assert hex_cdata != self._ffi.NULL + self.openssl_assert(hex_cdata != self._ffi.NULL) hex_str = self._ffi.string(hex_cdata) self._lib.OPENSSL_free(hex_cdata) return int(hex_str, 16) @@ -445,30 +372,30 @@ class Backend(object): if bn is None: bn = self._ffi.NULL - if six.PY3: + if not six.PY2: # Python 3 has constant time to_bytes, so use that. binary = num.to_bytes(int(num.bit_length() / 8.0 + 1), "big") bn_ptr = self._lib.BN_bin2bn(binary, len(binary), bn) - assert bn_ptr != self._ffi.NULL + self.openssl_assert(bn_ptr != self._ffi.NULL) return bn_ptr else: - # Under Python 2 the best we can do is hex() - - hex_num = hex(num).rstrip("L").lstrip("0x").encode("ascii") or b"0" + # Under Python 2 the best we can do is hex(), [2:] removes the 0x + # prefix. + hex_num = hex(num).rstrip("L")[2:].encode("ascii") bn_ptr = self._ffi.new("BIGNUM **") bn_ptr[0] = bn res = self._lib.BN_hex2bn(bn_ptr, hex_num) - assert res != 0 - assert bn_ptr[0] != self._ffi.NULL + self.openssl_assert(res != 0) + self.openssl_assert(bn_ptr[0] != self._ffi.NULL) return bn_ptr[0] def generate_rsa_private_key(self, public_exponent, key_size): rsa._verify_rsa_parameters(public_exponent, key_size) rsa_cdata = self._lib.RSA_new() - assert rsa_cdata != self._ffi.NULL + self.openssl_assert(rsa_cdata != self._ffi.NULL) rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free) bn = self._int_to_bn(public_exponent) @@ -477,7 +404,7 @@ class Backend(object): res = self._lib.RSA_generate_key_ex( rsa_cdata, key_size, bn, self._ffi.NULL ) - assert res == 1 + self.openssl_assert(res == 1) evp_pkey = self._rsa_cdata_to_evp_pkey(rsa_cdata) return _RSAPrivateKey(self, rsa_cdata, evp_pkey) @@ -498,18 +425,24 @@ class Backend(object): numbers.public_numbers.n ) rsa_cdata = self._lib.RSA_new() - assert rsa_cdata != self._ffi.NULL + self.openssl_assert(rsa_cdata != self._ffi.NULL) rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free) - rsa_cdata.p = self._int_to_bn(numbers.p) - rsa_cdata.q = self._int_to_bn(numbers.q) - rsa_cdata.d = self._int_to_bn(numbers.d) - rsa_cdata.dmp1 = self._int_to_bn(numbers.dmp1) - rsa_cdata.dmq1 = self._int_to_bn(numbers.dmq1) - rsa_cdata.iqmp = self._int_to_bn(numbers.iqmp) - rsa_cdata.e = self._int_to_bn(numbers.public_numbers.e) - rsa_cdata.n = self._int_to_bn(numbers.public_numbers.n) + p = self._int_to_bn(numbers.p) + q = self._int_to_bn(numbers.q) + d = self._int_to_bn(numbers.d) + dmp1 = self._int_to_bn(numbers.dmp1) + dmq1 = self._int_to_bn(numbers.dmq1) + iqmp = self._int_to_bn(numbers.iqmp) + e = self._int_to_bn(numbers.public_numbers.e) + n = self._int_to_bn(numbers.public_numbers.n) + res = self._lib.RSA_set0_factors(rsa_cdata, p, q) + self.openssl_assert(res == 1) + res = self._lib.RSA_set0_key(rsa_cdata, n, e, d) + self.openssl_assert(res == 1) + res = self._lib.RSA_set0_crt_params(rsa_cdata, dmp1, dmq1, iqmp) + self.openssl_assert(res == 1) res = self._lib.RSA_blinding_on(rsa_cdata, self._ffi.NULL) - assert res == 1 + self.openssl_assert(res == 1) evp_pkey = self._rsa_cdata_to_evp_pkey(rsa_cdata) return _RSAPrivateKey(self, rsa_cdata, evp_pkey) @@ -517,22 +450,26 @@ class Backend(object): def load_rsa_public_numbers(self, numbers): rsa._check_public_key_components(numbers.e, numbers.n) rsa_cdata = self._lib.RSA_new() - assert rsa_cdata != self._ffi.NULL + self.openssl_assert(rsa_cdata != self._ffi.NULL) rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free) - rsa_cdata.e = self._int_to_bn(numbers.e) - rsa_cdata.n = self._int_to_bn(numbers.n) - res = self._lib.RSA_blinding_on(rsa_cdata, self._ffi.NULL) - assert res == 1 + e = self._int_to_bn(numbers.e) + n = self._int_to_bn(numbers.n) + res = self._lib.RSA_set0_key(rsa_cdata, n, e, self._ffi.NULL) + self.openssl_assert(res == 1) evp_pkey = self._rsa_cdata_to_evp_pkey(rsa_cdata) return _RSAPublicKey(self, rsa_cdata, evp_pkey) - def _rsa_cdata_to_evp_pkey(self, rsa_cdata): + def _create_evp_pkey_gc(self): evp_pkey = self._lib.EVP_PKEY_new() - assert evp_pkey != self._ffi.NULL + self.openssl_assert(evp_pkey != self._ffi.NULL) evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free) + return evp_pkey + + def _rsa_cdata_to_evp_pkey(self, rsa_cdata): + evp_pkey = self._create_evp_pkey_gc() res = self._lib.EVP_PKEY_set1_RSA(evp_pkey, rsa_cdata) - assert res == 1 + self.openssl_assert(res == 1) return evp_pkey def _bytes_to_bio(self, data): @@ -542,22 +479,22 @@ class Backend(object): The char* is the storage for the BIO and it must stay alive until the BIO is finished with. """ - data_char_p = self._ffi.new("char[]", data) + data_ptr = self._ffi.from_buffer(data) bio = self._lib.BIO_new_mem_buf( - data_char_p, len(data) + data_ptr, len(data) ) - assert bio != self._ffi.NULL + self.openssl_assert(bio != self._ffi.NULL) - return _MemoryBIO(self._ffi.gc(bio, self._lib.BIO_free), data_char_p) + return _MemoryBIO(self._ffi.gc(bio, self._lib.BIO_free), data_ptr) - def _create_mem_bio(self): + def _create_mem_bio_gc(self): """ Creates an empty memory BIO. """ bio_method = self._lib.BIO_s_mem() - assert bio_method != self._ffi.NULL + self.openssl_assert(bio_method != self._ffi.NULL) bio = self._lib.BIO_new(bio_method) - assert bio != self._ffi.NULL + self.openssl_assert(bio != self._ffi.NULL) bio = self._ffi.gc(bio, self._lib.BIO_free) return bio @@ -567,8 +504,8 @@ class Backend(object): """ buf = self._ffi.new("char **") buf_len = self._lib.BIO_get_mem_data(bio, buf) - assert buf_len > 0 - assert buf[0] != self._ffi.NULL + self.openssl_assert(buf_len > 0) + self.openssl_assert(buf[0] != self._ffi.NULL) bio_data = self._ffi.buffer(buf[0], buf_len)[:] return bio_data @@ -578,24 +515,40 @@ class Backend(object): pointer. """ - key_type = evp_pkey.type + key_type = self._lib.EVP_PKEY_id(evp_pkey) if key_type == self._lib.EVP_PKEY_RSA: rsa_cdata = self._lib.EVP_PKEY_get1_RSA(evp_pkey) - assert rsa_cdata != self._ffi.NULL + self.openssl_assert(rsa_cdata != self._ffi.NULL) rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free) return _RSAPrivateKey(self, rsa_cdata, evp_pkey) elif key_type == self._lib.EVP_PKEY_DSA: dsa_cdata = self._lib.EVP_PKEY_get1_DSA(evp_pkey) - assert dsa_cdata != self._ffi.NULL + self.openssl_assert(dsa_cdata != self._ffi.NULL) dsa_cdata = self._ffi.gc(dsa_cdata, self._lib.DSA_free) return _DSAPrivateKey(self, dsa_cdata, evp_pkey) - elif (self._lib.Cryptography_HAS_EC == 1 and - key_type == self._lib.EVP_PKEY_EC): + elif key_type == self._lib.EVP_PKEY_EC: ec_cdata = self._lib.EVP_PKEY_get1_EC_KEY(evp_pkey) - assert ec_cdata != self._ffi.NULL + self.openssl_assert(ec_cdata != self._ffi.NULL) ec_cdata = self._ffi.gc(ec_cdata, self._lib.EC_KEY_free) return _EllipticCurvePrivateKey(self, ec_cdata, evp_pkey) + elif key_type in self._dh_types: + dh_cdata = self._lib.EVP_PKEY_get1_DH(evp_pkey) + self.openssl_assert(dh_cdata != self._ffi.NULL) + dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free) + return _DHPrivateKey(self, dh_cdata, evp_pkey) + elif key_type == getattr(self._lib, "EVP_PKEY_ED25519", None): + # EVP_PKEY_ED25519 is not present in OpenSSL < 1.1.1 + return _Ed25519PrivateKey(self, evp_pkey) + elif key_type == getattr(self._lib, "EVP_PKEY_X448", None): + # EVP_PKEY_X448 is not present in OpenSSL < 1.1.1 + return _X448PrivateKey(self, evp_pkey) + elif key_type == getattr(self._lib, "EVP_PKEY_X25519", None): + # EVP_PKEY_X25519 is not present in OpenSSL < 1.1.0 + return _X25519PrivateKey(self, evp_pkey) + elif key_type == getattr(self._lib, "EVP_PKEY_ED448", None): + # EVP_PKEY_ED448 is not present in OpenSSL < 1.1.1 + return _Ed448PrivateKey(self, evp_pkey) else: raise UnsupportedAlgorithm("Unsupported key type.") @@ -605,71 +558,54 @@ class Backend(object): pointer. """ - key_type = evp_pkey.type + key_type = self._lib.EVP_PKEY_id(evp_pkey) if key_type == self._lib.EVP_PKEY_RSA: rsa_cdata = self._lib.EVP_PKEY_get1_RSA(evp_pkey) - assert rsa_cdata != self._ffi.NULL + self.openssl_assert(rsa_cdata != self._ffi.NULL) rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free) return _RSAPublicKey(self, rsa_cdata, evp_pkey) elif key_type == self._lib.EVP_PKEY_DSA: dsa_cdata = self._lib.EVP_PKEY_get1_DSA(evp_pkey) - assert dsa_cdata != self._ffi.NULL + self.openssl_assert(dsa_cdata != self._ffi.NULL) dsa_cdata = self._ffi.gc(dsa_cdata, self._lib.DSA_free) return _DSAPublicKey(self, dsa_cdata, evp_pkey) - elif (self._lib.Cryptography_HAS_EC == 1 and - key_type == self._lib.EVP_PKEY_EC): + elif key_type == self._lib.EVP_PKEY_EC: ec_cdata = self._lib.EVP_PKEY_get1_EC_KEY(evp_pkey) - assert ec_cdata != self._ffi.NULL + self.openssl_assert(ec_cdata != self._ffi.NULL) ec_cdata = self._ffi.gc(ec_cdata, self._lib.EC_KEY_free) return _EllipticCurvePublicKey(self, ec_cdata, evp_pkey) + elif key_type in self._dh_types: + dh_cdata = self._lib.EVP_PKEY_get1_DH(evp_pkey) + self.openssl_assert(dh_cdata != self._ffi.NULL) + dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free) + return _DHPublicKey(self, dh_cdata, evp_pkey) + elif key_type == getattr(self._lib, "EVP_PKEY_ED25519", None): + # EVP_PKEY_ED25519 is not present in OpenSSL < 1.1.1 + return _Ed25519PublicKey(self, evp_pkey) + elif key_type == getattr(self._lib, "EVP_PKEY_X448", None): + # EVP_PKEY_X448 is not present in OpenSSL < 1.1.1 + return _X448PublicKey(self, evp_pkey) + elif key_type == getattr(self._lib, "EVP_PKEY_X25519", None): + # EVP_PKEY_X25519 is not present in OpenSSL < 1.1.0 + return _X25519PublicKey(self, evp_pkey) + elif key_type == getattr(self._lib, "EVP_PKEY_ED448", None): + # EVP_PKEY_X25519 is not present in OpenSSL < 1.1.1 + return _Ed448PublicKey(self, evp_pkey) else: raise UnsupportedAlgorithm("Unsupported key type.") - def _pem_password_cb(self, password): - """ - Generate a pem_password_cb function pointer that copied the password to - OpenSSL as required and returns the number of bytes copied. - - typedef int pem_password_cb(char *buf, int size, - int rwflag, void *userdata); - - Useful for decrypting PKCS8 files and so on. - - Returns a tuple of (cdata function pointer, callback function). - """ - - def pem_password_cb(buf, size, writing, userdata): - pem_password_cb.called += 1 - - if not password: - pem_password_cb.exception = TypeError( - "Password was not given but private key is encrypted." - ) - return 0 - elif len(password) < size: - pw_buf = self._ffi.buffer(buf, size) - pw_buf[:len(password)] = password - return len(password) - else: - pem_password_cb.exception = ValueError( - "Passwords longer than {0} bytes are not supported " - "by this backend.".format(size - 1) + def _oaep_hash_supported(self, algorithm): + if self._lib.Cryptography_HAS_RSA_OAEP_MD: + return isinstance( + algorithm, ( + hashes.SHA1, + hashes.SHA224, + hashes.SHA256, + hashes.SHA384, + hashes.SHA512, ) - return 0 - - pem_password_cb.called = 0 - pem_password_cb.exception = None - - return ( - self._ffi.callback("int (char *, int, int, void *)", - pem_password_cb), - pem_password_cb - ) - - def _mgf1_hash_supported(self, algorithm): - if self._lib.Cryptography_HAS_MGF1_MD: - return self.hash_supported(algorithm) + ) else: return isinstance(algorithm, hashes.SHA1) @@ -677,25 +613,25 @@ class Backend(object): if isinstance(padding, PKCS1v15): return True elif isinstance(padding, PSS) and isinstance(padding._mgf, MGF1): - return self._mgf1_hash_supported(padding._mgf._algorithm) + return self.hash_supported(padding._mgf._algorithm) elif isinstance(padding, OAEP) and isinstance(padding._mgf, MGF1): - return isinstance(padding._mgf._algorithm, hashes.SHA1) + return ( + self._oaep_hash_supported(padding._mgf._algorithm) and + self._oaep_hash_supported(padding._algorithm) and + ( + (padding._label is None or len(padding._label) == 0) or + self._lib.Cryptography_HAS_RSA_OAEP_LABEL == 1 + ) + ) else: return False def generate_dsa_parameters(self, key_size): if key_size not in (1024, 2048, 3072): - raise ValueError( - "Key size must be 1024 or 2048 or 3072 bits.") - - if (self._lib.OPENSSL_VERSION_NUMBER < 0x1000000f and - key_size > 1024): - raise ValueError( - "Key size must be 1024 because OpenSSL < 1.0.0 doesn't " - "support larger key sizes.") + raise ValueError("Key size must be 1024 or 2048 or 3072 bits.") ctx = self._lib.DSA_new() - assert ctx != self._ffi.NULL + self.openssl_assert(ctx != self._ffi.NULL) ctx = self._ffi.gc(ctx, self._lib.DSA_free) res = self._lib.DSA_generate_parameters_ex( @@ -703,18 +639,14 @@ class Backend(object): self._ffi.NULL, self._ffi.NULL, self._ffi.NULL ) - assert res == 1 + self.openssl_assert(res == 1) return _DSAParameters(self, ctx) def generate_dsa_private_key(self, parameters): - ctx = self._lib.DSA_new() - assert ctx != self._ffi.NULL + ctx = self._lib.DSAparams_dup(parameters._dsa_cdata) + self.openssl_assert(ctx != self._ffi.NULL) ctx = self._ffi.gc(ctx, self._lib.DSA_free) - ctx.p = self._lib.BN_dup(parameters._dsa_cdata.p) - ctx.q = self._lib.BN_dup(parameters._dsa_cdata.q) - ctx.g = self._lib.BN_dup(parameters._dsa_cdata.g) - self._lib.DSA_generate_key(ctx) evp_pkey = self._dsa_cdata_to_evp_pkey(ctx) @@ -724,19 +656,26 @@ class Backend(object): parameters = self.generate_dsa_parameters(key_size) return self.generate_dsa_private_key(parameters) + def _dsa_cdata_set_values(self, dsa_cdata, p, q, g, pub_key, priv_key): + res = self._lib.DSA_set0_pqg(dsa_cdata, p, q, g) + self.openssl_assert(res == 1) + res = self._lib.DSA_set0_key(dsa_cdata, pub_key, priv_key) + self.openssl_assert(res == 1) + def load_dsa_private_numbers(self, numbers): dsa._check_dsa_private_numbers(numbers) parameter_numbers = numbers.public_numbers.parameter_numbers dsa_cdata = self._lib.DSA_new() - assert dsa_cdata != self._ffi.NULL + self.openssl_assert(dsa_cdata != self._ffi.NULL) dsa_cdata = self._ffi.gc(dsa_cdata, self._lib.DSA_free) - dsa_cdata.p = self._int_to_bn(parameter_numbers.p) - dsa_cdata.q = self._int_to_bn(parameter_numbers.q) - dsa_cdata.g = self._int_to_bn(parameter_numbers.g) - dsa_cdata.pub_key = self._int_to_bn(numbers.public_numbers.y) - dsa_cdata.priv_key = self._int_to_bn(numbers.x) + p = self._int_to_bn(parameter_numbers.p) + q = self._int_to_bn(parameter_numbers.q) + g = self._int_to_bn(parameter_numbers.g) + pub_key = self._int_to_bn(numbers.public_numbers.y) + priv_key = self._int_to_bn(numbers.x) + self._dsa_cdata_set_values(dsa_cdata, p, q, g, pub_key, priv_key) evp_pkey = self._dsa_cdata_to_evp_pkey(dsa_cdata) @@ -745,13 +684,15 @@ class Backend(object): def load_dsa_public_numbers(self, numbers): dsa._check_dsa_parameters(numbers.parameter_numbers) dsa_cdata = self._lib.DSA_new() - assert dsa_cdata != self._ffi.NULL + self.openssl_assert(dsa_cdata != self._ffi.NULL) dsa_cdata = self._ffi.gc(dsa_cdata, self._lib.DSA_free) - dsa_cdata.p = self._int_to_bn(numbers.parameter_numbers.p) - dsa_cdata.q = self._int_to_bn(numbers.parameter_numbers.q) - dsa_cdata.g = self._int_to_bn(numbers.parameter_numbers.g) - dsa_cdata.pub_key = self._int_to_bn(numbers.y) + p = self._int_to_bn(numbers.parameter_numbers.p) + q = self._int_to_bn(numbers.parameter_numbers.q) + g = self._int_to_bn(numbers.parameter_numbers.g) + pub_key = self._int_to_bn(numbers.y) + priv_key = self._ffi.NULL + self._dsa_cdata_set_values(dsa_cdata, p, q, g, pub_key, priv_key) evp_pkey = self._dsa_cdata_to_evp_pkey(dsa_cdata) @@ -760,119 +701,370 @@ class Backend(object): def load_dsa_parameter_numbers(self, numbers): dsa._check_dsa_parameters(numbers) dsa_cdata = self._lib.DSA_new() - assert dsa_cdata != self._ffi.NULL + self.openssl_assert(dsa_cdata != self._ffi.NULL) dsa_cdata = self._ffi.gc(dsa_cdata, self._lib.DSA_free) - dsa_cdata.p = self._int_to_bn(numbers.p) - dsa_cdata.q = self._int_to_bn(numbers.q) - dsa_cdata.g = self._int_to_bn(numbers.g) + p = self._int_to_bn(numbers.p) + q = self._int_to_bn(numbers.q) + g = self._int_to_bn(numbers.g) + res = self._lib.DSA_set0_pqg(dsa_cdata, p, q, g) + self.openssl_assert(res == 1) return _DSAParameters(self, dsa_cdata) def _dsa_cdata_to_evp_pkey(self, dsa_cdata): - evp_pkey = self._lib.EVP_PKEY_new() - assert evp_pkey != self._ffi.NULL - evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free) + evp_pkey = self._create_evp_pkey_gc() res = self._lib.EVP_PKEY_set1_DSA(evp_pkey, dsa_cdata) - assert res == 1 + self.openssl_assert(res == 1) return evp_pkey def dsa_hash_supported(self, algorithm): - if self._lib.OPENSSL_VERSION_NUMBER < 0x1000000f: - return isinstance(algorithm, hashes.SHA1) - else: - return self.hash_supported(algorithm) + return self.hash_supported(algorithm) def dsa_parameters_supported(self, p, q, g): - if self._lib.OPENSSL_VERSION_NUMBER < 0x1000000f: - return utils.bit_length(p) <= 1024 and utils.bit_length(q) <= 160 - else: - return True + return True def cmac_algorithm_supported(self, algorithm): - return ( - self._lib.Cryptography_HAS_CMAC == 1 and - self.cipher_supported( - algorithm, CBC(b"\x00" * algorithm.block_size) - ) + return self.cipher_supported( + algorithm, CBC(b"\x00" * algorithm.block_size) ) def create_cmac_ctx(self, algorithm): return _CMACContext(self, algorithm) - def create_x509_csr(self, builder, private_key, algorithm): - if not isinstance(algorithm, hashes.HashAlgorithm): - raise TypeError('Algorithm must be a registered hash algorithm.') - - if self._lib.OPENSSL_VERSION_NUMBER <= 0x10001000: - if isinstance(private_key, _DSAPrivateKey): - raise NotImplementedError( - "Certificate signing requests aren't implemented for DSA" - " keys on OpenSSL versions less than 1.0.1." - ) - if isinstance(private_key, _EllipticCurvePrivateKey): - raise NotImplementedError( - "Certificate signing requests aren't implemented for EC" - " keys on OpenSSL versions less than 1.0.1." + def _x509_check_signature_params(self, private_key, algorithm): + if isinstance(private_key, + (ed25519.Ed25519PrivateKey, ed448.Ed448PrivateKey)): + if algorithm is not None: + raise ValueError( + "algorithm must be None when signing via ed25519 or ed448" ) + elif not isinstance(private_key, (rsa.RSAPrivateKey, dsa.DSAPrivateKey, + ec.EllipticCurvePrivateKey)): + raise TypeError( + "Key must be rsa, dsa, ec, ed25519 or ed448 private key." + ) + elif not isinstance(algorithm, hashes.HashAlgorithm): + raise TypeError("Algorithm must be a registered hash algorithm.") + elif ( + isinstance(algorithm, hashes.MD5) and not + isinstance(private_key, rsa.RSAPrivateKey) + ): + raise ValueError( + "MD5 hash algorithm is only supported with RSA keys" + ) + + def create_x509_csr(self, builder, private_key, algorithm): + if not isinstance(builder, x509.CertificateSigningRequestBuilder): + raise TypeError('Builder type mismatch.') + self._x509_check_signature_params(private_key, algorithm) # Resolve the signature algorithm. - evp_md = self._lib.EVP_get_digestbyname( - algorithm.name.encode('ascii') - ) - assert evp_md != self._ffi.NULL + evp_md = self._evp_md_x509_null_if_eddsa(private_key, algorithm) # Create an empty request. x509_req = self._lib.X509_REQ_new() - assert x509_req != self._ffi.NULL + self.openssl_assert(x509_req != self._ffi.NULL) x509_req = self._ffi.gc(x509_req, self._lib.X509_REQ_free) # Set x509 version. res = self._lib.X509_REQ_set_version(x509_req, x509.Version.v1.value) - assert res == 1 + self.openssl_assert(res == 1) # Set subject name. res = self._lib.X509_REQ_set_subject_name( - x509_req, _encode_name(self, list(builder._subject_name)) + x509_req, _encode_name_gc(self, builder._subject_name) ) - assert res == 1 + self.openssl_assert(res == 1) # Set subject public key. public_key = private_key.public_key() res = self._lib.X509_REQ_set_pubkey( x509_req, public_key._evp_pkey ) - assert res == 1 + self.openssl_assert(res == 1) # Add extensions. - extensions = self._lib.sk_X509_EXTENSION_new_null() - assert extensions != self._ffi.NULL - extensions = self._ffi.gc( - extensions, - self._lib.sk_X509_EXTENSION_free, - ) - for extension in builder._extensions: - if isinstance(extension.value, x509.BasicConstraints): - extension = _encode_basic_constraints( - self, - extension.value, - extension.critical + sk_extension = self._lib.sk_X509_EXTENSION_new_null() + self.openssl_assert(sk_extension != self._ffi.NULL) + sk_extension = self._ffi.gc( + sk_extension, + lambda x: self._lib.sk_X509_EXTENSION_pop_free( + x, self._ffi.addressof( + self._lib._original_lib, "X509_EXTENSION_free" ) - else: - raise NotImplementedError('Extension not yet supported.') - res = self._lib.sk_X509_EXTENSION_push(extensions, extension) - assert res == 1 - res = self._lib.X509_REQ_add_extensions(x509_req, extensions) - assert res == 1 + ) + ) + # Don't GC individual extensions because the memory is owned by + # sk_extensions and will be freed along with it. + self._create_x509_extensions( + extensions=builder._extensions, + handlers=_EXTENSION_ENCODE_HANDLERS, + x509_obj=sk_extension, + add_func=self._lib.sk_X509_EXTENSION_insert, + gc=False + ) + res = self._lib.X509_REQ_add_extensions(x509_req, sk_extension) + self.openssl_assert(res == 1) # Sign the request using the requester's private key. res = self._lib.X509_REQ_sign( x509_req, private_key._evp_pkey, evp_md ) - assert res > 0 + if res == 0: + errors = self._consume_errors() + self.openssl_assert( + errors[0]._lib_reason_match( + self._lib.ERR_LIB_RSA, + self._lib.RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY + ) + ) + + raise ValueError("Digest too big for RSA key") return _CertificateSigningRequest(self, x509_req) + def create_x509_certificate(self, builder, private_key, algorithm): + if not isinstance(builder, x509.CertificateBuilder): + raise TypeError('Builder type mismatch.') + self._x509_check_signature_params(private_key, algorithm) + + # Resolve the signature algorithm. + evp_md = self._evp_md_x509_null_if_eddsa(private_key, algorithm) + + # Create an empty certificate. + x509_cert = self._lib.X509_new() + x509_cert = self._ffi.gc(x509_cert, self._lib.X509_free) + + # Set the x509 version. + res = self._lib.X509_set_version(x509_cert, builder._version.value) + self.openssl_assert(res == 1) + + # Set the subject's name. + res = self._lib.X509_set_subject_name( + x509_cert, _encode_name_gc(self, builder._subject_name) + ) + self.openssl_assert(res == 1) + + # Set the subject's public key. + res = self._lib.X509_set_pubkey( + x509_cert, builder._public_key._evp_pkey + ) + self.openssl_assert(res == 1) + + # Set the certificate serial number. + serial_number = _encode_asn1_int_gc(self, builder._serial_number) + res = self._lib.X509_set_serialNumber(x509_cert, serial_number) + self.openssl_assert(res == 1) + + # Set the "not before" time. + self._set_asn1_time( + self._lib.X509_getm_notBefore(x509_cert), builder._not_valid_before + ) + + # Set the "not after" time. + self._set_asn1_time( + self._lib.X509_getm_notAfter(x509_cert), builder._not_valid_after + ) + + # Add extensions. + self._create_x509_extensions( + extensions=builder._extensions, + handlers=_EXTENSION_ENCODE_HANDLERS, + x509_obj=x509_cert, + add_func=self._lib.X509_add_ext, + gc=True + ) + + # Set the issuer name. + res = self._lib.X509_set_issuer_name( + x509_cert, _encode_name_gc(self, builder._issuer_name) + ) + self.openssl_assert(res == 1) + + # Sign the certificate with the issuer's private key. + res = self._lib.X509_sign( + x509_cert, private_key._evp_pkey, evp_md + ) + if res == 0: + errors = self._consume_errors() + self.openssl_assert( + errors[0]._lib_reason_match( + self._lib.ERR_LIB_RSA, + self._lib.RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY + ) + ) + raise ValueError("Digest too big for RSA key") + + return _Certificate(self, x509_cert) + + def _evp_md_x509_null_if_eddsa(self, private_key, algorithm): + if isinstance(private_key, + (ed25519.Ed25519PrivateKey, ed448.Ed448PrivateKey)): + # OpenSSL requires us to pass NULL for EVP_MD for ed25519/ed448 + return self._ffi.NULL + else: + return self._evp_md_non_null_from_algorithm(algorithm) + + def _set_asn1_time(self, asn1_time, time): + if time.year >= 2050: + asn1_str = time.strftime('%Y%m%d%H%M%SZ').encode('ascii') + else: + asn1_str = time.strftime('%y%m%d%H%M%SZ').encode('ascii') + res = self._lib.ASN1_TIME_set_string(asn1_time, asn1_str) + self.openssl_assert(res == 1) + + def _create_asn1_time(self, time): + asn1_time = self._lib.ASN1_TIME_new() + self.openssl_assert(asn1_time != self._ffi.NULL) + asn1_time = self._ffi.gc(asn1_time, self._lib.ASN1_TIME_free) + self._set_asn1_time(asn1_time, time) + return asn1_time + + def create_x509_crl(self, builder, private_key, algorithm): + if not isinstance(builder, x509.CertificateRevocationListBuilder): + raise TypeError('Builder type mismatch.') + self._x509_check_signature_params(private_key, algorithm) + + evp_md = self._evp_md_x509_null_if_eddsa(private_key, algorithm) + + # Create an empty CRL. + x509_crl = self._lib.X509_CRL_new() + x509_crl = self._ffi.gc(x509_crl, self._lib.X509_CRL_free) + + # Set the x509 CRL version. We only support v2 (integer value 1). + res = self._lib.X509_CRL_set_version(x509_crl, 1) + self.openssl_assert(res == 1) + + # Set the issuer name. + res = self._lib.X509_CRL_set_issuer_name( + x509_crl, _encode_name_gc(self, builder._issuer_name) + ) + self.openssl_assert(res == 1) + + # Set the last update time. + last_update = self._create_asn1_time(builder._last_update) + res = self._lib.X509_CRL_set_lastUpdate(x509_crl, last_update) + self.openssl_assert(res == 1) + + # Set the next update time. + next_update = self._create_asn1_time(builder._next_update) + res = self._lib.X509_CRL_set_nextUpdate(x509_crl, next_update) + self.openssl_assert(res == 1) + + # Add extensions. + self._create_x509_extensions( + extensions=builder._extensions, + handlers=_CRL_EXTENSION_ENCODE_HANDLERS, + x509_obj=x509_crl, + add_func=self._lib.X509_CRL_add_ext, + gc=True + ) + + # add revoked certificates + for revoked_cert in builder._revoked_certificates: + # Duplicating because the X509_CRL takes ownership and will free + # this memory when X509_CRL_free is called. + revoked = self._lib.X509_REVOKED_dup(revoked_cert._x509_revoked) + self.openssl_assert(revoked != self._ffi.NULL) + res = self._lib.X509_CRL_add0_revoked(x509_crl, revoked) + self.openssl_assert(res == 1) + + res = self._lib.X509_CRL_sign( + x509_crl, private_key._evp_pkey, evp_md + ) + if res == 0: + errors = self._consume_errors() + self.openssl_assert( + errors[0]._lib_reason_match( + self._lib.ERR_LIB_RSA, + self._lib.RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY + ) + ) + raise ValueError("Digest too big for RSA key") + + return _CertificateRevocationList(self, x509_crl) + + def _create_x509_extensions(self, extensions, handlers, x509_obj, + add_func, gc): + for i, extension in enumerate(extensions): + x509_extension = self._create_x509_extension( + handlers, extension + ) + self.openssl_assert(x509_extension != self._ffi.NULL) + + if gc: + x509_extension = self._ffi.gc( + x509_extension, self._lib.X509_EXTENSION_free + ) + res = add_func(x509_obj, x509_extension, i) + self.openssl_assert(res >= 1) + + def _create_raw_x509_extension(self, extension, value): + obj = _txt2obj_gc(self, extension.oid.dotted_string) + return self._lib.X509_EXTENSION_create_by_OBJ( + self._ffi.NULL, obj, 1 if extension.critical else 0, value + ) + + def _create_x509_extension(self, handlers, extension): + if isinstance(extension.value, x509.UnrecognizedExtension): + value = _encode_asn1_str_gc(self, extension.value.value) + return self._create_raw_x509_extension(extension, value) + elif isinstance(extension.value, x509.TLSFeature): + asn1 = encode_der( + SEQUENCE, + *[ + encode_der(INTEGER, encode_der_integer(x.value)) + for x in extension.value + ] + ) + value = _encode_asn1_str_gc(self, asn1) + return self._create_raw_x509_extension(extension, value) + elif isinstance(extension.value, x509.PrecertPoison): + value = _encode_asn1_str_gc(self, encode_der(NULL)) + return self._create_raw_x509_extension(extension, value) + else: + try: + encode = handlers[extension.oid] + except KeyError: + raise NotImplementedError( + 'Extension not supported: {}'.format(extension.oid) + ) + + ext_struct = encode(self, extension.value) + nid = self._lib.OBJ_txt2nid( + extension.oid.dotted_string.encode("ascii") + ) + self.openssl_assert(nid != self._lib.NID_undef) + return self._lib.X509V3_EXT_i2d( + nid, 1 if extension.critical else 0, ext_struct + ) + + def create_x509_revoked_certificate(self, builder): + if not isinstance(builder, x509.RevokedCertificateBuilder): + raise TypeError('Builder type mismatch.') + + x509_revoked = self._lib.X509_REVOKED_new() + self.openssl_assert(x509_revoked != self._ffi.NULL) + x509_revoked = self._ffi.gc(x509_revoked, self._lib.X509_REVOKED_free) + serial_number = _encode_asn1_int_gc(self, builder._serial_number) + res = self._lib.X509_REVOKED_set_serialNumber( + x509_revoked, serial_number + ) + self.openssl_assert(res == 1) + rev_date = self._create_asn1_time(builder._revocation_date) + res = self._lib.X509_REVOKED_set_revocationDate(x509_revoked, rev_date) + self.openssl_assert(res == 1) + # add CRL entry extensions + self._create_x509_extensions( + extensions=builder._extensions, + handlers=_CRL_ENTRY_EXTENSION_ENCODE_HANDLERS, + x509_obj=x509_revoked, + add_func=self._lib.X509_REVOKED_add_ext, + gc=True + ) + return _RevokedCertificate(self, None, x509_revoked) + def load_pem_private_key(self, data, password): return self._load_key( self._lib.PEM_read_bio_PrivateKey, @@ -895,7 +1087,7 @@ class Backend(object): # embedded in a subjectPublicKeyInfo) self._consume_errors() res = self._lib.BIO_reset(mem_bio.bio) - assert res == 1 + self.openssl_assert(res == 1) rsa_cdata = self._lib.PEM_read_bio_RSAPublicKey( mem_bio.bio, self._ffi.NULL, self._ffi.NULL, self._ffi.NULL ) @@ -906,22 +1098,24 @@ class Backend(object): else: self._handle_key_loading_error() + def load_pem_parameters(self, data): + mem_bio = self._bytes_to_bio(data) + # only DH is supported currently + dh_cdata = self._lib.PEM_read_bio_DHparams( + mem_bio.bio, self._ffi.NULL, self._ffi.NULL, self._ffi.NULL) + if dh_cdata != self._ffi.NULL: + dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free) + return _DHParameters(self, dh_cdata) + else: + self._handle_key_loading_error() + def load_der_private_key(self, data, password): - # OpenSSL has a function called d2i_AutoPrivateKey that can simplify - # this. Unfortunately it doesn't properly support PKCS8 on OpenSSL - # 0.9.8 so we can't use it. Instead we sequentially try to load it 3 - # different ways. First we'll try to load it as a traditional key + # OpenSSL has a function called d2i_AutoPrivateKey that in theory + # handles this automatically, however it doesn't handle encrypted + # private keys. Instead we try to load the key two different ways. + # First we'll try to load it as a traditional key. bio_data = self._bytes_to_bio(data) key = self._evp_pkey_from_der_traditional_key(bio_data, password) - if not key: - # Okay so it's not a traditional key. Let's try - # PKCS8 unencrypted. OpenSSL 0.9.8 can't load unencrypted - # PKCS8 keys using d2i_PKCS8PrivateKey_bio so we do this instead. - # Reset the memory BIO so we can read the data again. - res = self._lib.BIO_reset(bio_data.bio) - assert res == 1 - key = self._evp_pkey_from_der_unencrypted_pkcs8(bio_data, password) - if key: return self._evp_pkey_to_private_key(key) else: @@ -948,24 +1142,6 @@ class Backend(object): self._consume_errors() return None - def _evp_pkey_from_der_unencrypted_pkcs8(self, bio_data, password): - info = self._lib.d2i_PKCS8_PRIV_KEY_INFO_bio( - bio_data.bio, self._ffi.NULL - ) - info = self._ffi.gc(info, self._lib.PKCS8_PRIV_KEY_INFO_free) - if info != self._ffi.NULL: - key = self._lib.EVP_PKCS82PKEY(info) - assert key != self._ffi.NULL - key = self._ffi.gc(key, self._lib.EVP_PKEY_free) - if password is not None: - raise TypeError( - "Password was given but private key is not encrypted." - ) - return key - else: - self._consume_errors() - return None - def load_der_public_key(self, data): mem_bio = self._bytes_to_bio(data) evp_pkey = self._lib.d2i_PUBKEY_bio(mem_bio.bio, self._ffi.NULL) @@ -978,7 +1154,7 @@ class Backend(object): # embedded in a subjectPublicKeyInfo) self._consume_errors() res = self._lib.BIO_reset(mem_bio.bio) - assert res == 1 + self.openssl_assert(res == 1) rsa_cdata = self._lib.d2i_RSAPublicKey_bio( mem_bio.bio, self._ffi.NULL ) @@ -989,6 +1165,28 @@ class Backend(object): else: self._handle_key_loading_error() + def load_der_parameters(self, data): + mem_bio = self._bytes_to_bio(data) + dh_cdata = self._lib.d2i_DHparams_bio( + mem_bio.bio, self._ffi.NULL + ) + if dh_cdata != self._ffi.NULL: + dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free) + return _DHParameters(self, dh_cdata) + elif self._lib.Cryptography_HAS_EVP_PKEY_DHX: + # We check to see if the is dhx. + self._consume_errors() + res = self._lib.BIO_reset(mem_bio.bio) + self.openssl_assert(res == 1) + dh_cdata = self._lib.Cryptography_d2i_DHxparams_bio( + mem_bio.bio, self._ffi.NULL + ) + if dh_cdata != self._ffi.NULL: + dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free) + return _DHParameters(self, dh_cdata) + + self._handle_key_loading_error() + def load_pem_x509_certificate(self, data): mem_bio = self._bytes_to_bio(data) x509 = self._lib.PEM_read_bio_X509( @@ -996,7 +1194,10 @@ class Backend(object): ) if x509 == self._ffi.NULL: self._consume_errors() - raise ValueError("Unable to load certificate") + raise ValueError( + "Unable to load certificate. See https://cryptography.io/en/la" + "test/faq/#why-can-t-i-import-my-pem-file for more details." + ) x509 = self._ffi.gc(x509, self._lib.X509_free) return _Certificate(self, x509) @@ -1011,6 +1212,31 @@ class Backend(object): x509 = self._ffi.gc(x509, self._lib.X509_free) return _Certificate(self, x509) + def load_pem_x509_crl(self, data): + mem_bio = self._bytes_to_bio(data) + x509_crl = self._lib.PEM_read_bio_X509_CRL( + mem_bio.bio, self._ffi.NULL, self._ffi.NULL, self._ffi.NULL + ) + if x509_crl == self._ffi.NULL: + self._consume_errors() + raise ValueError( + "Unable to load CRL. See https://cryptography.io/en/la" + "test/faq/#why-can-t-i-import-my-pem-file for more details." + ) + + x509_crl = self._ffi.gc(x509_crl, self._lib.X509_CRL_free) + return _CertificateRevocationList(self, x509_crl) + + def load_der_x509_crl(self, data): + mem_bio = self._bytes_to_bio(data) + x509_crl = self._lib.d2i_X509_CRL_bio(mem_bio.bio, self._ffi.NULL) + if x509_crl == self._ffi.NULL: + self._consume_errors() + raise ValueError("Unable to load CRL") + + x509_crl = self._ffi.gc(x509_crl, self._lib.X509_CRL_free) + return _CertificateRevocationList(self, x509_crl) + def load_pem_x509_csr(self, data): mem_bio = self._bytes_to_bio(data) x509_req = self._lib.PEM_read_bio_X509_REQ( @@ -1018,7 +1244,10 @@ class Backend(object): ) if x509_req == self._ffi.NULL: self._consume_errors() - raise ValueError("Unable to load request") + raise ValueError( + "Unable to load request. See https://cryptography.io/en/la" + "test/faq/#why-can-t-i-import-my-pem-file for more details." + ) x509_req = self._ffi.gc(x509_req, self._lib.X509_REQ_free) return _CertificateSigningRequest(self, x509_req) @@ -1036,31 +1265,47 @@ class Backend(object): def _load_key(self, openssl_read_func, convert_func, data, password): mem_bio = self._bytes_to_bio(data) - password_callback, password_func = self._pem_password_cb(password) + userdata = self._ffi.new("CRYPTOGRAPHY_PASSWORD_DATA *") + if password is not None: + utils._check_byteslike("password", password) + password_ptr = self._ffi.from_buffer(password) + userdata.password = password_ptr + userdata.length = len(password) evp_pkey = openssl_read_func( mem_bio.bio, self._ffi.NULL, - password_callback, - self._ffi.NULL + self._ffi.addressof( + self._lib._original_lib, "Cryptography_pem_password_cb" + ), + userdata, ) if evp_pkey == self._ffi.NULL: - if password_func.exception is not None: + if userdata.error != 0: errors = self._consume_errors() - assert errors - raise password_func.exception + self.openssl_assert(errors) + if userdata.error == -1: + raise TypeError( + "Password was not given but private key is encrypted" + ) + else: + assert userdata.error == -2 + raise ValueError( + "Passwords longer than {} bytes are not supported " + "by this backend.".format(userdata.maxsize - 1) + ) else: self._handle_key_loading_error() evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free) - if password is not None and password_func.called == 0: + if password is not None and userdata.called == 0: raise TypeError( "Password was given but private key is not encrypted.") assert ( - (password is not None and password_func.called == 1) or + (password is not None and userdata.called == 1) or password is None ) @@ -1070,33 +1315,23 @@ class Backend(object): errors = self._consume_errors() if not errors: - raise ValueError("Could not unserialize key data.") + raise ValueError("Could not deserialize key data.") - elif errors[0][1:] in ( - ( - self._lib.ERR_LIB_EVP, - self._lib.EVP_F_EVP_DECRYPTFINAL_EX, - self._lib.EVP_R_BAD_DECRYPT - ), - ( + elif ( + errors[0]._lib_reason_match( + self._lib.ERR_LIB_EVP, self._lib.EVP_R_BAD_DECRYPT + ) or errors[0]._lib_reason_match( self._lib.ERR_LIB_PKCS12, - self._lib.PKCS12_F_PKCS12_PBE_CRYPT, - self._lib.PKCS12_R_PKCS12_CIPHERFINAL_ERROR, + self._lib.PKCS12_R_PKCS12_CIPHERFINAL_ERROR ) ): raise ValueError("Bad decrypt. Incorrect password?") - elif errors[0][1:] in ( - ( - self._lib.ERR_LIB_PEM, - self._lib.PEM_F_PEM_GET_EVP_CIPHER_INFO, - self._lib.PEM_R_UNSUPPORTED_ENCRYPTION - ), - - ( - self._lib.ERR_LIB_EVP, - self._lib.EVP_F_EVP_PBE_CIPHERINIT, - self._lib.EVP_R_UNKNOWN_PBE_ALGORITHM + elif ( + errors[0]._lib_reason_match( + self._lib.ERR_LIB_EVP, self._lib.EVP_R_UNKNOWN_PBE_ALGORITHM + ) or errors[0]._lib_reason_match( + self._lib.ERR_LIB_PEM, self._lib.PEM_R_UNSUPPORTED_ENCRYPTION ) ): raise UnsupportedAlgorithm( @@ -1105,70 +1340,52 @@ class Backend(object): ) elif any( - error[1:] == ( + error._lib_reason_match( self._lib.ERR_LIB_EVP, - self._lib.EVP_F_EVP_PKCS82PKEY, self._lib.EVP_R_UNSUPPORTED_PRIVATE_KEY_ALGORITHM ) for error in errors ): - raise UnsupportedAlgorithm( - "Unsupported public key algorithm.", - _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM - ) + raise ValueError("Unsupported public key algorithm.") else: - assert errors[0][1] in ( + assert errors[0].lib in ( self._lib.ERR_LIB_EVP, self._lib.ERR_LIB_PEM, self._lib.ERR_LIB_ASN1, ) - raise ValueError("Could not unserialize key data.") + raise ValueError("Could not deserialize key data.") def elliptic_curve_supported(self, curve): - if self._lib.Cryptography_HAS_EC != 1: - return False - try: curve_nid = self._elliptic_curve_to_nid(curve) except UnsupportedAlgorithm: curve_nid = self._lib.NID_undef - ctx = self._lib.EC_GROUP_new_by_curve_name(curve_nid) + group = self._lib.EC_GROUP_new_by_curve_name(curve_nid) - if ctx == self._ffi.NULL: + if group == self._ffi.NULL: errors = self._consume_errors() - assert ( + self.openssl_assert( curve_nid == self._lib.NID_undef or - errors[0][1:] == ( + errors[0]._lib_reason_match( self._lib.ERR_LIB_EC, - self._lib.EC_F_EC_GROUP_NEW_BY_CURVE_NAME, self._lib.EC_R_UNKNOWN_GROUP ) ) return False else: - assert curve_nid != self._lib.NID_undef - self._lib.EC_GROUP_free(ctx) + self.openssl_assert(curve_nid != self._lib.NID_undef) + self._lib.EC_GROUP_free(group) return True def elliptic_curve_signature_algorithm_supported( self, signature_algorithm, curve ): - if self._lib.Cryptography_HAS_EC != 1: - return False - # We only support ECDSA right now. if not isinstance(signature_algorithm, ec.ECDSA): return False - # Before 0.9.8m OpenSSL can't cope with digests longer than the curve. - if ( - self._lib.OPENSSL_VERSION_NUMBER < 0x009080df and - curve.key_size < signature_algorithm.algorithm.digest_size * 8 - ): - return False - return self.elliptic_curve_supported(curve) def generate_elliptic_curve_private_key(self, curve): @@ -1177,65 +1394,255 @@ class Backend(object): """ if self.elliptic_curve_supported(curve): - curve_nid = self._elliptic_curve_to_nid(curve) - - ec_cdata = self._lib.EC_KEY_new_by_curve_name(curve_nid) - assert ec_cdata != self._ffi.NULL - ec_cdata = self._ffi.gc(ec_cdata, self._lib.EC_KEY_free) + ec_cdata = self._ec_key_new_by_curve(curve) res = self._lib.EC_KEY_generate_key(ec_cdata) - assert res == 1 - - res = self._lib.EC_KEY_check_key(ec_cdata) - assert res == 1 + self.openssl_assert(res == 1) evp_pkey = self._ec_cdata_to_evp_pkey(ec_cdata) return _EllipticCurvePrivateKey(self, ec_cdata, evp_pkey) else: raise UnsupportedAlgorithm( - "Backend object does not support {0}.".format(curve.name), + "Backend object does not support {}.".format(curve.name), _Reasons.UNSUPPORTED_ELLIPTIC_CURVE ) def load_elliptic_curve_private_numbers(self, numbers): public = numbers.public_numbers - curve_nid = self._elliptic_curve_to_nid(public.curve) + ec_cdata = self._ec_key_new_by_curve(public.curve) - ec_cdata = self._lib.EC_KEY_new_by_curve_name(curve_nid) - assert ec_cdata != self._ffi.NULL - ec_cdata = self._ffi.gc(ec_cdata, self._lib.EC_KEY_free) + private_value = self._ffi.gc( + self._int_to_bn(numbers.private_value), self._lib.BN_clear_free + ) + res = self._lib.EC_KEY_set_private_key(ec_cdata, private_value) + self.openssl_assert(res == 1) ec_cdata = self._ec_key_set_public_key_affine_coordinates( ec_cdata, public.x, public.y) - res = self._lib.EC_KEY_set_private_key( - ec_cdata, self._int_to_bn(numbers.private_value)) - assert res == 1 evp_pkey = self._ec_cdata_to_evp_pkey(ec_cdata) return _EllipticCurvePrivateKey(self, ec_cdata, evp_pkey) def load_elliptic_curve_public_numbers(self, numbers): - curve_nid = self._elliptic_curve_to_nid(numbers.curve) - - ec_cdata = self._lib.EC_KEY_new_by_curve_name(curve_nid) - assert ec_cdata != self._ffi.NULL - ec_cdata = self._ffi.gc(ec_cdata, self._lib.EC_KEY_free) - + ec_cdata = self._ec_key_new_by_curve(numbers.curve) ec_cdata = self._ec_key_set_public_key_affine_coordinates( ec_cdata, numbers.x, numbers.y) evp_pkey = self._ec_cdata_to_evp_pkey(ec_cdata) return _EllipticCurvePublicKey(self, ec_cdata, evp_pkey) + def load_elliptic_curve_public_bytes(self, curve, point_bytes): + ec_cdata = self._ec_key_new_by_curve(curve) + group = self._lib.EC_KEY_get0_group(ec_cdata) + self.openssl_assert(group != self._ffi.NULL) + point = self._lib.EC_POINT_new(group) + self.openssl_assert(point != self._ffi.NULL) + point = self._ffi.gc(point, self._lib.EC_POINT_free) + with self._tmp_bn_ctx() as bn_ctx: + res = self._lib.EC_POINT_oct2point( + group, point, point_bytes, len(point_bytes), bn_ctx + ) + if res != 1: + self._consume_errors() + raise ValueError("Invalid public bytes for the given curve") + + res = self._lib.EC_KEY_set_public_key(ec_cdata, point) + self.openssl_assert(res == 1) + evp_pkey = self._ec_cdata_to_evp_pkey(ec_cdata) + return _EllipticCurvePublicKey(self, ec_cdata, evp_pkey) + + def derive_elliptic_curve_private_key(self, private_value, curve): + ec_cdata = self._ec_key_new_by_curve(curve) + + get_func, group = self._ec_key_determine_group_get_func(ec_cdata) + + point = self._lib.EC_POINT_new(group) + self.openssl_assert(point != self._ffi.NULL) + point = self._ffi.gc(point, self._lib.EC_POINT_free) + + value = self._int_to_bn(private_value) + value = self._ffi.gc(value, self._lib.BN_clear_free) + + with self._tmp_bn_ctx() as bn_ctx: + res = self._lib.EC_POINT_mul(group, point, value, self._ffi.NULL, + self._ffi.NULL, bn_ctx) + self.openssl_assert(res == 1) + + bn_x = self._lib.BN_CTX_get(bn_ctx) + bn_y = self._lib.BN_CTX_get(bn_ctx) + + res = get_func(group, point, bn_x, bn_y, bn_ctx) + self.openssl_assert(res == 1) + + res = self._lib.EC_KEY_set_public_key(ec_cdata, point) + self.openssl_assert(res == 1) + private = self._int_to_bn(private_value) + private = self._ffi.gc(private, self._lib.BN_clear_free) + res = self._lib.EC_KEY_set_private_key(ec_cdata, private) + self.openssl_assert(res == 1) + + evp_pkey = self._ec_cdata_to_evp_pkey(ec_cdata) + + return _EllipticCurvePrivateKey(self, ec_cdata, evp_pkey) + + def _ec_key_new_by_curve(self, curve): + curve_nid = self._elliptic_curve_to_nid(curve) + ec_cdata = self._lib.EC_KEY_new_by_curve_name(curve_nid) + self.openssl_assert(ec_cdata != self._ffi.NULL) + return self._ffi.gc(ec_cdata, self._lib.EC_KEY_free) + + def load_der_ocsp_request(self, data): + mem_bio = self._bytes_to_bio(data) + request = self._lib.d2i_OCSP_REQUEST_bio(mem_bio.bio, self._ffi.NULL) + if request == self._ffi.NULL: + self._consume_errors() + raise ValueError("Unable to load OCSP request") + + request = self._ffi.gc(request, self._lib.OCSP_REQUEST_free) + return _OCSPRequest(self, request) + + def load_der_ocsp_response(self, data): + mem_bio = self._bytes_to_bio(data) + response = self._lib.d2i_OCSP_RESPONSE_bio(mem_bio.bio, self._ffi.NULL) + if response == self._ffi.NULL: + self._consume_errors() + raise ValueError("Unable to load OCSP response") + + response = self._ffi.gc(response, self._lib.OCSP_RESPONSE_free) + return _OCSPResponse(self, response) + + def create_ocsp_request(self, builder): + ocsp_req = self._lib.OCSP_REQUEST_new() + self.openssl_assert(ocsp_req != self._ffi.NULL) + ocsp_req = self._ffi.gc(ocsp_req, self._lib.OCSP_REQUEST_free) + cert, issuer, algorithm = builder._request + evp_md = self._evp_md_non_null_from_algorithm(algorithm) + certid = self._lib.OCSP_cert_to_id( + evp_md, cert._x509, issuer._x509 + ) + self.openssl_assert(certid != self._ffi.NULL) + onereq = self._lib.OCSP_request_add0_id(ocsp_req, certid) + self.openssl_assert(onereq != self._ffi.NULL) + self._create_x509_extensions( + extensions=builder._extensions, + handlers=_OCSP_REQUEST_EXTENSION_ENCODE_HANDLERS, + x509_obj=ocsp_req, + add_func=self._lib.OCSP_REQUEST_add_ext, + gc=True, + ) + return _OCSPRequest(self, ocsp_req) + + def _create_ocsp_basic_response(self, builder, private_key, algorithm): + self._x509_check_signature_params(private_key, algorithm) + + basic = self._lib.OCSP_BASICRESP_new() + self.openssl_assert(basic != self._ffi.NULL) + basic = self._ffi.gc(basic, self._lib.OCSP_BASICRESP_free) + evp_md = self._evp_md_non_null_from_algorithm( + builder._response._algorithm + ) + certid = self._lib.OCSP_cert_to_id( + evp_md, builder._response._cert._x509, + builder._response._issuer._x509 + ) + self.openssl_assert(certid != self._ffi.NULL) + certid = self._ffi.gc(certid, self._lib.OCSP_CERTID_free) + if builder._response._revocation_reason is None: + reason = -1 + else: + reason = _CRL_ENTRY_REASON_ENUM_TO_CODE[ + builder._response._revocation_reason + ] + if builder._response._revocation_time is None: + rev_time = self._ffi.NULL + else: + rev_time = self._create_asn1_time( + builder._response._revocation_time + ) + + next_update = self._ffi.NULL + if builder._response._next_update is not None: + next_update = self._create_asn1_time( + builder._response._next_update + ) + + this_update = self._create_asn1_time(builder._response._this_update) + + res = self._lib.OCSP_basic_add1_status( + basic, + certid, + builder._response._cert_status.value, + reason, + rev_time, + this_update, + next_update + ) + self.openssl_assert(res != self._ffi.NULL) + # okay, now sign the basic structure + evp_md = self._evp_md_x509_null_if_eddsa(private_key, algorithm) + responder_cert, responder_encoding = builder._responder_id + flags = self._lib.OCSP_NOCERTS + if responder_encoding is ocsp.OCSPResponderEncoding.HASH: + flags |= self._lib.OCSP_RESPID_KEY + + if builder._certs is not None: + for cert in builder._certs: + res = self._lib.OCSP_basic_add1_cert(basic, cert._x509) + self.openssl_assert(res == 1) + + self._create_x509_extensions( + extensions=builder._extensions, + handlers=_OCSP_BASICRESP_EXTENSION_ENCODE_HANDLERS, + x509_obj=basic, + add_func=self._lib.OCSP_BASICRESP_add_ext, + gc=True, + ) + + res = self._lib.OCSP_basic_sign( + basic, responder_cert._x509, private_key._evp_pkey, + evp_md, self._ffi.NULL, flags + ) + if res != 1: + errors = self._consume_errors() + self.openssl_assert( + errors[0]._lib_reason_match( + self._lib.ERR_LIB_X509, + self._lib.X509_R_KEY_VALUES_MISMATCH + ) + ) + raise ValueError("responder_cert must be signed by private_key") + + return basic + + def create_ocsp_response(self, response_status, builder, private_key, + algorithm): + if response_status is ocsp.OCSPResponseStatus.SUCCESSFUL: + basic = self._create_ocsp_basic_response( + builder, private_key, algorithm + ) + else: + basic = self._ffi.NULL + + ocsp_resp = self._lib.OCSP_response_create( + response_status.value, basic + ) + self.openssl_assert(ocsp_resp != self._ffi.NULL) + ocsp_resp = self._ffi.gc(ocsp_resp, self._lib.OCSP_RESPONSE_free) + return _OCSPResponse(self, ocsp_resp) + + def elliptic_curve_exchange_algorithm_supported(self, algorithm, curve): + return ( + self.elliptic_curve_supported(curve) and + isinstance(algorithm, ec.ECDH) + ) + def _ec_cdata_to_evp_pkey(self, ec_cdata): - evp_pkey = self._lib.EVP_PKEY_new() - assert evp_pkey != self._ffi.NULL - evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free) + evp_pkey = self._create_evp_pkey_gc() res = self._lib.EVP_PKEY_set1_EC_KEY(evp_pkey, ec_cdata) - assert res == 1 + self.openssl_assert(res == 1) return evp_pkey def _elliptic_curve_to_nid(self, curve): @@ -1253,7 +1660,7 @@ class Backend(object): curve_nid = self._lib.OBJ_sn2nid(curve_name.encode()) if curve_nid == self._lib.NID_undef: raise UnsupportedAlgorithm( - "{0} is not a supported elliptic curve".format(curve.name), + "{} is not a supported elliptic curve".format(curve.name), _Reasons.UNSUPPORTED_ELLIPTIC_CURVE ) return curve_nid @@ -1261,7 +1668,7 @@ class Backend(object): @contextmanager def _tmp_bn_ctx(self): bn_ctx = self._lib.BN_CTX_new() - assert bn_ctx != self._ffi.NULL + self.openssl_assert(bn_ctx != self._ffi.NULL) bn_ctx = self._ffi.gc(bn_ctx, self._lib.BN_CTX_free) self._lib.BN_CTX_start(bn_ctx) try: @@ -1269,41 +1676,36 @@ class Backend(object): finally: self._lib.BN_CTX_end(bn_ctx) - def _ec_key_determine_group_get_set_funcs(self, ctx): + def _ec_key_determine_group_get_func(self, ctx): """ - Given an EC_KEY determine the group and what methods are required to - get/set point coordinates. + Given an EC_KEY determine the group and what function is required to + get point coordinates. """ - assert ctx != self._ffi.NULL + self.openssl_assert(ctx != self._ffi.NULL) nid_two_field = self._lib.OBJ_sn2nid(b"characteristic-two-field") - assert nid_two_field != self._lib.NID_undef + self.openssl_assert(nid_two_field != self._lib.NID_undef) group = self._lib.EC_KEY_get0_group(ctx) - assert group != self._ffi.NULL + self.openssl_assert(group != self._ffi.NULL) method = self._lib.EC_GROUP_method_of(group) - assert method != self._ffi.NULL + self.openssl_assert(method != self._ffi.NULL) nid = self._lib.EC_METHOD_get_field_type(method) - assert nid != self._lib.NID_undef + self.openssl_assert(nid != self._lib.NID_undef) if nid == nid_two_field and self._lib.Cryptography_HAS_EC2M: - set_func = self._lib.EC_POINT_set_affine_coordinates_GF2m get_func = self._lib.EC_POINT_get_affine_coordinates_GF2m else: - set_func = self._lib.EC_POINT_set_affine_coordinates_GFp get_func = self._lib.EC_POINT_get_affine_coordinates_GFp - assert set_func and get_func + assert get_func - return set_func, get_func, group + return get_func, group def _ec_key_set_public_key_affine_coordinates(self, ctx, x, y): """ - This is a port of EC_KEY_set_public_key_affine_coordinates that was - added in 1.0.1. - Sets the public key point in the EC_KEY context to the affine x and y values. """ @@ -1313,36 +1715,9 @@ class Backend(object): "Invalid EC key. Both x and y must be non-negative." ) - set_func, get_func, group = ( - self._ec_key_determine_group_get_set_funcs(ctx) - ) - - point = self._lib.EC_POINT_new(group) - assert point != self._ffi.NULL - point = self._ffi.gc(point, self._lib.EC_POINT_free) - - bn_x = self._int_to_bn(x) - bn_y = self._int_to_bn(y) - - with self._tmp_bn_ctx() as bn_ctx: - check_x = self._lib.BN_CTX_get(bn_ctx) - check_y = self._lib.BN_CTX_get(bn_ctx) - - res = set_func(group, point, bn_x, bn_y, bn_ctx) - assert res == 1 - - res = get_func(group, point, check_x, check_y, bn_ctx) - assert res == 1 - - res = self._lib.BN_cmp(bn_x, check_x) - assert res == 0 - res = self._lib.BN_cmp(bn_y, check_y) - assert res == 0 - - res = self._lib.EC_KEY_set_public_key(ctx, point) - assert res == 1 - - res = self._lib.EC_KEY_check_key(ctx) + x = self._ffi.gc(self._int_to_bn(x), self._lib.BN_free) + y = self._ffi.gc(self._int_to_bn(y), self._lib.BN_free) + res = self._lib.EC_KEY_set_public_key_affine_coordinates(ctx, x, y) if res != 1: self._consume_errors() raise ValueError("Invalid EC key.") @@ -1350,15 +1725,14 @@ class Backend(object): return ctx def _private_key_bytes(self, encoding, format, encryption_algorithm, - evp_pkey, cdata): + key, evp_pkey, cdata): + # validate argument types if not isinstance(encoding, serialization.Encoding): raise TypeError("encoding must be an item from the Encoding enum") - if not isinstance(format, serialization.PrivateFormat): raise TypeError( "format must be an item from the PrivateFormat enum" ) - if not isinstance(encryption_algorithm, serialization.KeySerializationEncryption): raise TypeError( @@ -1366,19 +1740,13 @@ class Backend(object): "instance" ) + # validate password if isinstance(encryption_algorithm, serialization.NoEncryption): password = b"" - passlen = 0 - evp_cipher = self._ffi.NULL elif isinstance(encryption_algorithm, serialization.BestAvailableEncryption): - # This is a curated value that we will update over time. - evp_cipher = self._lib.EVP_get_cipherbyname( - b"aes-256-cbc" - ) password = encryption_algorithm.password - passlen = len(password) - if passlen > 1023: + if len(password) > 1023: raise ValueError( "Passwords longer than 1023 bytes are not supported by " "this backend" @@ -1386,95 +1754,693 @@ class Backend(object): else: raise ValueError("Unsupported encryption type") - if encoding is serialization.Encoding.PEM: - if format is serialization.PrivateFormat.PKCS8: + # PKCS8 + PEM/DER + if format is serialization.PrivateFormat.PKCS8: + if encoding is serialization.Encoding.PEM: write_bio = self._lib.PEM_write_bio_PKCS8PrivateKey - key = evp_pkey - elif format is serialization.PrivateFormat.TraditionalOpenSSL: - if evp_pkey.type == self._lib.EVP_PKEY_RSA: + elif encoding is serialization.Encoding.DER: + write_bio = self._lib.i2d_PKCS8PrivateKey_bio + else: + raise ValueError("Unsupported encoding for PKCS8") + return self._private_key_bytes_via_bio( + write_bio, evp_pkey, password + ) + + # TraditionalOpenSSL + PEM/DER + if format is serialization.PrivateFormat.TraditionalOpenSSL: + key_type = self._lib.EVP_PKEY_id(evp_pkey) + + if encoding is serialization.Encoding.PEM: + if key_type == self._lib.EVP_PKEY_RSA: write_bio = self._lib.PEM_write_bio_RSAPrivateKey - elif evp_pkey.type == self._lib.EVP_PKEY_DSA: + elif key_type == self._lib.EVP_PKEY_DSA: write_bio = self._lib.PEM_write_bio_DSAPrivateKey - elif (self._lib.Cryptography_HAS_EC == 1 and - evp_pkey.type == self._lib.EVP_PKEY_EC): + elif key_type == self._lib.EVP_PKEY_EC: write_bio = self._lib.PEM_write_bio_ECPrivateKey + else: + raise ValueError( + "Unsupported key type for TraditionalOpenSSL" + ) + return self._private_key_bytes_via_bio( + write_bio, cdata, password + ) - key = cdata - elif encoding is serialization.Encoding.DER: - if format is serialization.PrivateFormat.TraditionalOpenSSL: - if not isinstance( - encryption_algorithm, serialization.NoEncryption - ): + if encoding is serialization.Encoding.DER: + if password: raise ValueError( "Encryption is not supported for DER encoded " "traditional OpenSSL keys" ) + if key_type == self._lib.EVP_PKEY_RSA: + write_bio = self._lib.i2d_RSAPrivateKey_bio + elif key_type == self._lib.EVP_PKEY_EC: + write_bio = self._lib.i2d_ECPrivateKey_bio + elif key_type == self._lib.EVP_PKEY_DSA: + write_bio = self._lib.i2d_DSAPrivateKey_bio + else: + raise ValueError( + "Unsupported key type for TraditionalOpenSSL" + ) + return self._bio_func_output(write_bio, cdata) - return self._private_key_bytes_traditional_der( - evp_pkey.type, cdata - ) - elif format is serialization.PrivateFormat.PKCS8: - write_bio = self._lib.i2d_PKCS8PrivateKey_bio - key = evp_pkey + raise ValueError( + "Unsupported encoding for TraditionalOpenSSL" + ) + + # Anything that key-specific code was supposed to handle earlier, + # like Raw. + raise ValueError("format is invalid with this key") + + def _private_key_bytes_via_bio(self, write_bio, evp_pkey, password): + if not password: + evp_cipher = self._ffi.NULL + else: + # This is a curated value that we will update over time. + evp_cipher = self._lib.EVP_get_cipherbyname(b"aes-256-cbc") - bio = self._create_mem_bio() - res = write_bio( - bio, - key, + return self._bio_func_output( + write_bio, + evp_pkey, evp_cipher, password, - passlen, + len(password), self._ffi.NULL, self._ffi.NULL ) - assert res == 1 - return self._read_mem_bio(bio) - def _private_key_bytes_traditional_der(self, key_type, cdata): - if key_type == self._lib.EVP_PKEY_RSA: - write_bio = self._lib.i2d_RSAPrivateKey_bio - elif (self._lib.Cryptography_HAS_EC == 1 and - key_type == self._lib.EVP_PKEY_EC): - write_bio = self._lib.i2d_ECPrivateKey_bio - elif key_type == self._lib.EVP_PKEY_DSA: - write_bio = self._lib.i2d_DSAPrivateKey_bio - - bio = self._create_mem_bio() - res = write_bio(bio, cdata) - assert res == 1 + def _bio_func_output(self, write_bio, *args): + bio = self._create_mem_bio_gc() + res = write_bio(bio, *args) + self.openssl_assert(res == 1) return self._read_mem_bio(bio) - def _public_key_bytes(self, encoding, format, evp_pkey, cdata): + def _public_key_bytes(self, encoding, format, key, evp_pkey, cdata): if not isinstance(encoding, serialization.Encoding): raise TypeError("encoding must be an item from the Encoding enum") - if not isinstance(format, serialization.PublicFormat): raise TypeError( "format must be an item from the PublicFormat enum" ) + # SubjectPublicKeyInfo + PEM/DER if format is serialization.PublicFormat.SubjectPublicKeyInfo: if encoding is serialization.Encoding.PEM: write_bio = self._lib.PEM_write_bio_PUBKEY elif encoding is serialization.Encoding.DER: write_bio = self._lib.i2d_PUBKEY_bio + else: + raise ValueError( + "SubjectPublicKeyInfo works only with PEM or DER encoding" + ) + return self._bio_func_output(write_bio, evp_pkey) - key = evp_pkey - elif format is serialization.PublicFormat.PKCS1: + # PKCS1 + PEM/DER + if format is serialization.PublicFormat.PKCS1: # Only RSA is supported here. - assert evp_pkey.type == self._lib.EVP_PKEY_RSA + key_type = self._lib.EVP_PKEY_id(evp_pkey) + if key_type != self._lib.EVP_PKEY_RSA: + raise ValueError("PKCS1 format is supported only for RSA keys") + if encoding is serialization.Encoding.PEM: write_bio = self._lib.PEM_write_bio_RSAPublicKey elif encoding is serialization.Encoding.DER: write_bio = self._lib.i2d_RSAPublicKey_bio + else: + raise ValueError( + "PKCS1 works only with PEM or DER encoding" + ) + return self._bio_func_output(write_bio, cdata) + + # OpenSSH + OpenSSH + if format is serialization.PublicFormat.OpenSSH: + if encoding is serialization.Encoding.OpenSSH: + return self._openssh_public_key_bytes(key) - key = cdata + raise ValueError( + "OpenSSH format must be used with OpenSSH encoding" + ) - bio = self._create_mem_bio() - res = write_bio(bio, key) - assert res == 1 + # Anything that key-specific code was supposed to handle earlier, + # like Raw, CompressedPoint, UncompressedPoint + raise ValueError("format is invalid with this key") + + def _openssh_public_key_bytes(self, key): + if isinstance(key, rsa.RSAPublicKey): + public_numbers = key.public_numbers() + return b"ssh-rsa " + base64.b64encode( + ssh._ssh_write_string(b"ssh-rsa") + + ssh._ssh_write_mpint(public_numbers.e) + + ssh._ssh_write_mpint(public_numbers.n) + ) + elif isinstance(key, dsa.DSAPublicKey): + public_numbers = key.public_numbers() + parameter_numbers = public_numbers.parameter_numbers + return b"ssh-dss " + base64.b64encode( + ssh._ssh_write_string(b"ssh-dss") + + ssh._ssh_write_mpint(parameter_numbers.p) + + ssh._ssh_write_mpint(parameter_numbers.q) + + ssh._ssh_write_mpint(parameter_numbers.g) + + ssh._ssh_write_mpint(public_numbers.y) + ) + elif isinstance(key, ed25519.Ed25519PublicKey): + raw_bytes = key.public_bytes(serialization.Encoding.Raw, + serialization.PublicFormat.Raw) + return b"ssh-ed25519 " + base64.b64encode( + ssh._ssh_write_string(b"ssh-ed25519") + + ssh._ssh_write_string(raw_bytes) + ) + elif isinstance(key, ec.EllipticCurvePublicKey): + public_numbers = key.public_numbers() + try: + curve_name = { + ec.SECP256R1: b"nistp256", + ec.SECP384R1: b"nistp384", + ec.SECP521R1: b"nistp521", + }[type(public_numbers.curve)] + except KeyError: + raise ValueError( + "Only SECP256R1, SECP384R1, and SECP521R1 curves are " + "supported by the SSH public key format" + ) + + point = key.public_bytes( + serialization.Encoding.X962, + serialization.PublicFormat.UncompressedPoint + ) + return b"ecdsa-sha2-" + curve_name + b" " + base64.b64encode( + ssh._ssh_write_string(b"ecdsa-sha2-" + curve_name) + + ssh._ssh_write_string(curve_name) + + ssh._ssh_write_string(point) + ) + else: + raise ValueError( + "OpenSSH encoding is not supported for this key type" + ) + + def _parameter_bytes(self, encoding, format, cdata): + if encoding is serialization.Encoding.OpenSSH: + raise TypeError( + "OpenSSH encoding is not supported" + ) + + # Only DH is supported here currently. + q = self._ffi.new("BIGNUM **") + self._lib.DH_get0_pqg(cdata, + self._ffi.NULL, + q, + self._ffi.NULL) + if encoding is serialization.Encoding.PEM: + if q[0] != self._ffi.NULL: + write_bio = self._lib.PEM_write_bio_DHxparams + else: + write_bio = self._lib.PEM_write_bio_DHparams + elif encoding is serialization.Encoding.DER: + if q[0] != self._ffi.NULL: + write_bio = self._lib.Cryptography_i2d_DHxparams_bio + else: + write_bio = self._lib.i2d_DHparams_bio + else: + raise TypeError("encoding must be an item from the Encoding enum") + + bio = self._create_mem_bio_gc() + res = write_bio(bio, cdata) + self.openssl_assert(res == 1) return self._read_mem_bio(bio) + def generate_dh_parameters(self, generator, key_size): + if key_size < 512: + raise ValueError("DH key_size must be at least 512 bits") + + if generator not in (2, 5): + raise ValueError("DH generator must be 2 or 5") + + dh_param_cdata = self._lib.DH_new() + self.openssl_assert(dh_param_cdata != self._ffi.NULL) + dh_param_cdata = self._ffi.gc(dh_param_cdata, self._lib.DH_free) + + res = self._lib.DH_generate_parameters_ex( + dh_param_cdata, + key_size, + generator, + self._ffi.NULL + ) + self.openssl_assert(res == 1) + + return _DHParameters(self, dh_param_cdata) + + def _dh_cdata_to_evp_pkey(self, dh_cdata): + evp_pkey = self._create_evp_pkey_gc() + res = self._lib.EVP_PKEY_set1_DH(evp_pkey, dh_cdata) + self.openssl_assert(res == 1) + return evp_pkey + + def generate_dh_private_key(self, parameters): + dh_key_cdata = _dh_params_dup(parameters._dh_cdata, self) + + res = self._lib.DH_generate_key(dh_key_cdata) + self.openssl_assert(res == 1) + + evp_pkey = self._dh_cdata_to_evp_pkey(dh_key_cdata) + + return _DHPrivateKey(self, dh_key_cdata, evp_pkey) + + def generate_dh_private_key_and_parameters(self, generator, key_size): + return self.generate_dh_private_key( + self.generate_dh_parameters(generator, key_size)) + + def load_dh_private_numbers(self, numbers): + parameter_numbers = numbers.public_numbers.parameter_numbers + + dh_cdata = self._lib.DH_new() + self.openssl_assert(dh_cdata != self._ffi.NULL) + dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free) + + p = self._int_to_bn(parameter_numbers.p) + g = self._int_to_bn(parameter_numbers.g) + + if parameter_numbers.q is not None: + q = self._int_to_bn(parameter_numbers.q) + else: + q = self._ffi.NULL + + pub_key = self._int_to_bn(numbers.public_numbers.y) + priv_key = self._int_to_bn(numbers.x) + + res = self._lib.DH_set0_pqg(dh_cdata, p, q, g) + self.openssl_assert(res == 1) + + res = self._lib.DH_set0_key(dh_cdata, pub_key, priv_key) + self.openssl_assert(res == 1) + + codes = self._ffi.new("int[]", 1) + res = self._lib.Cryptography_DH_check(dh_cdata, codes) + self.openssl_assert(res == 1) + + # DH_check will return DH_NOT_SUITABLE_GENERATOR if p % 24 does not + # equal 11 when the generator is 2 (a quadratic nonresidue). + # We want to ignore that error because p % 24 == 23 is also fine. + # Specifically, g is then a quadratic residue. Within the context of + # Diffie-Hellman this means it can only generate half the possible + # values. That sounds bad, but quadratic nonresidues leak a bit of + # the key to the attacker in exchange for having the full key space + # available. See: https://crypto.stackexchange.com/questions/12961 + if codes[0] != 0 and not ( + parameter_numbers.g == 2 and + codes[0] ^ self._lib.DH_NOT_SUITABLE_GENERATOR == 0 + ): + raise ValueError( + "DH private numbers did not pass safety checks." + ) + + evp_pkey = self._dh_cdata_to_evp_pkey(dh_cdata) + + return _DHPrivateKey(self, dh_cdata, evp_pkey) + + def load_dh_public_numbers(self, numbers): + dh_cdata = self._lib.DH_new() + self.openssl_assert(dh_cdata != self._ffi.NULL) + dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free) + + parameter_numbers = numbers.parameter_numbers + + p = self._int_to_bn(parameter_numbers.p) + g = self._int_to_bn(parameter_numbers.g) + + if parameter_numbers.q is not None: + q = self._int_to_bn(parameter_numbers.q) + else: + q = self._ffi.NULL + + pub_key = self._int_to_bn(numbers.y) + + res = self._lib.DH_set0_pqg(dh_cdata, p, q, g) + self.openssl_assert(res == 1) + + res = self._lib.DH_set0_key(dh_cdata, pub_key, self._ffi.NULL) + self.openssl_assert(res == 1) + + evp_pkey = self._dh_cdata_to_evp_pkey(dh_cdata) + + return _DHPublicKey(self, dh_cdata, evp_pkey) + + def load_dh_parameter_numbers(self, numbers): + dh_cdata = self._lib.DH_new() + self.openssl_assert(dh_cdata != self._ffi.NULL) + dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free) + + p = self._int_to_bn(numbers.p) + g = self._int_to_bn(numbers.g) + + if numbers.q is not None: + q = self._int_to_bn(numbers.q) + else: + q = self._ffi.NULL + + res = self._lib.DH_set0_pqg(dh_cdata, p, q, g) + self.openssl_assert(res == 1) + + return _DHParameters(self, dh_cdata) + + def dh_parameters_supported(self, p, g, q=None): + dh_cdata = self._lib.DH_new() + self.openssl_assert(dh_cdata != self._ffi.NULL) + dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free) + + p = self._int_to_bn(p) + g = self._int_to_bn(g) + + if q is not None: + q = self._int_to_bn(q) + else: + q = self._ffi.NULL + + res = self._lib.DH_set0_pqg(dh_cdata, p, q, g) + self.openssl_assert(res == 1) + + codes = self._ffi.new("int[]", 1) + res = self._lib.Cryptography_DH_check(dh_cdata, codes) + self.openssl_assert(res == 1) + + return codes[0] == 0 + + def dh_x942_serialization_supported(self): + return self._lib.Cryptography_HAS_EVP_PKEY_DHX == 1 + + def x509_name_bytes(self, name): + x509_name = _encode_name_gc(self, name) + pp = self._ffi.new("unsigned char **") + res = self._lib.i2d_X509_NAME(x509_name, pp) + self.openssl_assert(pp[0] != self._ffi.NULL) + pp = self._ffi.gc( + pp, lambda pointer: self._lib.OPENSSL_free(pointer[0]) + ) + self.openssl_assert(res > 0) + return self._ffi.buffer(pp[0], res)[:] + + def x25519_load_public_bytes(self, data): + # When we drop support for CRYPTOGRAPHY_OPENSSL_LESS_THAN_111 we can + # switch this to EVP_PKEY_new_raw_public_key + if len(data) != 32: + raise ValueError("An X25519 public key is 32 bytes long") + + evp_pkey = self._create_evp_pkey_gc() + res = self._lib.EVP_PKEY_set_type(evp_pkey, self._lib.NID_X25519) + self.openssl_assert(res == 1) + res = self._lib.EVP_PKEY_set1_tls_encodedpoint( + evp_pkey, data, len(data) + ) + self.openssl_assert(res == 1) + return _X25519PublicKey(self, evp_pkey) + + def x25519_load_private_bytes(self, data): + # When we drop support for CRYPTOGRAPHY_OPENSSL_LESS_THAN_111 we can + # switch this to EVP_PKEY_new_raw_private_key and drop the + # zeroed_bytearray garbage. + # OpenSSL only has facilities for loading PKCS8 formatted private + # keys using the algorithm identifiers specified in + # https://tools.ietf.org/html/draft-ietf-curdle-pkix-09. + # This is the standard PKCS8 prefix for a 32 byte X25519 key. + # The form is: + # 0:d=0 hl=2 l= 46 cons: SEQUENCE + # 2:d=1 hl=2 l= 1 prim: INTEGER :00 + # 5:d=1 hl=2 l= 5 cons: SEQUENCE + # 7:d=2 hl=2 l= 3 prim: OBJECT :1.3.101.110 + # 12:d=1 hl=2 l= 34 prim: OCTET STRING (the key) + # Of course there's a bit more complexity. In reality OCTET STRING + # contains an OCTET STRING of length 32! So the last two bytes here + # are \x04\x20, which is an OCTET STRING of length 32. + if len(data) != 32: + raise ValueError("An X25519 private key is 32 bytes long") + + pkcs8_prefix = b'0.\x02\x01\x000\x05\x06\x03+en\x04"\x04 ' + with self._zeroed_bytearray(48) as ba: + ba[0:16] = pkcs8_prefix + ba[16:] = data + bio = self._bytes_to_bio(ba) + evp_pkey = self._lib.d2i_PrivateKey_bio(bio.bio, self._ffi.NULL) + + self.openssl_assert(evp_pkey != self._ffi.NULL) + evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free) + self.openssl_assert( + self._lib.EVP_PKEY_id(evp_pkey) == self._lib.EVP_PKEY_X25519 + ) + return _X25519PrivateKey(self, evp_pkey) + + def _evp_pkey_keygen_gc(self, nid): + evp_pkey_ctx = self._lib.EVP_PKEY_CTX_new_id(nid, self._ffi.NULL) + self.openssl_assert(evp_pkey_ctx != self._ffi.NULL) + evp_pkey_ctx = self._ffi.gc(evp_pkey_ctx, self._lib.EVP_PKEY_CTX_free) + res = self._lib.EVP_PKEY_keygen_init(evp_pkey_ctx) + self.openssl_assert(res == 1) + evp_ppkey = self._ffi.new("EVP_PKEY **") + res = self._lib.EVP_PKEY_keygen(evp_pkey_ctx, evp_ppkey) + self.openssl_assert(res == 1) + self.openssl_assert(evp_ppkey[0] != self._ffi.NULL) + evp_pkey = self._ffi.gc(evp_ppkey[0], self._lib.EVP_PKEY_free) + return evp_pkey + + def x25519_generate_key(self): + evp_pkey = self._evp_pkey_keygen_gc(self._lib.NID_X25519) + return _X25519PrivateKey(self, evp_pkey) + + def x25519_supported(self): + return self._lib.CRYPTOGRAPHY_OPENSSL_110_OR_GREATER + + def x448_load_public_bytes(self, data): + if len(data) != 56: + raise ValueError("An X448 public key is 56 bytes long") + + evp_pkey = self._lib.EVP_PKEY_new_raw_public_key( + self._lib.NID_X448, self._ffi.NULL, data, len(data) + ) + self.openssl_assert(evp_pkey != self._ffi.NULL) + evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free) + return _X448PublicKey(self, evp_pkey) + + def x448_load_private_bytes(self, data): + if len(data) != 56: + raise ValueError("An X448 private key is 56 bytes long") + + data_ptr = self._ffi.from_buffer(data) + evp_pkey = self._lib.EVP_PKEY_new_raw_private_key( + self._lib.NID_X448, self._ffi.NULL, data_ptr, len(data) + ) + self.openssl_assert(evp_pkey != self._ffi.NULL) + evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free) + return _X448PrivateKey(self, evp_pkey) + + def x448_generate_key(self): + evp_pkey = self._evp_pkey_keygen_gc(self._lib.NID_X448) + return _X448PrivateKey(self, evp_pkey) + + def x448_supported(self): + return not self._lib.CRYPTOGRAPHY_OPENSSL_LESS_THAN_111 + + def ed25519_supported(self): + return not self._lib.CRYPTOGRAPHY_OPENSSL_LESS_THAN_111B + + def ed25519_load_public_bytes(self, data): + utils._check_bytes("data", data) + + if len(data) != ed25519._ED25519_KEY_SIZE: + raise ValueError("An Ed25519 public key is 32 bytes long") + + evp_pkey = self._lib.EVP_PKEY_new_raw_public_key( + self._lib.NID_ED25519, self._ffi.NULL, data, len(data) + ) + self.openssl_assert(evp_pkey != self._ffi.NULL) + evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free) + + return _Ed25519PublicKey(self, evp_pkey) + + def ed25519_load_private_bytes(self, data): + if len(data) != ed25519._ED25519_KEY_SIZE: + raise ValueError("An Ed25519 private key is 32 bytes long") + + utils._check_byteslike("data", data) + data_ptr = self._ffi.from_buffer(data) + evp_pkey = self._lib.EVP_PKEY_new_raw_private_key( + self._lib.NID_ED25519, self._ffi.NULL, data_ptr, len(data) + ) + self.openssl_assert(evp_pkey != self._ffi.NULL) + evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free) + + return _Ed25519PrivateKey(self, evp_pkey) + + def ed25519_generate_key(self): + evp_pkey = self._evp_pkey_keygen_gc(self._lib.NID_ED25519) + return _Ed25519PrivateKey(self, evp_pkey) + + def ed448_supported(self): + return not self._lib.CRYPTOGRAPHY_OPENSSL_LESS_THAN_111B + + def ed448_load_public_bytes(self, data): + utils._check_bytes("data", data) + if len(data) != _ED448_KEY_SIZE: + raise ValueError("An Ed448 public key is 57 bytes long") + + evp_pkey = self._lib.EVP_PKEY_new_raw_public_key( + self._lib.NID_ED448, self._ffi.NULL, data, len(data) + ) + self.openssl_assert(evp_pkey != self._ffi.NULL) + evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free) + + return _Ed448PublicKey(self, evp_pkey) + + def ed448_load_private_bytes(self, data): + utils._check_byteslike("data", data) + if len(data) != _ED448_KEY_SIZE: + raise ValueError("An Ed448 private key is 57 bytes long") + + data_ptr = self._ffi.from_buffer(data) + evp_pkey = self._lib.EVP_PKEY_new_raw_private_key( + self._lib.NID_ED448, self._ffi.NULL, data_ptr, len(data) + ) + self.openssl_assert(evp_pkey != self._ffi.NULL) + evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free) + + return _Ed448PrivateKey(self, evp_pkey) + + def ed448_generate_key(self): + evp_pkey = self._evp_pkey_keygen_gc(self._lib.NID_ED448) + return _Ed448PrivateKey(self, evp_pkey) + + def derive_scrypt(self, key_material, salt, length, n, r, p): + buf = self._ffi.new("unsigned char[]", length) + key_material_ptr = self._ffi.from_buffer(key_material) + res = self._lib.EVP_PBE_scrypt( + key_material_ptr, len(key_material), salt, len(salt), n, r, p, + scrypt._MEM_LIMIT, buf, length + ) + if res != 1: + errors = self._consume_errors() + if not self._lib.CRYPTOGRAPHY_OPENSSL_LESS_THAN_111: + # This error is only added to the stack in 1.1.1+ + self.openssl_assert( + errors[0]._lib_reason_match( + self._lib.ERR_LIB_EVP, + self._lib.ERR_R_MALLOC_FAILURE + ) or + errors[0]._lib_reason_match( + self._lib.ERR_LIB_EVP, + self._lib.EVP_R_MEMORY_LIMIT_EXCEEDED + ) + ) + + # memory required formula explained here: + # https://blog.filippo.io/the-scrypt-parameters/ + min_memory = 128 * n * r // (1024**2) + raise MemoryError( + "Not enough memory to derive key. These parameters require" + " {} MB of memory.".format(min_memory) + ) + return self._ffi.buffer(buf)[:] + + def aead_cipher_supported(self, cipher): + cipher_name = aead._aead_cipher_name(cipher) + return ( + self._lib.EVP_get_cipherbyname(cipher_name) != self._ffi.NULL + ) + + @contextlib.contextmanager + def _zeroed_bytearray(self, length): + """ + This method creates a bytearray, which we copy data into (hopefully + also from a mutable buffer that can be dynamically erased!), and then + zero when we're done. + """ + ba = bytearray(length) + try: + yield ba + finally: + self._zero_data(ba, length) + + def _zero_data(self, data, length): + # We clear things this way because at the moment we're not + # sure of a better way that can guarantee it overwrites the + # memory of a bytearray and doesn't just replace the underlying char *. + for i in range(length): + data[i] = 0 + + @contextlib.contextmanager + def _zeroed_null_terminated_buf(self, data): + """ + This method takes bytes, which can be a bytestring or a mutable + buffer like a bytearray, and yields a null-terminated version of that + data. This is required because PKCS12_parse doesn't take a length with + its password char * and ffi.from_buffer doesn't provide null + termination. So, to support zeroing the data via bytearray we + need to build this ridiculous construct that copies the memory, but + zeroes it after use. + """ + if data is None: + yield self._ffi.NULL + else: + data_len = len(data) + buf = self._ffi.new("char[]", data_len + 1) + self._ffi.memmove(buf, data, data_len) + try: + yield buf + finally: + # Cast to a uint8_t * so we can assign by integer + self._zero_data(self._ffi.cast("uint8_t *", buf), data_len) + + def load_key_and_certificates_from_pkcs12(self, data, password): + if password is not None: + utils._check_byteslike("password", password) + + bio = self._bytes_to_bio(data) + p12 = self._lib.d2i_PKCS12_bio(bio.bio, self._ffi.NULL) + if p12 == self._ffi.NULL: + self._consume_errors() + raise ValueError("Could not deserialize PKCS12 data") + + p12 = self._ffi.gc(p12, self._lib.PKCS12_free) + evp_pkey_ptr = self._ffi.new("EVP_PKEY **") + x509_ptr = self._ffi.new("X509 **") + sk_x509_ptr = self._ffi.new("Cryptography_STACK_OF_X509 **") + with self._zeroed_null_terminated_buf(password) as password_buf: + res = self._lib.PKCS12_parse( + p12, password_buf, evp_pkey_ptr, x509_ptr, sk_x509_ptr + ) + + if res == 0: + self._consume_errors() + raise ValueError("Invalid password or PKCS12 data") + + cert = None + key = None + additional_certificates = [] + + if evp_pkey_ptr[0] != self._ffi.NULL: + evp_pkey = self._ffi.gc(evp_pkey_ptr[0], self._lib.EVP_PKEY_free) + key = self._evp_pkey_to_private_key(evp_pkey) + + if x509_ptr[0] != self._ffi.NULL: + x509 = self._ffi.gc(x509_ptr[0], self._lib.X509_free) + cert = _Certificate(self, x509) + + if sk_x509_ptr[0] != self._ffi.NULL: + sk_x509 = self._ffi.gc(sk_x509_ptr[0], self._lib.sk_X509_free) + num = self._lib.sk_X509_num(sk_x509_ptr[0]) + for i in range(num): + x509 = self._lib.sk_X509_value(sk_x509, i) + x509 = self._ffi.gc(x509, self._lib.X509_free) + self.openssl_assert(x509 != self._ffi.NULL) + additional_certificates.append(_Certificate(self, x509)) + + return (key, cert, additional_certificates) + + def poly1305_supported(self): + return self._lib.Cryptography_HAS_POLY1305 == 1 + + def create_poly1305_ctx(self, key): + utils._check_byteslike("key", key) + if len(key) != _POLY1305_KEY_SIZE: + raise ValueError("A poly1305 key is 32 bytes long") + + return _Poly1305Context(self, key) + class GetCipherByName(object): def __init__(self, fmt): @@ -1485,4 +2451,9 @@ class GetCipherByName(object): return backend._lib.EVP_get_cipherbyname(cipher_name.encode("ascii")) +def _get_xts_cipher(backend, cipher, mode): + cipher_name = "aes-{}-xts".format(cipher.key_size // 2) + return backend._lib.EVP_get_cipherbyname(cipher_name.encode("ascii")) + + backend = Backend() diff --git a/src/cryptography/hazmat/backends/openssl/ciphers.py b/src/cryptography/hazmat/backends/openssl/ciphers.py index 64097c7b..1de81419 100644 --- a/src/cryptography/hazmat/backends/openssl/ciphers.py +++ b/src/cryptography/hazmat/backends/openssl/ciphers.py @@ -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( @@ -39,7 +40,7 @@ class _CipherContext(object): adapter = registry[type(cipher), type(mode)] except KeyError: raise UnsupportedAlgorithm( - "cipher {0} in {1} mode is not supported " + "cipher {} in {} mode is not supported " "by this backend.".format( cipher.name, mode.name if mode else mode), _Reasons.UNSUPPORTED_CIPHER @@ -47,17 +48,25 @@ class _CipherContext(object): evp_cipher = adapter(self._backend, cipher, mode) if evp_cipher == self._backend._ffi.NULL: - 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 - ) + msg = "cipher {0.name} ".format(cipher) + if mode is not None: + msg += "in {0.name} mode ".format(mode) + msg += ( + "is not supported by this backend (Your version of OpenSSL " + "may be too old. Current version: {}.)" + ).format(self._backend.openssl_version_text()) + raise UnsupportedAlgorithm(msg, _Reasons.UNSUPPORTED_CIPHER) if isinstance(mode, modes.ModeWithInitializationVector): - iv_nonce = mode.initialization_vector + iv_nonce = self._backend._ffi.from_buffer( + mode.initialization_vector + ) + elif isinstance(mode, modes.ModeWithTweak): + iv_nonce = self._backend._ffi.from_buffer(mode.tweak) elif isinstance(mode, modes.ModeWithNonce): - iv_nonce = mode.nonce + iv_nonce = self._backend._ffi.from_buffer(mode.nonce) + elif isinstance(cipher, modes.ModeWithNonce): + iv_nonce = self._backend._ffi.from_buffer(cipher.nonce) else: iv_nonce = self._backend._ffi.NULL # begin init with cipher and operation type @@ -66,68 +75,75 @@ class _CipherContext(object): self._backend._ffi.NULL, self._backend._ffi.NULL, operation) - assert res != 0 + self._backend.openssl_assert(res != 0) # set the key length to handle variable key ciphers res = self._backend._lib.EVP_CIPHER_CTX_set_key_length( ctx, len(cipher.key) ) - assert res != 0 + 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 ) - assert res != 0 - if operation == self._DECRYPT: + self._backend.openssl_assert(res != 0) + if mode.tag is not None: res = self._backend._lib.EVP_CIPHER_CTX_ctrl( - ctx, self._backend._lib.EVP_CTRL_GCM_SET_TAG, + ctx, self._backend._lib.EVP_CTRL_AEAD_SET_TAG, len(mode.tag), mode.tag ) - assert res != 0 + self._backend.openssl_assert(res != 0) + self._tag = mode.tag # pass key/iv res = self._backend._lib.EVP_CipherInit_ex( ctx, self._backend._ffi.NULL, self._backend._ffi.NULL, - cipher.key, + self._backend._ffi.from_buffer(cipher.key), iv_nonce, operation ) - assert res != 0 + self._backend.openssl_assert(res != 0) # We purposely disable padding here as it's handled higher up in the # API. self._backend._lib.EVP_CIPHER_CTX_set_padding(ctx, 0) 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 = self._backend._ffi.new("unsigned char[]", - len(data) + self._block_size - 1) + buf = bytearray(len(data) + self._block_size_bytes - 1) + n = self.update_into(data, buf) + return bytes(buf[:n]) + + def update_into(self, data, buf): + if len(buf) < (len(data) + self._block_size_bytes - 1): + raise ValueError( + "buffer must be at least {} bytes for this " + "payload".format(len(data) + self._block_size_bytes - 1) + ) + + buf = self._backend._ffi.cast( + "unsigned char *", self._backend._ffi.from_buffer(buf) + ) outlen = self._backend._ffi.new("int *") - res = self._backend._lib.EVP_CipherUpdate(self._ctx, buf, outlen, data, - len(data)) - assert res != 0 - return self._backend._ffi.buffer(buf)[:outlen[0]] + res = self._backend._lib.EVP_CipherUpdate( + self._ctx, buf, outlen, + self._backend._ffi.from_buffer(data), len(data) + ) + self._backend.openssl_assert(res != 0) + return outlen[0] def finalize(self): - # OpenSSL 1.0.1 on Ubuntu 12.04 (and possibly other distributions) - # appears to have a bug where you must make at least one call to update - # even if you are only using authenticate_additional_data or the - # GCM tag will be wrong. An (empty) call to update resolves this - # and is harmless for all other versions of OpenSSL. - if isinstance(self._mode, modes.GCM): - self.update(b"") - - 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: @@ -136,81 +152,53 @@ class _CipherContext(object): if not errors and isinstance(self._mode, modes.GCM): raise InvalidTag - assert errors - - if errors[0][1:] == ( - 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 - ): - raise ValueError( - "The length of the provided data is not a multiple of " - "the block length." + self._backend.openssl_assert( + errors[0]._lib_reason_match( + self._backend._lib.ERR_LIB_EVP, + self._backend._lib.EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH ) - else: - raise self._backend._unknown_error(errors[0]) + ) + raise ValueError( + "The length of the provided data is not a multiple of " + "the block length." + ) if (isinstance(self._mode, modes.GCM) and self._operation == self._ENCRYPT): - 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 ) - assert res != 0 + self._backend.openssl_assert(res != 0) self._tag = self._backend._ffi.buffer(tag_buf)[:] res = self._backend._lib.EVP_CIPHER_CTX_cleanup(self._ctx) - assert res == 1 + self._backend.openssl_assert(res == 1) return self._backend._ffi.buffer(buf)[:outlen[0]] + def finalize_with_tag(self, tag): + if len(tag) < self._mode._min_tag_length: + raise ValueError( + "Authentication tag must be {} bytes or longer.".format( + self._mode._min_tag_length) + ) + res = self._backend._lib.EVP_CIPHER_CTX_ctrl( + self._ctx, self._backend._lib.EVP_CTRL_AEAD_SET_TAG, + len(tag), tag + ) + self._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( - self._ctx, self._backend._ffi.NULL, outlen, data, len(data) + self._ctx, self._backend._ffi.NULL, outlen, + self._backend._ffi.from_buffer(data), len(data) ) - assert res != 0 + 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 *") - assert self._key != self._backend._ffi.NULL - res = self._backend._lib.AES_set_encrypt_key( - cipher.key, len(cipher.key) * 8, self._key - ) - 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"" diff --git a/src/cryptography/hazmat/backends/openssl/cmac.py b/src/cryptography/hazmat/backends/openssl/cmac.py index 6c48ac34..d4d46f55 100644 --- a/src/cryptography/hazmat/backends/openssl/cmac.py +++ b/src/cryptography/hazmat/backends/openssl/cmac.py @@ -9,11 +9,10 @@ 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 from cryptography.hazmat.primitives.ciphers.modes import CBC -@utils.register_interface(interfaces.MACContext) class _CMACContext(object): def __init__(self, backend, algorithm, ctx=None): if not backend.cmac_algorithm_supported(algorithm): @@ -33,13 +32,15 @@ class _CMACContext(object): ctx = self._backend._lib.CMAC_CTX_new() - assert ctx != self._backend._ffi.NULL + 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( - ctx, self._key, len(self._key), + key_ptr = self._backend._ffi.from_buffer(self._key) + res = self._backend._lib.CMAC_Init( + ctx, key_ptr, len(self._key), evp_cipher, self._backend._ffi.NULL ) + self._backend.openssl_assert(res == 1) self._ctx = ctx @@ -47,7 +48,7 @@ class _CMACContext(object): def update(self, data): res = self._backend._lib.CMAC_Update(self._ctx, data, len(data)) - assert res == 1 + self._backend.openssl_assert(res == 1) def finalize(self): buf = self._backend._ffi.new("unsigned char[]", self._output_length) @@ -55,7 +56,7 @@ class _CMACContext(object): res = self._backend._lib.CMAC_Final( self._ctx, buf, length ) - assert res == 1 + self._backend.openssl_assert(res == 1) self._ctx = None @@ -69,7 +70,7 @@ class _CMACContext(object): res = self._backend._lib.CMAC_CTX_copy( copied_ctx, self._ctx ) - assert res == 1 + self._backend.openssl_assert(res == 1) return _CMACContext( self._backend, self._algorithm, ctx=copied_ctx ) diff --git a/src/cryptography/hazmat/backends/openssl/decode_asn1.py b/src/cryptography/hazmat/backends/openssl/decode_asn1.py new file mode 100644 index 00000000..7639e689 --- /dev/null +++ b/src/cryptography/hazmat/backends/openssl/decode_asn1.py @@ -0,0 +1,910 @@ +# 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 six + +from cryptography import x509 +from cryptography.hazmat._der import DERReader, INTEGER, NULL, SEQUENCE +from cryptography.x509.extensions import _TLS_FEATURE_TYPE_TO_ENUM +from cryptography.x509.name import _ASN1_TYPE_TO_ENUM +from cryptography.x509.oid import ( + CRLEntryExtensionOID, CertificatePoliciesOID, ExtensionOID, + OCSPExtensionOID, +) + + +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({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"/{}".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( + "{} 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 {} 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: + # The extension contents are a SEQUENCE OF INTEGERs. + data = backend._lib.X509_EXTENSION_get_data(ext) + data_bytes = _asn1_string_to_bytes(backend, data) + features = DERReader(data_bytes).read_single_element(SEQUENCE) + parsed = [] + while not features.is_empty(): + parsed.append(features.read_element(INTEGER).as_integer()) + # Map the features to their enum value. + value = x509.TLSFeature( + [_TLS_FEATURE_TYPE_TO_ENUM[x] 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) + # The contents of the extension must be an ASN.1 NULL. + reader = DERReader(_asn1_string_to_bytes(backend, data)) + reader.read_single_element(NULL).check_empty() + 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 {} 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, + lambda x: backend._lib.sk_ACCESS_DESCRIPTION_pop_free( + x, backend._ffi.addressof( + backend._lib._original_lib, "ACCESS_DESCRIPTION_free" + ) + ) + ) + num = backend._lib.sk_ACCESS_DESCRIPTION_num(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_issuing_dist_point(backend, idp): + idp = backend._ffi.cast("ISSUING_DIST_POINT *", idp) + idp = backend._ffi.gc(idp, backend._lib.ISSUING_DIST_POINT_free) + if idp.distpoint != backend._ffi.NULL: + full_name, relative_name = _decode_distpoint(backend, idp.distpoint) + else: + full_name = None + relative_name = None + + only_user = idp.onlyuser == 255 + only_ca = idp.onlyCA == 255 + indirect_crl = idp.indirectCRL == 255 + only_attr = idp.onlyattr == 255 + if idp.onlysomereasons != backend._ffi.NULL: + only_some_reasons = _decode_reasons(backend, idp.onlysomereasons) + else: + only_some_reasons = None + + return x509.IssuingDistributionPoint( + full_name, relative_name, only_user, only_ca, only_some_reasons, + indirect_crl, only_attr + ) + + +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: {}".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: {}".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 + ), + ExtensionOID.ISSUING_DISTRIBUTION_POINT: _decode_issuing_dist_point, + ExtensionOID.FRESHEST_CRL: _decode_freshest_crl, +} + +_OCSP_REQ_EXTENSION_HANDLERS = { + OCSPExtensionOID.NONCE: _decode_nonce, +} + +_OCSP_BASICRESP_EXTENSION_HANDLERS = { + OCSPExtensionOID.NONCE: _decode_nonce, +} + +# All revoked extensions are valid single response extensions, see: +# https://tools.ietf.org/html/rfc6960#section-4.4.5 +_OCSP_SINGLERESP_EXTENSION_HANDLERS = _REVOKED_EXTENSION_HANDLERS.copy() + +_CERTIFICATE_EXTENSION_PARSER_NO_SCT = _X509ExtensionParser( + ext_count=lambda backend, x: backend._lib.X509_get_ext_count(x), + get_ext=lambda backend, x, i: backend._lib.X509_get_ext(x, i), + handlers=_EXTENSION_HANDLERS_NO_SCT +) + +_CERTIFICATE_EXTENSION_PARSER = _X509ExtensionParser( + ext_count=lambda backend, x: backend._lib.X509_get_ext_count(x), + get_ext=lambda backend, x, i: backend._lib.X509_get_ext(x, i), + handlers=_EXTENSION_HANDLERS +) + +_CSR_EXTENSION_PARSER = _X509ExtensionParser( + ext_count=lambda backend, x: backend._lib.sk_X509_EXTENSION_num(x), + get_ext=lambda backend, x, i: backend._lib.sk_X509_EXTENSION_value(x, i), + handlers=_EXTENSION_HANDLERS +) + +_REVOKED_CERTIFICATE_EXTENSION_PARSER = _X509ExtensionParser( + ext_count=lambda backend, x: backend._lib.X509_REVOKED_get_ext_count(x), + get_ext=lambda backend, x, i: backend._lib.X509_REVOKED_get_ext(x, i), + handlers=_REVOKED_EXTENSION_HANDLERS, +) + +_CRL_EXTENSION_PARSER = _X509ExtensionParser( + ext_count=lambda backend, x: backend._lib.X509_CRL_get_ext_count(x), + get_ext=lambda backend, x, i: backend._lib.X509_CRL_get_ext(x, i), + handlers=_CRL_EXTENSION_HANDLERS, +) + +_OCSP_REQ_EXT_PARSER = _X509ExtensionParser( + ext_count=lambda backend, x: backend._lib.OCSP_REQUEST_get_ext_count(x), + get_ext=lambda backend, x, i: backend._lib.OCSP_REQUEST_get_ext(x, i), + handlers=_OCSP_REQ_EXTENSION_HANDLERS, +) + +_OCSP_BASICRESP_EXT_PARSER = _X509ExtensionParser( + ext_count=lambda backend, x: backend._lib.OCSP_BASICRESP_get_ext_count(x), + get_ext=lambda backend, x, i: backend._lib.OCSP_BASICRESP_get_ext(x, i), + handlers=_OCSP_BASICRESP_EXTENSION_HANDLERS, +) + +_OCSP_SINGLERESP_EXT_PARSER = _X509ExtensionParser( + ext_count=lambda backend, x: backend._lib.OCSP_SINGLERESP_get_ext_count(x), + get_ext=lambda backend, x, i: backend._lib.OCSP_SINGLERESP_get_ext(x, i), + handlers=_OCSP_SINGLERESP_EXTENSION_HANDLERS, +) diff --git a/src/cryptography/hazmat/backends/openssl/dh.py b/src/cryptography/hazmat/backends/openssl/dh.py new file mode 100644 index 00000000..1d5065c2 --- /dev/null +++ b/src/cryptography/hazmat/backends/openssl/dh.py @@ -0,0 +1,281 @@ +# 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_IS_LIBRESSL: + # In libressl DHparams_dup don't copy q + q = ffi.new("BIGNUM **") + lib.DH_get0_pqg(dh_cdata, ffi.NULL, q, ffi.NULL) + q_dup = lib.BN_dup(q[0]) + 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, + 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 + ) diff --git a/src/cryptography/hazmat/backends/openssl/dsa.py b/src/cryptography/hazmat/backends/openssl/dsa.py index f84857ff..79142bf6 100644 --- a/src/cryptography/hazmat/backends/openssl/dsa.py +++ b/src/cryptography/hazmat/backends/openssl/dsa.py @@ -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.primitives import hashes, serialization +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, 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 *") + + # 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]] - order_bits = backend._lib.BN_num_bits(dsa_cdata.q) - return _truncate_digest(digest, order_bits) + +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) - assert res == 1 - 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() - assert dsa_cdata != self._backend._ffi.NULL + 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() - assert dsa_cdata != self._backend._ffi.NULL + 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): @@ -160,10 +183,17 @@ class _DSAPrivateKey(object): encoding, format, encryption_algorithm, + self, self._evp_pkey, 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,45 +201,64 @@ 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() + utils._check_bytes("signature", signature) + + _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() - 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): - if format is serialization.PublicFormat.PKCS1: - raise ValueError( - "DSA public keys do not support PKCS1 serialization" - ) - return self._backend._public_key_bytes( encoding, format, + self, self._evp_pkey, None ) + + 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) diff --git a/src/cryptography/hazmat/backends/openssl/ec.py b/src/cryptography/hazmat/backends/openssl/ec.py index 7d3afb94..e70a3410 100644 --- a/src/cryptography/hazmat/backends/openssl/ec.py +++ b/src/cryptography/hazmat/backends/openssl/ec.py @@ -8,53 +8,50 @@ 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) - assert order != _ffi.NULL - - res = _lib.EC_GROUP_get_order(group, order, bn_ctx) - 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): group = backend._lib.EC_KEY_get0_group(ec_key) - assert group != backend._ffi.NULL + backend.openssl_assert(group != backend._ffi.NULL) nid = backend._lib.EC_GROUP_get_curve_name(group) # The following check is to find EC keys with unnamed curves and raise # an error for now. if nid == backend._lib.NID_undef: raise NotImplementedError( - "ECDSA certificates with unnamed curves are unsupported " + "ECDSA keys with unnamed curves are unsupported " + "at this time" + ) + + # This is like the above check, but it also catches the case where you + # explicitly encoded a curve with the same parameters as a named curve. + # Don't do that. + if ( + backend._lib.CRYPTOGRAPHY_OPENSSL_110_OR_GREATER and + backend._lib.EC_GROUP_get_asn1_flag(group) == 0 + ): + raise NotImplementedError( + "ECDSA keys with unnamed curves are unsupported " "at this time" ) curve_name = backend._lib.OBJ_nid2sn(nid) - assert curve_name != backend._ffi.NULL + backend.openssl_assert(curve_name != backend._ffi.NULL) sn = backend._ffi.string(curve_name).decode('ascii') return sn @@ -77,11 +74,33 @@ def _sn_to_elliptic_curve(backend, sn): return ec._CURVE_TYPES[sn]() except KeyError: raise UnsupportedAlgorithm( - "{0} is not a supported elliptic curve".format(sn), + "{} is not a supported elliptic curve".format(sn), _Reasons.UNSUPPORTED_ELLIPTIC_CURVE ) +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 +112,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) - 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 - ) - 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,66 +129,84 @@ 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) class _EllipticCurvePrivateKey(object): def __init__(self, backend, ec_key_cdata, evp_pkey): self._backend = backend - _mark_asn1_named_ec_curve(backend, ec_key_cdata) self._ec_key = ec_key_cdata self._evp_pkey = evp_pkey sn = _ec_key_curve_sn(backend, ec_key_cdata) self._curve = _sn_to_elliptic_curve(backend, sn) + _mark_asn1_named_ec_curve(backend, ec_key_cdata) curve = utils.read_only_property("_curve") + @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 + _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 ( + self._backend.elliptic_curve_exchange_algorithm_supported( + algorithm, self.curve ) - else: + ): raise UnsupportedAlgorithm( - "Unsupported elliptic curve signature algorithm.", - _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM) + "This backend does not support the ECDH algorithm.", + _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM + ) + + if peer_public_key.curve.name != self.curve.name: + raise ValueError( + "peer_public_key and self are not on the same curve" + ) + + group = self._backend._lib.EC_KEY_get0_group(self._ec_key) + z_len = (self._backend._lib.EC_GROUP_get_degree(group) + 7) // 8 + self._backend.openssl_assert(z_len > 0) + z_buf = self._backend._ffi.new("uint8_t[]", z_len) + peer_key = self._backend._lib.EC_KEY_get0_public_key( + peer_public_key._ec_key + ) + + r = self._backend._lib.ECDH_compute_key( + z_buf, z_len, peer_key, self._ec_key, self._backend._ffi.NULL + ) + self._backend.openssl_assert(r > 0) + return self._backend._ffi.buffer(z_buf)[:z_len] def public_key(self): group = self._backend._lib.EC_KEY_get0_group(self._ec_key) - assert group != self._backend._ffi.NULL + self._backend.openssl_assert(group != self._backend._ffi.NULL) curve_nid = self._backend._lib.EC_GROUP_get_curve_name(group) public_ec_key = self._backend._lib.EC_KEY_new_by_curve_name(curve_nid) - assert public_ec_key != self._backend._ffi.NULL + self._backend.openssl_assert(public_ec_key != self._backend._ffi.NULL) public_ec_key = self._backend._ffi.gc( public_ec_key, self._backend._lib.EC_KEY_free ) point = self._backend._lib.EC_KEY_get0_public_key(self._ec_key) - assert point != self._backend._ffi.NULL + self._backend.openssl_assert(point != self._backend._ffi.NULL) res = self._backend._lib.EC_KEY_set_public_key(public_ec_key, point) - assert res == 1 + self._backend.openssl_assert(res == 1) evp_pkey = self._backend._ec_cdata_to_evp_pkey(public_ec_key) @@ -206,47 +225,59 @@ class _EllipticCurvePrivateKey(object): encoding, format, encryption_algorithm, + self, self._evp_pkey, 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): def __init__(self, backend, ec_key_cdata, evp_pkey): self._backend = backend - _mark_asn1_named_ec_curve(backend, ec_key_cdata) self._ec_key = ec_key_cdata self._evp_pkey = evp_pkey sn = _ec_key_curve_sn(backend, ec_key_cdata) self._curve = _sn_to_elliptic_curve(backend, sn) + _mark_asn1_named_ec_curve(backend, ec_key_cdata) curve = utils.read_only_property("_curve") + @property + def key_size(self): + return self.curve.key_size + def verifier(self, signature, signature_algorithm): - 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) + _warn_sign_verify_deprecated() + utils._check_bytes("signature", signature) + + _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) - assert point != self._backend._ffi.NULL + self._backend.openssl_assert(point != self._backend._ffi.NULL) with self._backend._tmp_bn_ctx() as bn_ctx: bn_x = self._backend._lib.BN_CTX_get(bn_ctx) bn_y = self._backend._lib.BN_CTX_get(bn_ctx) res = get_func(group, point, bn_x, bn_y, bn_ctx) - assert res == 1 + self._backend.openssl_assert(res == 1) x = self._backend._bn_to_int(bn_x) y = self._backend._bn_to_int(bn_y) @@ -257,15 +288,62 @@ class _EllipticCurvePublicKey(object): curve=self._curve ) + def _encode_point(self, format): + if format is serialization.PublicFormat.CompressedPoint: + conversion = self._backend._lib.POINT_CONVERSION_COMPRESSED + else: + assert format is serialization.PublicFormat.UncompressedPoint + conversion = self._backend._lib.POINT_CONVERSION_UNCOMPRESSED + + group = self._backend._lib.EC_KEY_get0_group(self._ec_key) + self._backend.openssl_assert(group != self._backend._ffi.NULL) + point = self._backend._lib.EC_KEY_get0_public_key(self._ec_key) + self._backend.openssl_assert(point != self._backend._ffi.NULL) + with self._backend._tmp_bn_ctx() as bn_ctx: + buflen = self._backend._lib.EC_POINT_point2oct( + group, point, conversion, self._backend._ffi.NULL, 0, bn_ctx + ) + self._backend.openssl_assert(buflen > 0) + buf = self._backend._ffi.new("char[]", buflen) + res = self._backend._lib.EC_POINT_point2oct( + group, point, conversion, buf, buflen, bn_ctx + ) + self._backend.openssl_assert(buflen == res) + + return self._backend._ffi.buffer(buf)[:] + def public_bytes(self, encoding, format): - if format is serialization.PublicFormat.PKCS1: - raise ValueError( - "EC public keys do not support PKCS1 serialization" + + if ( + encoding is serialization.Encoding.X962 or + format is serialization.PublicFormat.CompressedPoint or + format is serialization.PublicFormat.UncompressedPoint + ): + if ( + encoding is not serialization.Encoding.X962 or + format not in ( + serialization.PublicFormat.CompressedPoint, + serialization.PublicFormat.UncompressedPoint + ) + ): + raise ValueError( + "X962 encoding must be used with CompressedPoint or " + "UncompressedPoint format" + ) + + return self._encode_point(format) + else: + return self._backend._public_key_bytes( + encoding, + format, + self, + self._evp_pkey, + None ) - return self._backend._public_key_bytes( - encoding, - format, - 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) diff --git a/src/cryptography/hazmat/backends/openssl/ed25519.py b/src/cryptography/hazmat/backends/openssl/ed25519.py new file mode 100644 index 00000000..1632ec37 --- /dev/null +++ b/src/cryptography/hazmat/backends/openssl/ed25519.py @@ -0,0 +1,134 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import absolute_import, division, print_function + +from cryptography import exceptions, utils +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.asymmetric.ed25519 import ( + Ed25519PrivateKey, Ed25519PublicKey, _ED25519_KEY_SIZE, _ED25519_SIG_SIZE +) + + +@utils.register_interface(Ed25519PublicKey) +class _Ed25519PublicKey(object): + def __init__(self, backend, evp_pkey): + self._backend = backend + self._evp_pkey = evp_pkey + + def public_bytes(self, encoding, format): + if ( + encoding is serialization.Encoding.Raw or + format is serialization.PublicFormat.Raw + ): + if ( + encoding is not serialization.Encoding.Raw or + format is not serialization.PublicFormat.Raw + ): + raise ValueError( + "When using Raw both encoding and format must be Raw" + ) + + return self._raw_public_bytes() + + return self._backend._public_key_bytes( + encoding, format, self, self._evp_pkey, None + ) + + def _raw_public_bytes(self): + buf = self._backend._ffi.new("unsigned char []", _ED25519_KEY_SIZE) + buflen = self._backend._ffi.new("size_t *", _ED25519_KEY_SIZE) + res = self._backend._lib.EVP_PKEY_get_raw_public_key( + self._evp_pkey, buf, buflen + ) + self._backend.openssl_assert(res == 1) + self._backend.openssl_assert(buflen[0] == _ED25519_KEY_SIZE) + return self._backend._ffi.buffer(buf, _ED25519_KEY_SIZE)[:] + + def verify(self, signature, data): + evp_md_ctx = self._backend._lib.Cryptography_EVP_MD_CTX_new() + self._backend.openssl_assert(evp_md_ctx != self._backend._ffi.NULL) + evp_md_ctx = self._backend._ffi.gc( + evp_md_ctx, self._backend._lib.Cryptography_EVP_MD_CTX_free + ) + res = self._backend._lib.EVP_DigestVerifyInit( + evp_md_ctx, self._backend._ffi.NULL, self._backend._ffi.NULL, + self._backend._ffi.NULL, self._evp_pkey + ) + self._backend.openssl_assert(res == 1) + res = self._backend._lib.EVP_DigestVerify( + evp_md_ctx, signature, len(signature), data, len(data) + ) + if res != 1: + self._backend._consume_errors() + raise exceptions.InvalidSignature + + +@utils.register_interface(Ed25519PrivateKey) +class _Ed25519PrivateKey(object): + def __init__(self, backend, evp_pkey): + self._backend = backend + self._evp_pkey = evp_pkey + + def public_key(self): + buf = self._backend._ffi.new("unsigned char []", _ED25519_KEY_SIZE) + buflen = self._backend._ffi.new("size_t *", _ED25519_KEY_SIZE) + res = self._backend._lib.EVP_PKEY_get_raw_public_key( + self._evp_pkey, buf, buflen + ) + self._backend.openssl_assert(res == 1) + self._backend.openssl_assert(buflen[0] == _ED25519_KEY_SIZE) + public_bytes = self._backend._ffi.buffer(buf)[:] + return self._backend.ed25519_load_public_bytes(public_bytes) + + def sign(self, data): + evp_md_ctx = self._backend._lib.Cryptography_EVP_MD_CTX_new() + self._backend.openssl_assert(evp_md_ctx != self._backend._ffi.NULL) + evp_md_ctx = self._backend._ffi.gc( + evp_md_ctx, self._backend._lib.Cryptography_EVP_MD_CTX_free + ) + res = self._backend._lib.EVP_DigestSignInit( + evp_md_ctx, self._backend._ffi.NULL, self._backend._ffi.NULL, + self._backend._ffi.NULL, self._evp_pkey + ) + self._backend.openssl_assert(res == 1) + buf = self._backend._ffi.new("unsigned char[]", _ED25519_SIG_SIZE) + buflen = self._backend._ffi.new("size_t *", len(buf)) + res = self._backend._lib.EVP_DigestSign( + evp_md_ctx, buf, buflen, data, len(data) + ) + self._backend.openssl_assert(res == 1) + self._backend.openssl_assert(buflen[0] == _ED25519_SIG_SIZE) + return self._backend._ffi.buffer(buf, buflen[0])[:] + + def private_bytes(self, encoding, format, encryption_algorithm): + if ( + encoding is serialization.Encoding.Raw or + format is serialization.PublicFormat.Raw + ): + if ( + format is not serialization.PrivateFormat.Raw or + encoding is not serialization.Encoding.Raw or not + isinstance(encryption_algorithm, serialization.NoEncryption) + ): + raise ValueError( + "When using Raw both encoding and format must be Raw " + "and encryption_algorithm must be NoEncryption()" + ) + + return self._raw_private_bytes() + + return self._backend._private_key_bytes( + encoding, format, encryption_algorithm, self, self._evp_pkey, None + ) + + def _raw_private_bytes(self): + buf = self._backend._ffi.new("unsigned char []", _ED25519_KEY_SIZE) + buflen = self._backend._ffi.new("size_t *", _ED25519_KEY_SIZE) + res = self._backend._lib.EVP_PKEY_get_raw_private_key( + self._evp_pkey, buf, buflen + ) + self._backend.openssl_assert(res == 1) + self._backend.openssl_assert(buflen[0] == _ED25519_KEY_SIZE) + return self._backend._ffi.buffer(buf, _ED25519_KEY_SIZE)[:] diff --git a/src/cryptography/hazmat/backends/openssl/ed448.py b/src/cryptography/hazmat/backends/openssl/ed448.py new file mode 100644 index 00000000..4845a1f2 --- /dev/null +++ b/src/cryptography/hazmat/backends/openssl/ed448.py @@ -0,0 +1,137 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import absolute_import, division, print_function + +from cryptography import exceptions, utils +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.asymmetric.ed448 import ( + Ed448PrivateKey, Ed448PublicKey +) + +_ED448_KEY_SIZE = 57 +_ED448_SIG_SIZE = 114 + + +@utils.register_interface(Ed448PublicKey) +class _Ed448PublicKey(object): + def __init__(self, backend, evp_pkey): + self._backend = backend + self._evp_pkey = evp_pkey + + def public_bytes(self, encoding, format): + if ( + encoding is serialization.Encoding.Raw or + format is serialization.PublicFormat.Raw + ): + if ( + encoding is not serialization.Encoding.Raw or + format is not serialization.PublicFormat.Raw + ): + raise ValueError( + "When using Raw both encoding and format must be Raw" + ) + + return self._raw_public_bytes() + + return self._backend._public_key_bytes( + encoding, format, self, self._evp_pkey, None + ) + + def _raw_public_bytes(self): + buf = self._backend._ffi.new("unsigned char []", _ED448_KEY_SIZE) + buflen = self._backend._ffi.new("size_t *", _ED448_KEY_SIZE) + res = self._backend._lib.EVP_PKEY_get_raw_public_key( + self._evp_pkey, buf, buflen + ) + self._backend.openssl_assert(res == 1) + self._backend.openssl_assert(buflen[0] == _ED448_KEY_SIZE) + return self._backend._ffi.buffer(buf, _ED448_KEY_SIZE)[:] + + def verify(self, signature, data): + evp_md_ctx = self._backend._lib.Cryptography_EVP_MD_CTX_new() + self._backend.openssl_assert(evp_md_ctx != self._backend._ffi.NULL) + evp_md_ctx = self._backend._ffi.gc( + evp_md_ctx, self._backend._lib.Cryptography_EVP_MD_CTX_free + ) + res = self._backend._lib.EVP_DigestVerifyInit( + evp_md_ctx, self._backend._ffi.NULL, self._backend._ffi.NULL, + self._backend._ffi.NULL, self._evp_pkey + ) + self._backend.openssl_assert(res == 1) + res = self._backend._lib.EVP_DigestVerify( + evp_md_ctx, signature, len(signature), data, len(data) + ) + if res != 1: + self._backend._consume_errors() + raise exceptions.InvalidSignature + + +@utils.register_interface(Ed448PrivateKey) +class _Ed448PrivateKey(object): + def __init__(self, backend, evp_pkey): + self._backend = backend + self._evp_pkey = evp_pkey + + def public_key(self): + buf = self._backend._ffi.new("unsigned char []", _ED448_KEY_SIZE) + buflen = self._backend._ffi.new("size_t *", _ED448_KEY_SIZE) + res = self._backend._lib.EVP_PKEY_get_raw_public_key( + self._evp_pkey, buf, buflen + ) + self._backend.openssl_assert(res == 1) + self._backend.openssl_assert(buflen[0] == _ED448_KEY_SIZE) + public_bytes = self._backend._ffi.buffer(buf)[:] + return self._backend.ed448_load_public_bytes(public_bytes) + + def sign(self, data): + evp_md_ctx = self._backend._lib.Cryptography_EVP_MD_CTX_new() + self._backend.openssl_assert(evp_md_ctx != self._backend._ffi.NULL) + evp_md_ctx = self._backend._ffi.gc( + evp_md_ctx, self._backend._lib.Cryptography_EVP_MD_CTX_free + ) + res = self._backend._lib.EVP_DigestSignInit( + evp_md_ctx, self._backend._ffi.NULL, self._backend._ffi.NULL, + self._backend._ffi.NULL, self._evp_pkey + ) + self._backend.openssl_assert(res == 1) + buf = self._backend._ffi.new("unsigned char[]", _ED448_SIG_SIZE) + buflen = self._backend._ffi.new("size_t *", len(buf)) + res = self._backend._lib.EVP_DigestSign( + evp_md_ctx, buf, buflen, data, len(data) + ) + self._backend.openssl_assert(res == 1) + self._backend.openssl_assert(buflen[0] == _ED448_SIG_SIZE) + return self._backend._ffi.buffer(buf, buflen[0])[:] + + def private_bytes(self, encoding, format, encryption_algorithm): + if ( + encoding is serialization.Encoding.Raw or + format is serialization.PublicFormat.Raw + ): + if ( + format is not serialization.PrivateFormat.Raw or + encoding is not serialization.Encoding.Raw or not + isinstance(encryption_algorithm, serialization.NoEncryption) + ): + raise ValueError( + "When using Raw both encoding and format must be Raw " + "and encryption_algorithm must be NoEncryption()" + ) + + return self._raw_private_bytes() + + return self._backend._private_key_bytes( + encoding, format, encryption_algorithm, self, self._evp_pkey, None + ) + + def _raw_private_bytes(self): + buf = self._backend._ffi.new("unsigned char []", _ED448_KEY_SIZE) + buflen = self._backend._ffi.new("size_t *", _ED448_KEY_SIZE) + res = self._backend._lib.EVP_PKEY_get_raw_private_key( + self._evp_pkey, buf, buflen + ) + self._backend.openssl_assert(res == 1) + self._backend.openssl_assert(buflen[0] == _ED448_KEY_SIZE) + return self._backend._ffi.buffer(buf, _ED448_KEY_SIZE)[:] diff --git a/src/cryptography/hazmat/backends/openssl/encode_asn1.py b/src/cryptography/hazmat/backends/openssl/encode_asn1.py new file mode 100644 index 00000000..ca35f0e7 --- /dev/null +++ b/src/cryptography/hazmat/backends/openssl/encode_asn1.py @@ -0,0 +1,660 @@ +# 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') + elif attribute._type is _ASN1Type.UniversalString: + value = attribute.value.encode('utf_32_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_issuing_dist_point(backend, ext): + idp = backend._lib.ISSUING_DIST_POINT_new() + backend.openssl_assert(idp != backend._ffi.NULL) + idp = backend._ffi.gc(idp, backend._lib.ISSUING_DIST_POINT_free) + idp.onlyuser = 255 if ext.only_contains_user_certs else 0 + idp.onlyCA = 255 if ext.only_contains_ca_certs else 0 + idp.indirectCRL = 255 if ext.indirect_crl else 0 + idp.onlyattr = 255 if ext.only_contains_attribute_certs else 0 + if ext.only_some_reasons: + idp.onlysomereasons = _encode_reasonflags( + backend, ext.only_some_reasons + ) + + if ext.full_name: + idp.distpoint = _encode_full_name(backend, ext.full_name) + + if ext.relative_name: + idp.distpoint = _encode_relative_name(backend, ext.relative_name) + + return idp + + +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, + lambda x: backend._lib.sk_ACCESS_DESCRIPTION_pop_free( + x, backend._ffi.addressof( + backend._lib._original_lib, "ACCESS_DESCRIPTION_free" + ) + ) + ) + for access_description in authority_info_access: + ad = backend._lib.ACCESS_DESCRIPTION_new() + method = _txt2obj( + backend, access_description.access_method.dotted_string + ) + _encode_general_name_preallocated( + backend, access_description.access_location, ad.location + ) + ad.method = method + 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): + gn = backend._lib.GENERAL_NAME_new() + _encode_general_name_preallocated(backend, name, gn) + return gn + + +def _encode_general_name_preallocated(backend, name, gn): + if isinstance(name, x509.DNSName): + 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): + 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): + 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): + 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): + 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): + 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): + 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( + "{} is an unknown GeneralName type".format(name) + ) + + +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_reasonflags(backend, reasons): + bitmask = backend._lib.ASN1_BIT_STRING_new() + backend.openssl_assert(bitmask != backend._ffi.NULL) + for reason in reasons: + res = backend._lib.ASN1_BIT_STRING_set_bit( + bitmask, _CRLREASONFLAGS[reason], 1 + ) + backend.openssl_assert(res == 1) + + return bitmask + + +def _encode_full_name(backend, 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, full_name) + return dpn + + +def _encode_relative_name(backend, relative_name): + dpn = backend._lib.DIST_POINT_NAME_new() + backend.openssl_assert(dpn != backend._ffi.NULL) + dpn.type = _DISTPOINT_TYPE_RELATIVENAME + dpn.name.relativename = _encode_sk_name_entry(backend, relative_name) + return dpn + + +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: + dp.reasons = _encode_reasonflags(backend, point.reasons) + + if point.full_name: + dp.distpoint = _encode_full_name(backend, point.full_name) + + if point.relative_name: + dp.distpoint = _encode_relative_name(backend, point.relative_name) + + 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, + ExtensionOID.ISSUING_DISTRIBUTION_POINT: _encode_issuing_dist_point, + ExtensionOID.FRESHEST_CRL: _encode_cdps_freshest_crl, +} + +_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, +} diff --git a/src/cryptography/hazmat/backends/openssl/hashes.py b/src/cryptography/hazmat/backends/openssl/hashes.py index 2c1702f8..7f9d840b 100644 --- a/src/cryptography/hazmat/backends/openssl/hashes.py +++ b/src/cryptography/hazmat/backends/openssl/hashes.py @@ -18,45 +18,61 @@ 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( + "{} is not a supported hash on this backend.".format( algorithm.name), _Reasons.UNSUPPORTED_HASH ) res = self._backend._lib.EVP_DigestInit_ex(ctx, evp_md, self._backend._ffi.NULL) - assert res != 0 + self._backend.openssl_assert(res != 0) self._ctx = ctx 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) - assert res != 0 + self._backend.openssl_assert(res != 0) return _HashContext(self._backend, self.algorithm, ctx=copied_ctx) def update(self, data): - res = self._backend._lib.EVP_DigestUpdate(self._ctx, data, len(data)) - assert res != 0 + data_ptr = self._backend._ffi.from_buffer(data) + res = self._backend._lib.EVP_DigestUpdate( + self._ctx, data_ptr, len(data) + ) + self._backend.openssl_assert(res != 0) def finalize(self): + if isinstance(self.algorithm, hashes.ExtendableOutputFunction): + # extendable output functions use a different finalize + return self._finalize_xof() + else: + buf = self._backend._ffi.new("unsigned char[]", + self._backend._lib.EVP_MAX_MD_SIZE) + outlen = self._backend._ffi.new("unsigned int *") + res = self._backend._lib.EVP_DigestFinal_ex(self._ctx, buf, outlen) + self._backend.openssl_assert(res != 0) + self._backend.openssl_assert( + outlen[0] == self.algorithm.digest_size + ) + return self._backend._ffi.buffer(buf)[:outlen[0]] + + def _finalize_xof(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.EVP_DigestFinal_ex(self._ctx, buf, outlen) - assert res != 0 - assert outlen[0] == self.algorithm.digest_size - res = self._backend._lib.EVP_MD_CTX_cleanup(self._ctx) - assert res == 1 - return self._backend._ffi.buffer(buf)[:outlen[0]] + self.algorithm.digest_size) + res = self._backend._lib.EVP_DigestFinalXOF( + self._ctx, buf, self.algorithm.digest_size + ) + self._backend.openssl_assert(res != 0) + return self._backend._ffi.buffer(buf)[:self.algorithm.digest_size] diff --git a/src/cryptography/hazmat/backends/openssl/hmac.py b/src/cryptography/hazmat/backends/openssl/hmac.py index dfe9d93b..2e09cbc8 100644 --- a/src/cryptography/hazmat/backends/openssl/hmac.py +++ b/src/cryptography/hazmat/backends/openssl/hmac.py @@ -9,10 +9,9 @@ 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 -@utils.register_interface(interfaces.MACContext) @utils.register_interface(hashes.HashContext) class _HMACContext(object): def __init__(self, backend, key, algorithm, ctx=None): @@ -20,23 +19,23 @@ 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( + "{} is not a supported hash on this backend".format( algorithm.name), _Reasons.UNSUPPORTED_HASH ) - res = self._backend._lib.Cryptography_HMAC_Init_ex( - ctx, key, len(key), evp_md, self._backend._ffi.NULL + key_ptr = self._backend._ffi.from_buffer(key) + res = self._backend._lib.HMAC_Init_ex( + ctx, key_ptr, len(key), evp_md, self._backend._ffi.NULL ) - assert res != 0 + self._backend.openssl_assert(res != 0) self._ctx = ctx self._key = key @@ -44,35 +43,29 @@ 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 + copied_ctx, self._backend._lib.Cryptography_HMAC_CTX_free ) - res = self._backend._lib.Cryptography_HMAC_CTX_copy( - copied_ctx, self._ctx - ) - assert res != 0 + 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) - ) - assert res != 0 + data_ptr = self._backend._ffi.from_buffer(data) + res = self._backend._lib.HMAC_Update(self._ctx, data_ptr, 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 - ) - assert res != 0 - assert outlen[0] == self.algorithm.digest_size - self._backend._lib.HMAC_CTX_cleanup(self._ctx) + res = self._backend._lib.HMAC_Final(self._ctx, buf, outlen) + self._backend.openssl_assert(res != 0) + self._backend.openssl_assert(outlen[0] == self.algorithm.digest_size) return self._backend._ffi.buffer(buf)[:outlen[0]] def verify(self, signature): diff --git a/src/cryptography/hazmat/backends/openssl/ocsp.py b/src/cryptography/hazmat/backends/openssl/ocsp.py new file mode 100644 index 00000000..e42565ef --- /dev/null +++ b/src/cryptography/hazmat/backends/openssl/ocsp.py @@ -0,0 +1,389 @@ +# 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, _OCSP_SINGLERESP_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: {} 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_hash_algorithm(self): + oid = self.signature_algorithm_oid + try: + return x509._SIG_OIDS_TO_HASH[oid] + except KeyError: + raise UnsupportedAlgorithm( + "Signature algorithm OID:{} not recognized".format(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) + + @utils.cached_property + @_requires_successful_response + def single_extensions(self): + return _OCSP_SINGLERESP_EXT_PARSER.parse( + self._backend, self._single + ) + + 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) diff --git a/src/cryptography/hazmat/backends/openssl/poly1305.py b/src/cryptography/hazmat/backends/openssl/poly1305.py new file mode 100644 index 00000000..25448dd0 --- /dev/null +++ b/src/cryptography/hazmat/backends/openssl/poly1305.py @@ -0,0 +1,60 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import absolute_import, division, print_function + + +from cryptography.exceptions import InvalidSignature +from cryptography.hazmat.primitives import constant_time + + +_POLY1305_TAG_SIZE = 16 +_POLY1305_KEY_SIZE = 32 + + +class _Poly1305Context(object): + def __init__(self, backend, key): + self._backend = backend + + key_ptr = self._backend._ffi.from_buffer(key) + # This function copies the key into OpenSSL-owned memory so we don't + # need to retain it ourselves + evp_pkey = self._backend._lib.EVP_PKEY_new_raw_private_key( + self._backend._lib.NID_poly1305, + self._backend._ffi.NULL, key_ptr, len(key) + ) + self._backend.openssl_assert(evp_pkey != self._backend._ffi.NULL) + self._evp_pkey = self._backend._ffi.gc( + evp_pkey, self._backend._lib.EVP_PKEY_free + ) + ctx = self._backend._lib.Cryptography_EVP_MD_CTX_new() + self._backend.openssl_assert(ctx != self._backend._ffi.NULL) + self._ctx = self._backend._ffi.gc( + ctx, self._backend._lib.Cryptography_EVP_MD_CTX_free + ) + res = self._backend._lib.EVP_DigestSignInit( + self._ctx, self._backend._ffi.NULL, self._backend._ffi.NULL, + self._backend._ffi.NULL, self._evp_pkey + ) + self._backend.openssl_assert(res == 1) + + def update(self, data): + data_ptr = self._backend._ffi.from_buffer(data) + res = self._backend._lib.EVP_DigestSignUpdate( + self._ctx, data_ptr, len(data) + ) + self._backend.openssl_assert(res != 0) + + def finalize(self): + buf = self._backend._ffi.new("unsigned char[]", _POLY1305_TAG_SIZE) + outlen = self._backend._ffi.new("size_t *") + res = self._backend._lib.EVP_DigestSignFinal(self._ctx, buf, outlen) + self._backend.openssl_assert(res != 0) + self._backend.openssl_assert(outlen[0] == _POLY1305_TAG_SIZE) + return self._backend._ffi.buffer(buf)[:outlen[0]] + + def verify(self, tag): + mac = self.finalize() + if not constant_time.bytes_eq(mac, tag): + raise InvalidSignature("Value did not match computed tag.") diff --git a/src/cryptography/hazmat/backends/openssl/rsa.py b/src/cryptography/hazmat/backends/openssl/rsa.py index 21414c05..a9e07b52 100644 --- a/src/cryptography/hazmat/backends/openssl/rsa.py +++ b/src/cryptography/hazmat/backends/openssl/rsa.py @@ -4,33 +4,31 @@ from __future__ import absolute_import, division, print_function -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,63 +41,80 @@ 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( + "{} is not supported by this backend.".format( padding.name ), _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 ) - assert pkey_ctx != backend._ffi.NULL + backend.openssl_assert(pkey_ctx != backend._ffi.NULL) pkey_ctx = backend._ffi.gc(pkey_ctx, backend._lib.EVP_PKEY_CTX_free) res = init(pkey_ctx) - assert res == 1 + backend.openssl_assert(res == 1) res = backend._lib.EVP_PKEY_CTX_set_rsa_padding( pkey_ctx, padding_enum) - assert res > 0 + backend.openssl_assert(res > 0) buf_size = backend._lib.EVP_PKEY_size(key._evp_pkey) - assert buf_size > 0 + 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,96 +122,165 @@ 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) - 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 - assert errors[0].lib == backend._lib.ERR_LIB_RSA + backend.openssl_assert(errors) + backend.openssl_assert(errors[0].lib == backend._lib.ERR_LIB_RSA) if isinstance(key, _RSAPublicKey): - assert (errors[0].reason == - backend._lib.RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE) + backend.openssl_assert( + errors[0].reason == backend._lib.RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE + ) raise ValueError( "Data too long for key size. Encrypt less data or use a " "larger key size." ) else: decoding_errors = [ + backend._lib.RSA_R_BAD_PAD_BYTE_COUNT, 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) - assert errors[0].reason in decoding_errors + backend.openssl_assert(errors[0].reason in decoding_errors) raise ValueError("Decryption failed.") -@utils.register_interface(AsymmetricSignatureContext) -class _RSASignatureContext(object): - def __init__(self, backend, private_key, padding, algorithm): - self._backend = backend - self._private_key = private_key +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( + "{} is not supported by this backend.".format(padding.name), + _Reasons.UNSUPPORTED_PADDING + ) + + return padding_enum - 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 +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( + "{} 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) - 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) - assert self._pkey_size > 0 - 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 + 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() + backend.openssl_assert(errors[0].lib == backend._lib.ERR_LIB_RSA) + if ( + errors[0].reason == + backend._lib.RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE + ): + reason = ("Salt length too long for key size. Try using " + "MAX_LENGTH instead.") else: - raise UnsupportedAlgorithm( - "{0} is not supported by this backend.".format(padding.name), - _Reasons.UNSUPPORTED_PADDING + backend.openssl_assert( + errors[0].reason == + backend._lib.RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY ) + reason = "Digest too large for key size. Use a larger key." + raise ValueError(reason) + 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 + + # 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) @@ -205,132 +289,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")) - 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 - ) - 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) - assert res == 1 - res = self._backend._lib.EVP_PKEY_CTX_set_signature_md( - pkey_ctx, evp_md) - assert res > 0 - - res = self._backend._lib.EVP_PKEY_CTX_set_rsa_padding( - pkey_ctx, self._padding_enum) - 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 - ) - ) - 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")) - assert mgf1_md != self._backend._ffi.NULL - res = self._backend._lib.EVP_PKEY_CTX_set_rsa_mgf1_md( - pkey_ctx, mgf1_md - ) - 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) - ) - 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.") - elif (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) - ) + return _rsa_sig_sign( + self._backend, + self._padding, + self._algorithm, + self._private_key, + self._hash_ctx.finalize() ) - 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 - ) - assert sig_len != -1 - return self._backend._ffi.buffer(sig_buf)[:sig_len] @utils.register_interface(AsymmetricVerificationContext) @@ -339,55 +304,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 - ) - - 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) - assert self._pkey_size > 0 - 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) @@ -395,115 +318,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")) - 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 - ) - 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) - assert res == 1 - res = self._backend._lib.EVP_PKEY_CTX_set_signature_md( - pkey_ctx, evp_md) - assert res > 0 - - res = self._backend._lib.EVP_PKEY_CTX_set_rsa_padding( - pkey_ctx, self._padding_enum) - 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 - ) - ) - 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")) - assert mgf1_md != self._backend._ffi.NULL - res = self._backend._lib.EVP_PKEY_CTX_set_rsa_mgf1_md( - pkey_ctx, mgf1_md - ) - assert res > 0 - - data_to_verify = self._hash_ctx.finalize() - res = self._backend._lib.EVP_PKEY_verify( - pkey_ctx, - self._signature, - len(self._signature), - data_to_verify, - len(data_to_verify) - ) - # 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. - 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. - 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), + return _rsa_sig_verify( + self._backend, + self._padding, + self._algorithm, + self._public_key, self._signature, - buf, - self._public_key._rsa_cdata, - self._backend._lib.RSA_NO_PADDING + self._hash_ctx.finalize() ) - 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) @@ -513,42 +335,69 @@ 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): - key_size_bytes = int(math.ceil(self.key_size / 8.0)) + key_size_bytes = (self.key_size + 7) // 8 if key_size_bytes != len(ciphertext): raise ValueError("Ciphertext length must be equal to key size.") return _enc_dec_rsa(self._backend, self, ciphertext, padding) def public_key(self): - ctx = self._backend._lib.RSA_new() - assert ctx != self._backend._ffi.NULL + 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) - assert res == 1 + 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]), ) ) @@ -557,10 +406,17 @@ class _RSAPrivateKey(object): encoding, format, encryption_algorithm, + self, self._evp_pkey, 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): @@ -569,11 +425,21 @@ 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() + utils._check_bytes("signature", signature) + + _check_not_prehashed(algorithm) return _RSAVerificationContext( self._backend, self, signature, padding, algorithm ) @@ -582,15 +448,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 + ) diff --git a/src/cryptography/hazmat/backends/openssl/utils.py b/src/cryptography/hazmat/backends/openssl/utils.py index 001121f9..ee472c0e 100644 --- a/src/cryptography/hazmat/backends/openssl/utils.py +++ b/src/cryptography/hazmat/backends/openssl/utils.py @@ -4,23 +4,66 @@ 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) - if 8 * digest_len > order_bits: - digest_len = (order_bits + 7) // 8 - digest = digest[:digest_len] +def _evp_pkey_derive(backend, evp_pkey, peer_public_key): + ctx = backend._lib.EVP_PKEY_CTX_new(evp_pkey, backend._ffi.NULL) + backend.openssl_assert(ctx != backend._ffi.NULL) + ctx = backend._ffi.gc(ctx, backend._lib.EVP_PKEY_CTX_free) + res = backend._lib.EVP_PKEY_derive_init(ctx) + backend.openssl_assert(res == 1) + res = backend._lib.EVP_PKEY_derive_set_peer( + ctx, peer_public_key._evp_pkey + ) + backend.openssl_assert(res == 1) + keylen = backend._ffi.new("size_t *") + res = backend._lib.EVP_PKEY_derive(ctx, backend._ffi.NULL, keylen) + backend.openssl_assert(res == 1) + backend.openssl_assert(keylen[0] > 0) + buf = backend._ffi.new("unsigned char[]", keylen[0]) + res = backend._lib.EVP_PKEY_derive(ctx, buf, keylen) + if res != 1: + raise ValueError( + "Null shared key derived from public/private pair." + ) - if 8 * digest_len > order_bits: - rshift = 8 - (order_bits & 0x7) - assert 0 < rshift < 8 + return backend._ffi.buffer(buf, keylen[0])[:] - mask = 0xFF >> rshift << rshift - # Set the bottom rshift bits to 0 - digest = digest[:-1] + six.int2byte(six.indexbytes(digest, -1) & mask) +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 - return digest + if len(data) != algorithm.digest_size: + raise ValueError( + "The provided data must be the same length as the hash " + "algorithm's digest size." + ) + + return (data, algorithm) + + +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." + ) + + +def _warn_sign_verify_deprecated(): + warnings.warn( + "signer and verifier have been deprecated. Please use sign " + "and verify instead.", + utils.PersistentlyDeprecated2017, + stacklevel=3 + ) diff --git a/src/cryptography/hazmat/backends/openssl/x25519.py b/src/cryptography/hazmat/backends/openssl/x25519.py new file mode 100644 index 00000000..665acf2f --- /dev/null +++ b/src/cryptography/hazmat/backends/openssl/x25519.py @@ -0,0 +1,118 @@ +# 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.backends.openssl.utils import _evp_pkey_derive +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.asymmetric.x25519 import ( + X25519PrivateKey, X25519PublicKey +) + + +_X25519_KEY_SIZE = 32 + + +@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, encoding, format): + if ( + encoding is serialization.Encoding.Raw or + format is serialization.PublicFormat.Raw + ): + if ( + encoding is not serialization.Encoding.Raw or + format is not serialization.PublicFormat.Raw + ): + raise ValueError( + "When using Raw both encoding and format must be Raw" + ) + + return self._raw_public_bytes() + + return self._backend._public_key_bytes( + encoding, format, self, self._evp_pkey, None + ) + + def _raw_public_bytes(self): + 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.") + + return _evp_pkey_derive( + self._backend, self._evp_pkey, peer_public_key + ) + + def private_bytes(self, encoding, format, encryption_algorithm): + if ( + encoding is serialization.Encoding.Raw or + format is serialization.PublicFormat.Raw + ): + if ( + format is not serialization.PrivateFormat.Raw or + encoding is not serialization.Encoding.Raw or not + isinstance(encryption_algorithm, serialization.NoEncryption) + ): + raise ValueError( + "When using Raw both encoding and format must be Raw " + "and encryption_algorithm must be NoEncryption()" + ) + + return self._raw_private_bytes() + + return self._backend._private_key_bytes( + encoding, format, encryption_algorithm, self, self._evp_pkey, None + ) + + def _raw_private_bytes(self): + # When we drop support for CRYPTOGRAPHY_OPENSSL_LESS_THAN_111 we can + # switch this to EVP_PKEY_new_raw_private_key + # The trick we use here is serializing to a PKCS8 key and just + # using the last 32 bytes, which is the key itself. + bio = self._backend._create_mem_bio_gc() + res = self._backend._lib.i2d_PKCS8PrivateKey_bio( + bio, self._evp_pkey, + self._backend._ffi.NULL, self._backend._ffi.NULL, + 0, self._backend._ffi.NULL, self._backend._ffi.NULL + ) + self._backend.openssl_assert(res == 1) + pkcs8 = self._backend._read_mem_bio(bio) + self._backend.openssl_assert(len(pkcs8) == 48) + return pkcs8[-_X25519_KEY_SIZE:] diff --git a/src/cryptography/hazmat/backends/openssl/x448.py b/src/cryptography/hazmat/backends/openssl/x448.py new file mode 100644 index 00000000..3de35b43 --- /dev/null +++ b/src/cryptography/hazmat/backends/openssl/x448.py @@ -0,0 +1,106 @@ +# 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.backends.openssl.utils import _evp_pkey_derive +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.asymmetric.x448 import ( + X448PrivateKey, X448PublicKey +) + +_X448_KEY_SIZE = 56 + + +@utils.register_interface(X448PublicKey) +class _X448PublicKey(object): + def __init__(self, backend, evp_pkey): + self._backend = backend + self._evp_pkey = evp_pkey + + def public_bytes(self, encoding, format): + if ( + encoding is serialization.Encoding.Raw or + format is serialization.PublicFormat.Raw + ): + if ( + encoding is not serialization.Encoding.Raw or + format is not serialization.PublicFormat.Raw + ): + raise ValueError( + "When using Raw both encoding and format must be Raw" + ) + + return self._raw_public_bytes() + + return self._backend._public_key_bytes( + encoding, format, self, self._evp_pkey, None + ) + + def _raw_public_bytes(self): + buf = self._backend._ffi.new("unsigned char []", _X448_KEY_SIZE) + buflen = self._backend._ffi.new("size_t *", _X448_KEY_SIZE) + res = self._backend._lib.EVP_PKEY_get_raw_public_key( + self._evp_pkey, buf, buflen + ) + self._backend.openssl_assert(res == 1) + self._backend.openssl_assert(buflen[0] == _X448_KEY_SIZE) + return self._backend._ffi.buffer(buf, _X448_KEY_SIZE)[:] + + +@utils.register_interface(X448PrivateKey) +class _X448PrivateKey(object): + def __init__(self, backend, evp_pkey): + self._backend = backend + self._evp_pkey = evp_pkey + + def public_key(self): + buf = self._backend._ffi.new("unsigned char []", _X448_KEY_SIZE) + buflen = self._backend._ffi.new("size_t *", _X448_KEY_SIZE) + res = self._backend._lib.EVP_PKEY_get_raw_public_key( + self._evp_pkey, buf, buflen + ) + self._backend.openssl_assert(res == 1) + self._backend.openssl_assert(buflen[0] == _X448_KEY_SIZE) + return self._backend.x448_load_public_bytes(buf) + + def exchange(self, peer_public_key): + if not isinstance(peer_public_key, X448PublicKey): + raise TypeError("peer_public_key must be X448PublicKey.") + + return _evp_pkey_derive( + self._backend, self._evp_pkey, peer_public_key + ) + + def private_bytes(self, encoding, format, encryption_algorithm): + if ( + encoding is serialization.Encoding.Raw or + format is serialization.PublicFormat.Raw + ): + if ( + format is not serialization.PrivateFormat.Raw or + encoding is not serialization.Encoding.Raw or not + isinstance(encryption_algorithm, serialization.NoEncryption) + ): + raise ValueError( + "When using Raw both encoding and format must be Raw " + "and encryption_algorithm must be NoEncryption()" + ) + + return self._raw_private_bytes() + + return self._backend._private_key_bytes( + encoding, format, encryption_algorithm, self, self._evp_pkey, None + ) + + def _raw_private_bytes(self): + buf = self._backend._ffi.new("unsigned char []", _X448_KEY_SIZE) + buflen = self._backend._ffi.new("size_t *", _X448_KEY_SIZE) + res = self._backend._lib.EVP_PKEY_get_raw_private_key( + self._evp_pkey, buf, buflen + ) + self._backend.openssl_assert(res == 1) + self._backend.openssl_assert(buflen[0] == _X448_KEY_SIZE) + return self._backend._ffi.buffer(buf, _X448_KEY_SIZE)[:] diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py index a03414c8..efbb1793 100644 --- a/src/cryptography/hazmat/backends/openssl/x509.py +++ b/src/cryptography/hazmat/backends/openssl/x509.py @@ -5,152 +5,21 @@ from __future__ import absolute_import, division, print_function import datetime -import ipaddress -from email.utils import parseaddr - -import idna - -import six - -from six.moves import urllib_parse +import operator from cryptography import utils, x509 from cryptography.exceptions import UnsupportedAlgorithm +from cryptography.hazmat.backends.openssl.decode_asn1 import ( + _CERTIFICATE_EXTENSION_PARSER, _CERTIFICATE_EXTENSION_PARSER_NO_SCT, + _CRL_EXTENSION_PARSER, _CSR_EXTENSION_PARSER, + _REVOKED_CERTIFICATE_EXTENSION_PARSER, _asn1_integer_to_int, + _asn1_string_to_bytes, _decode_x509_name, _obj2txt, _parse_asn1_time +) +from cryptography.hazmat.backends.openssl.encode_asn1 import ( + _encode_asn1_int_gc +) from cryptography.hazmat.primitives import hashes, serialization - - -def _obj2txt(backend, obj): - # Set to 80 on the recommendation of - # https://www.openssl.org/docs/crypto/OBJ_nid2ln.html#return_values - buf_len = 80 - buf = backend._ffi.new("char[]", buf_len) - res = backend._lib.OBJ_obj2txt(buf, buf_len, obj, 1) - assert res > 0 - return backend._ffi.buffer(buf, res)[:].decode() - - -def _asn1_integer_to_int(backend, asn1_int): - bn = backend._lib.ASN1_INTEGER_to_BN(asn1_int, backend._ffi.NULL) - assert bn != backend._ffi.NULL - bn = backend._ffi.gc(bn, backend._lib.BN_free) - return backend._bn_to_int(bn) - - -def _asn1_string_to_utf8(backend, asn1_string): - buf = backend._ffi.new("unsigned char **") - res = backend._lib.ASN1_STRING_to_UTF8(buf, asn1_string) - assert res >= 0 - 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 _decode_x509_name_entry(backend, x509_name_entry): - obj = backend._lib.X509_NAME_ENTRY_get_object(x509_name_entry) - assert obj != backend._ffi.NULL - data = backend._lib.X509_NAME_ENTRY_get_data(x509_name_entry) - assert data != backend._ffi.NULL - value = _asn1_string_to_utf8(backend, data) - oid = _obj2txt(backend, obj) - - return x509.NameAttribute(x509.ObjectIdentifier(oid), value) - - -def _decode_x509_name(backend, x509_name): - count = backend._lib.X509_NAME_entry_count(x509_name) - attributes = [] - for x in range(count): - entry = backend._lib.X509_NAME_get_entry(x509_name, x) - attributes.append(_decode_x509_name_entry(backend, entry)) - - return x509.Name(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) - 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: - data = backend._ffi.buffer(gn.d.dNSName.data, gn.d.dNSName.length)[:] - return x509.DNSName(idna.decode(data)) - elif gn.type == backend._lib.GEN_URI: - data = backend._ffi.buffer( - gn.d.uniformResourceIdentifier.data, - gn.d.uniformResourceIdentifier.length - )[:].decode("ascii") - parsed = urllib_parse.urlparse(data) - hostname = idna.decode(parsed.hostname) - if parsed.port: - netloc = hostname + u":" + six.text_type(parsed.port) - else: - netloc = hostname - - # Note that building a URL in this fashion means it should be - # semantically indistinguishable from the original but is not - # guaranteed to be exactly the same. - uri = urllib_parse.urlunparse(( - parsed.scheme, - netloc, - parsed.path, - parsed.params, - parsed.query, - parsed.fragment - )) - return x509.UniformResourceIdentifier(uri) - 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: - return x509.IPAddress( - ipaddress.ip_address( - backend._ffi.buffer( - gn.d.iPAddress.data, gn.d.iPAddress.length - )[:] - ) - ) - elif gn.type == backend._lib.GEN_DIRNAME: - return x509.DirectoryName( - _decode_x509_name(backend, gn.d.directoryName) - ) - elif gn.type == backend._lib.GEN_EMAIL: - data = backend._ffi.buffer( - gn.d.rfc822Name.data, gn.d.rfc822Name.length - )[:].decode("ascii") - name, address = parseaddr(data) - parts = address.split(u"@") - if name or len(parts) > 2 or not address: - # parseaddr has found a name (e.g. Name <email>) or the split - # has found more than 2 parts (which means more than one @ sign) - # or the entire value is an empty string. - raise ValueError("Invalid rfc822name value") - elif len(parts) == 1: - # Single label email name. This is valid for local delivery. No - # IDNA decoding can be done since there is no domain component. - return x509.RFC822Name(address) - else: - # A normal email of the form user@domain.com. Let's attempt to - # decode the domain component and return the entire address. - return x509.RFC822Name( - parts[0] + u"@" + idna.decode(parts[1]) - ) - else: - # otherName, x400Address or ediPartyName - raise x509.UnsupportedGeneralNameType( - "{0} is not a supported type".format( - x509._GENERAL_NAMES.get(gn.type, gn.type) - ), - gn.type - ) +from cryptography.hazmat.primitives.asymmetric import dsa, ec, rsa @utils.register_interface(x509.Certificate) @@ -160,7 +29,7 @@ class _Certificate(object): self._x509 = x509 def __repr__(self): - return "<Certificate(subject={0}, ...)>".format(self.subject) + return "<Certificate(subject={}, ...)>".format(self.subject) def __eq__(self, other): if not isinstance(other, x509.Certificate): @@ -172,15 +41,12 @@ class _Certificate(object): def __ne__(self, other): return not self == other + def __hash__(self): + return hash(self.public_bytes(serialization.Encoding.DER)) + def fingerprint(self, algorithm): h = hashes.Hash(algorithm, self._backend) - bio = self._backend._create_mem_bio() - res = self._backend._lib.i2d_X509_bio( - bio, self._x509 - ) - assert res == 1 - der = self._backend._read_mem_bio(bio) - h.update(der) + h.update(self.public_bytes(serialization.Encoding.DER)) return h.finalize() @property @@ -192,476 +58,320 @@ class _Certificate(object): return x509.Version.v3 else: raise x509.InvalidVersion( - "{0} is not a valid X509 version".format(version), version + "{} is not a valid X509 version".format(version), version ) @property - def serial(self): + def serial_number(self): asn1_int = self._backend._lib.X509_get_serialNumber(self._x509) - assert asn1_int != self._backend._ffi.NULL + self._backend.openssl_assert(asn1_int != self._backend._ffi.NULL) return _asn1_integer_to_int(self._backend, asn1_int) def public_key(self): pkey = self._backend._lib.X509_get_pubkey(self._x509) - assert pkey != self._backend._ffi.NULL + if pkey == self._backend._ffi.NULL: + # Remove errors from the stack. + self._backend._consume_errors() + raise ValueError("Certificate public key is of an unknown type") + pkey = self._backend._ffi.gc(pkey, self._backend._lib.EVP_PKEY_free) return self._backend._evp_pkey_to_public_key(pkey) @property def not_valid_before(self): - asn1_time = self._backend._lib.X509_get_notBefore(self._x509) - return self._parse_asn1_time(asn1_time) + asn1_time = self._backend._lib.X509_getm_notBefore(self._x509) + return _parse_asn1_time(self._backend, asn1_time) @property def not_valid_after(self): - asn1_time = self._backend._lib.X509_get_notAfter(self._x509) - return self._parse_asn1_time(asn1_time) - - def _parse_asn1_time(self, asn1_time): - assert asn1_time != self._backend._ffi.NULL - generalized_time = self._backend._lib.ASN1_TIME_to_generalizedtime( - asn1_time, self._backend._ffi.NULL - ) - assert generalized_time != self._backend._ffi.NULL - generalized_time = self._backend._ffi.gc( - generalized_time, self._backend._lib.ASN1_GENERALIZEDTIME_free - ) - time = self._backend._ffi.string( - self._backend._lib.ASN1_STRING_data( - self._backend._ffi.cast("ASN1_STRING *", generalized_time) - ) - ).decode("ascii") - return datetime.datetime.strptime(time, "%Y%m%d%H%M%SZ") + asn1_time = self._backend._lib.X509_getm_notAfter(self._x509) + return _parse_asn1_time(self._backend, asn1_time) @property def issuer(self): issuer = self._backend._lib.X509_get_issuer_name(self._x509) - assert issuer != self._backend._ffi.NULL + self._backend.openssl_assert(issuer != self._backend._ffi.NULL) return _decode_x509_name(self._backend, issuer) @property def subject(self): subject = self._backend._lib.X509_get_subject_name(self._x509) - assert subject != self._backend._ffi.NULL + self._backend.openssl_assert(subject != self._backend._ffi.NULL) return _decode_x509_name(self._backend, subject) @property def signature_hash_algorithm(self): - oid = _obj2txt(self._backend, self._x509.sig_alg.algorithm) + oid = self.signature_algorithm_oid try: return x509._SIG_OIDS_TO_HASH[oid] except KeyError: raise UnsupportedAlgorithm( - "Signature algorithm OID:{0} not recognized".format(oid) + "Signature algorithm OID:{} not recognized".format(oid) ) @property + def signature_algorithm_oid(self): + alg = self._backend._ffi.new("X509_ALGOR **") + self._backend._lib.X509_get0_signature( + self._backend._ffi.NULL, alg, self._x509 + ) + self._backend.openssl_assert(alg[0] != self._backend._ffi.NULL) + oid = _obj2txt(self._backend, alg[0].algorithm) + return x509.ObjectIdentifier(oid) + + @utils.cached_property def extensions(self): - extensions = [] - seen_oids = set() - extcount = self._backend._lib.X509_get_ext_count(self._x509) - for i in range(0, extcount): - ext = self._backend._lib.X509_get_ext(self._x509, i) - assert ext != self._backend._ffi.NULL - crit = self._backend._lib.X509_EXTENSION_get_critical(ext) - critical = crit == 1 - oid = x509.ObjectIdentifier(_obj2txt(self._backend, ext.object)) - if oid in seen_oids: - raise x509.DuplicateExtension( - "Duplicate {0} extension found".format(oid), oid - ) - elif oid == x509.OID_BASIC_CONSTRAINTS: - value = _decode_basic_constraints(self._backend, ext) - elif oid == x509.OID_SUBJECT_KEY_IDENTIFIER: - value = _decode_subject_key_identifier(self._backend, ext) - elif oid == x509.OID_KEY_USAGE: - value = _decode_key_usage(self._backend, ext) - elif oid == x509.OID_SUBJECT_ALTERNATIVE_NAME: - value = _decode_subject_alt_name(self._backend, ext) - elif oid == x509.OID_EXTENDED_KEY_USAGE: - value = _decode_extended_key_usage(self._backend, ext) - elif oid == x509.OID_AUTHORITY_KEY_IDENTIFIER: - value = _decode_authority_key_identifier(self._backend, ext) - elif oid == x509.OID_AUTHORITY_INFORMATION_ACCESS: - value = _decode_authority_information_access( - self._backend, ext - ) - elif oid == x509.OID_CERTIFICATE_POLICIES: - value = _decode_certificate_policies(self._backend, ext) - elif oid == x509.OID_CRL_DISTRIBUTION_POINTS: - value = _decode_crl_distribution_points(self._backend, ext) - elif oid == x509.OID_OCSP_NO_CHECK: - value = x509.OCSPNoCheck() - elif oid == x509.OID_INHIBIT_ANY_POLICY: - value = _decode_inhibit_any_policy(self._backend, ext) - elif oid == x509.OID_ISSUER_ALTERNATIVE_NAME: - value = _decode_issuer_alt_name(self._backend, ext) - elif critical: - raise x509.UnsupportedExtension( - "{0} is not currently supported".format(oid), oid - ) - else: - # Unsupported non-critical extension, silently skipping for now - seen_oids.add(oid) - continue + if self._backend._lib.CRYPTOGRAPHY_OPENSSL_110_OR_GREATER: + return _CERTIFICATE_EXTENSION_PARSER.parse( + self._backend, self._x509 + ) + else: + return _CERTIFICATE_EXTENSION_PARSER_NO_SCT.parse( + self._backend, self._x509 + ) - seen_oids.add(oid) - extensions.append(x509.Extension(oid, critical, value)) + @property + def signature(self): + sig = self._backend._ffi.new("ASN1_BIT_STRING **") + self._backend._lib.X509_get0_signature( + sig, self._backend._ffi.NULL, self._x509 + ) + self._backend.openssl_assert(sig[0] != self._backend._ffi.NULL) + return _asn1_string_to_bytes(self._backend, sig[0]) - return x509.Extensions(extensions) + @property + def tbs_certificate_bytes(self): + pp = self._backend._ffi.new("unsigned char **") + res = self._backend._lib.i2d_re_X509_tbs(self._x509, pp) + self._backend.openssl_assert(res > 0) + pp = self._backend._ffi.gc( + pp, lambda pointer: self._backend._lib.OPENSSL_free(pointer[0]) + ) + return self._backend._ffi.buffer(pp[0], res)[:] def public_bytes(self, encoding): - if not isinstance(encoding, serialization.Encoding): - raise TypeError("encoding must be an item from the Encoding enum") - - bio = self._backend._create_mem_bio() + bio = self._backend._create_mem_bio_gc() if encoding is serialization.Encoding.PEM: res = self._backend._lib.PEM_write_bio_X509(bio, self._x509) elif encoding is serialization.Encoding.DER: res = self._backend._lib.i2d_X509_bio(bio, self._x509) - assert res == 1 + else: + raise TypeError("encoding must be an item from the Encoding enum") + + self._backend.openssl_assert(res == 1) return self._backend._read_mem_bio(bio) -def _decode_certificate_policies(backend, ext): - cp = backend._ffi.cast( - "Cryptography_STACK_OF_POLICYINFO *", - backend._lib.X509V3_EXT_d2i(ext) - ) - assert cp != backend._ffi.NULL - cp = backend._ffi.gc(cp, backend._lib.sk_POLICYINFO_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 == x509.OID_CPS_QUALIFIER: - cpsuri = backend._ffi.buffer( - pqi.d.cpsuri.data, pqi.d.cpsuri.length - )[:].decode('ascii') - qualifiers.append(cpsuri) - elif pqualid == x509.OID_CPS_USER_NOTICE: - user_notice = _decode_user_notice( - backend, pqi.d.usernotice - ) - qualifiers.append(user_notice) - - certificate_policies.append( - x509.PolicyInformation(oid, qualifiers) +@utils.register_interface(x509.RevokedCertificate) +class _RevokedCertificate(object): + def __init__(self, backend, crl, x509_revoked): + self._backend = backend + # The X509_REVOKED_value is a X509_REVOKED * that has + # no reference counting. This means when X509_CRL_free is + # called then the CRL and all X509_REVOKED * are freed. Since + # you can retain a reference to a single revoked certificate + # and let the CRL fall out of scope we need to retain a + # private reference to the CRL inside the RevokedCertificate + # object to prevent the gc from being called inappropriately. + self._crl = crl + self._x509_revoked = x509_revoked + + @property + def serial_number(self): + asn1_int = self._backend._lib.X509_REVOKED_get0_serialNumber( + self._x509_revoked ) + self._backend.openssl_assert(asn1_int != self._backend._ffi.NULL) + return _asn1_integer_to_int(self._backend, asn1_int) - return x509.CertificatePolicies(certificate_policies) + @property + def revocation_date(self): + return _parse_asn1_time( + self._backend, + self._backend._lib.X509_REVOKED_get0_revocationDate( + self._x509_revoked + ) + ) + @utils.cached_property + def extensions(self): + return _REVOKED_CERTIFICATE_EXTENSION_PARSER.parse( + self._backend, self._x509_revoked + ) -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) +@utils.register_interface(x509.CertificateRevocationList) +class _CertificateRevocationList(object): + def __init__(self, backend, x509_crl): + self._backend = backend + self._x509_crl = x509_crl + + def __eq__(self, other): + if not isinstance(other, x509.CertificateRevocationList): + return NotImplemented + + res = self._backend._lib.X509_CRL_cmp(self._x509_crl, other._x509_crl) + return res == 0 + + def __ne__(self, other): + return not self == other - if un.noticeref != backend._ffi.NULL: - organization = _asn1_string_to_utf8( - backend, un.noticeref.organization + def fingerprint(self, algorithm): + h = hashes.Hash(algorithm, self._backend) + bio = self._backend._create_mem_bio_gc() + res = self._backend._lib.i2d_X509_CRL_bio( + bio, self._x509_crl ) + self._backend.openssl_assert(res == 1) + der = self._backend._read_mem_bio(bio) + h.update(der) + return h.finalize() - num = backend._lib.sk_ASN1_INTEGER_num( - un.noticeref.noticenos + @utils.cached_property + def _sorted_crl(self): + # X509_CRL_get0_by_serial sorts in place, which breaks a variety of + # things we don't want to break (like iteration and the signature). + # Let's dupe it and sort that instead. + dup = self._backend._lib.X509_CRL_dup(self._x509_crl) + self._backend.openssl_assert(dup != self._backend._ffi.NULL) + dup = self._backend._ffi.gc(dup, self._backend._lib.X509_CRL_free) + return dup + + def get_revoked_certificate_by_serial_number(self, serial_number): + revoked = self._backend._ffi.new("X509_REVOKED **") + asn1_int = _encode_asn1_int_gc(self._backend, serial_number) + res = self._backend._lib.X509_CRL_get0_by_serial( + self._sorted_crl, revoked, asn1_int ) - notice_numbers = [] - for i in range(num): - asn1_int = backend._lib.sk_ASN1_INTEGER_value( - un.noticeref.noticenos, i + if res == 0: + return None + else: + self._backend.openssl_assert( + revoked[0] != self._backend._ffi.NULL ) - notice_num = _asn1_integer_to_int( - backend, asn1_int + return _RevokedCertificate( + self._backend, self._sorted_crl, revoked[0] ) - notice_numbers.append(notice_num) - notice_reference = x509.NoticeReference( - organization, notice_numbers - ) + @property + def signature_hash_algorithm(self): + oid = self.signature_algorithm_oid + try: + return x509._SIG_OIDS_TO_HASH[oid] + except KeyError: + raise UnsupportedAlgorithm( + "Signature algorithm OID:{} not recognized".format(oid) + ) - return x509.UserNotice(notice_reference, explicit_text) - - -def _decode_basic_constraints(backend, ext): - bc_st = backend._lib.X509V3_EXT_d2i(ext) - assert bc_st != backend._ffi.NULL - 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 - if basic_constraints.pathlen == backend._ffi.NULL: - path_length = None - else: - path_length = _asn1_integer_to_int( - backend, basic_constraints.pathlen + @property + def signature_algorithm_oid(self): + alg = self._backend._ffi.new("X509_ALGOR **") + self._backend._lib.X509_CRL_get0_signature( + self._x509_crl, self._backend._ffi.NULL, alg ) + self._backend.openssl_assert(alg[0] != self._backend._ffi.NULL) + oid = _obj2txt(self._backend, alg[0].algorithm) + return x509.ObjectIdentifier(oid) + + @property + def issuer(self): + issuer = self._backend._lib.X509_CRL_get_issuer(self._x509_crl) + self._backend.openssl_assert(issuer != self._backend._ffi.NULL) + return _decode_x509_name(self._backend, issuer) - return x509.BasicConstraints(ca, path_length) - - -def _decode_subject_key_identifier(backend, ext): - asn1_string = backend._lib.X509V3_EXT_d2i(ext) - assert asn1_string != backend._ffi.NULL - 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, ext): - akid = backend._lib.X509V3_EXT_d2i(ext) - assert akid != backend._ffi.NULL - akid = backend._ffi.cast("AUTHORITY_KEYID *", akid) - akid = backend._ffi.gc( - akid, backend._lib.AUTHORITY_KEYID_free - ) - key_identifier = None - authority_cert_issuer = None - authority_cert_serial_number = 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 + @property + def next_update(self): + nu = self._backend._lib.X509_CRL_get_nextUpdate(self._x509_crl) + self._backend.openssl_assert(nu != self._backend._ffi.NULL) + return _parse_asn1_time(self._backend, nu) + + @property + def last_update(self): + lu = self._backend._lib.X509_CRL_get_lastUpdate(self._x509_crl) + self._backend.openssl_assert(lu != self._backend._ffi.NULL) + return _parse_asn1_time(self._backend, lu) + + @property + def signature(self): + sig = self._backend._ffi.new("ASN1_BIT_STRING **") + self._backend._lib.X509_CRL_get0_signature( + self._x509_crl, sig, self._backend._ffi.NULL ) + self._backend.openssl_assert(sig[0] != self._backend._ffi.NULL) + return _asn1_string_to_bytes(self._backend, sig[0]) - if akid.serial != backend._ffi.NULL: - authority_cert_serial_number = _asn1_integer_to_int( - backend, akid.serial + @property + def tbs_certlist_bytes(self): + pp = self._backend._ffi.new("unsigned char **") + res = self._backend._lib.i2d_re_X509_CRL_tbs(self._x509_crl, pp) + self._backend.openssl_assert(res > 0) + pp = self._backend._ffi.gc( + pp, lambda pointer: self._backend._lib.OPENSSL_free(pointer[0]) ) + return self._backend._ffi.buffer(pp[0], res)[:] - return x509.AuthorityKeyIdentifier( - key_identifier, authority_cert_issuer, authority_cert_serial_number - ) - - -def _decode_authority_information_access(backend, ext): - aia = backend._lib.X509V3_EXT_d2i(ext) - assert aia != backend._ffi.NULL - 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) - assert ad.method != backend._ffi.NULL - oid = x509.ObjectIdentifier(_obj2txt(backend, ad.method)) - 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, ext): - bit_string = backend._lib.X509V3_EXT_d2i(ext) - assert bit_string != backend._ffi.NULL - 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, ext): - gns = backend._ffi.cast( - "GENERAL_NAMES *", backend._lib.X509V3_EXT_d2i(ext) - ) - assert gns != backend._ffi.NULL - 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_extended_key_usage(backend, ext): - sk = backend._ffi.cast( - "Cryptography_STACK_OF_ASN1_OBJECT *", - backend._lib.X509V3_EXT_d2i(ext) - ) - assert sk != backend._ffi.NULL - 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) - assert obj != backend._ffi.NULL - oid = x509.ObjectIdentifier(_obj2txt(backend, obj)) - ekus.append(oid) - - return x509.ExtendedKeyUsage(ekus) - - -def _decode_crl_distribution_points(backend, ext): - cdps = backend._ffi.cast( - "Cryptography_STACK_OF_DIST_POINT *", - backend._lib.X509V3_EXT_d2i(ext) - ) - assert cdps != backend._ffi.NULL - cdps = backend._ffi.gc( - cdps, backend._lib.sk_DIST_POINT_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: - # We will check each bit from RFC 5280 - # ReasonFlags ::= BIT STRING { - # unused (0), - # keyCompromise (1), - # cACompromise (2), - # affiliationChanged (3), - # superseded (4), - # cessationOfOperation (5), - # certificateHold (6), - # privilegeWithdrawn (7), - # aACompromise (8) } - reasons = [] - get_bit = backend._lib.ASN1_BIT_STRING_get_bit - if get_bit(cdp.reasons, 1): - reasons.append(x509.ReasonFlags.key_compromise) - - if get_bit(cdp.reasons, 2): - reasons.append(x509.ReasonFlags.ca_compromise) - - if get_bit(cdp.reasons, 3): - reasons.append(x509.ReasonFlags.affiliation_changed) - - if get_bit(cdp.reasons, 4): - reasons.append(x509.ReasonFlags.superseded) - - if get_bit(cdp.reasons, 5): - reasons.append(x509.ReasonFlags.cessation_of_operation) - - if get_bit(cdp.reasons, 6): - reasons.append(x509.ReasonFlags.certificate_hold) - - if get_bit(cdp.reasons, 7): - reasons.append(x509.ReasonFlags.privilege_withdrawn) - - if get_bit(cdp.reasons, 8): - reasons.append(x509.ReasonFlags.aa_compromise) - - reasons = frozenset(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: - # Type 0 is fullName, there is no #define for it in the code. - if cdp.distpoint.type == 0: - full_name = _decode_general_names( - backend, cdp.distpoint.name.fullname - ) - # OpenSSL code doesn't test for a specific type for - # relativename, everything that isn't fullname is considered - # relativename. - else: - rns = cdp.distpoint.name.relativename - rnum = backend._lib.sk_X509_NAME_ENTRY_num(rns) - attributes = [] - for i in range(rnum): - rn = backend._lib.sk_X509_NAME_ENTRY_value( - rns, i - ) - assert rn != backend._ffi.NULL - attributes.append( - _decode_x509_name_entry(backend, rn) - ) - - relative_name = x509.Name(attributes) - - dist_points.append( - x509.DistributionPoint( - full_name, relative_name, reasons, crl_issuer + def public_bytes(self, encoding): + bio = self._backend._create_mem_bio_gc() + if encoding is serialization.Encoding.PEM: + res = self._backend._lib.PEM_write_bio_X509_CRL( + bio, self._x509_crl ) - ) + elif encoding is serialization.Encoding.DER: + res = self._backend._lib.i2d_X509_CRL_bio(bio, self._x509_crl) + else: + raise TypeError("encoding must be an item from the Encoding enum") + + self._backend.openssl_assert(res == 1) + return self._backend._read_mem_bio(bio) + + def _revoked_cert(self, idx): + revoked = self._backend._lib.X509_CRL_get_REVOKED(self._x509_crl) + r = self._backend._lib.sk_X509_REVOKED_value(revoked, idx) + self._backend.openssl_assert(r != self._backend._ffi.NULL) + return _RevokedCertificate(self._backend, self, r) + + def __iter__(self): + for i in range(len(self)): + yield self._revoked_cert(i) + + def __getitem__(self, idx): + if isinstance(idx, slice): + start, stop, step = idx.indices(len(self)) + return [self._revoked_cert(i) for i in range(start, stop, step)] + else: + idx = operator.index(idx) + if idx < 0: + idx += len(self) + if not 0 <= idx < len(self): + raise IndexError + return self._revoked_cert(idx) + + def __len__(self): + revoked = self._backend._lib.X509_CRL_get_REVOKED(self._x509_crl) + if revoked == self._backend._ffi.NULL: + return 0 + else: + return self._backend._lib.sk_X509_REVOKED_num(revoked) - return x509.CRLDistributionPoints(dist_points) + @utils.cached_property + def extensions(self): + return _CRL_EXTENSION_PARSER.parse(self._backend, self._x509_crl) + + def is_signature_valid(self, public_key): + if not isinstance(public_key, (dsa.DSAPublicKey, rsa.RSAPublicKey, + ec.EllipticCurvePublicKey)): + raise TypeError('Expecting one of DSAPublicKey, RSAPublicKey,' + ' or EllipticCurvePublicKey.') + res = self._backend._lib.X509_CRL_verify( + self._x509_crl, public_key._evp_pkey + ) + if res != 1: + self._backend._consume_errors() + return False -def _decode_inhibit_any_policy(backend, ext): - asn1_int = backend._ffi.cast( - "ASN1_INTEGER *", - backend._lib.X509V3_EXT_d2i(ext) - ) - assert asn1_int != backend._ffi.NULL - 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) + return True @utils.register_interface(x509.CertificateSigningRequest) @@ -670,70 +380,167 @@ class _CertificateSigningRequest(object): self._backend = backend self._x509_req = x509_req + def __eq__(self, other): + if not isinstance(other, _CertificateSigningRequest): + return NotImplemented + + self_bytes = self.public_bytes(serialization.Encoding.DER) + other_bytes = other.public_bytes(serialization.Encoding.DER) + return self_bytes == other_bytes + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash(self.public_bytes(serialization.Encoding.DER)) + def public_key(self): pkey = self._backend._lib.X509_REQ_get_pubkey(self._x509_req) - assert pkey != self._backend._ffi.NULL + self._backend.openssl_assert(pkey != self._backend._ffi.NULL) pkey = self._backend._ffi.gc(pkey, self._backend._lib.EVP_PKEY_free) return self._backend._evp_pkey_to_public_key(pkey) @property def subject(self): subject = self._backend._lib.X509_REQ_get_subject_name(self._x509_req) - assert subject != self._backend._ffi.NULL + self._backend.openssl_assert(subject != self._backend._ffi.NULL) return _decode_x509_name(self._backend, subject) @property def signature_hash_algorithm(self): - oid = _obj2txt(self._backend, self._x509_req.sig_alg.algorithm) + oid = self.signature_algorithm_oid try: return x509._SIG_OIDS_TO_HASH[oid] except KeyError: raise UnsupportedAlgorithm( - "Signature algorithm OID:{0} not recognized".format(oid) + "Signature algorithm OID:{} not recognized".format(oid) ) @property + def signature_algorithm_oid(self): + alg = self._backend._ffi.new("X509_ALGOR **") + self._backend._lib.X509_REQ_get0_signature( + self._x509_req, self._backend._ffi.NULL, alg + ) + self._backend.openssl_assert(alg[0] != self._backend._ffi.NULL) + oid = _obj2txt(self._backend, alg[0].algorithm) + return x509.ObjectIdentifier(oid) + + @utils.cached_property def extensions(self): - extensions = [] - seen_oids = set() x509_exts = self._backend._lib.X509_REQ_get_extensions(self._x509_req) - extcount = self._backend._lib.sk_X509_EXTENSION_num(x509_exts) - for i in range(0, extcount): - ext = self._backend._lib.sk_X509_EXTENSION_value(x509_exts, i) - assert ext != self._backend._ffi.NULL - crit = self._backend._lib.X509_EXTENSION_get_critical(ext) - critical = crit == 1 - oid = x509.ObjectIdentifier(_obj2txt(self._backend, ext.object)) - if oid in seen_oids: - raise x509.DuplicateExtension( - "Duplicate {0} extension found".format(oid), oid + x509_exts = self._backend._ffi.gc( + x509_exts, + lambda x: self._backend._lib.sk_X509_EXTENSION_pop_free( + x, self._backend._ffi.addressof( + self._backend._lib._original_lib, "X509_EXTENSION_free" ) - elif oid == x509.OID_BASIC_CONSTRAINTS: - value = _decode_basic_constraints(self._backend, ext) - elif critical: - raise x509.UnsupportedExtension( - "{0} is not currently supported".format(oid), oid - ) - else: - # Unsupported non-critical extension, silently skipping for now - seen_oids.add(oid) - continue - - seen_oids.add(oid) - extensions.append(x509.Extension(oid, critical, value)) - - return x509.Extensions(extensions) + ) + ) + return _CSR_EXTENSION_PARSER.parse(self._backend, x509_exts) def public_bytes(self, encoding): - if not isinstance(encoding, serialization.Encoding): - raise TypeError("encoding must be an item from the Encoding enum") - - bio = self._backend._create_mem_bio() + bio = self._backend._create_mem_bio_gc() if encoding is serialization.Encoding.PEM: res = self._backend._lib.PEM_write_bio_X509_REQ( bio, self._x509_req ) elif encoding is serialization.Encoding.DER: res = self._backend._lib.i2d_X509_REQ_bio(bio, self._x509_req) - assert res == 1 + else: + raise TypeError("encoding must be an item from the Encoding enum") + + self._backend.openssl_assert(res == 1) return self._backend._read_mem_bio(bio) + + @property + def tbs_certrequest_bytes(self): + pp = self._backend._ffi.new("unsigned char **") + res = self._backend._lib.i2d_re_X509_REQ_tbs(self._x509_req, pp) + self._backend.openssl_assert(res > 0) + pp = self._backend._ffi.gc( + pp, lambda pointer: self._backend._lib.OPENSSL_free(pointer[0]) + ) + return self._backend._ffi.buffer(pp[0], res)[:] + + @property + def signature(self): + sig = self._backend._ffi.new("ASN1_BIT_STRING **") + self._backend._lib.X509_REQ_get0_signature( + self._x509_req, sig, self._backend._ffi.NULL + ) + self._backend.openssl_assert(sig[0] != self._backend._ffi.NULL) + return _asn1_string_to_bytes(self._backend, sig[0]) + + @property + def is_signature_valid(self): + pkey = self._backend._lib.X509_REQ_get_pubkey(self._x509_req) + self._backend.openssl_assert(pkey != self._backend._ffi.NULL) + pkey = self._backend._ffi.gc(pkey, self._backend._lib.EVP_PKEY_free) + res = self._backend._lib.X509_REQ_verify(self._x509_req, pkey) + + if res != 1: + self._backend._consume_errors() + return False + + return True + + +@utils.register_interface( + x509.certificate_transparency.SignedCertificateTimestamp +) +class _SignedCertificateTimestamp(object): + def __init__(self, backend, sct_list, sct): + self._backend = backend + # Keep the SCT_LIST that this SCT came from alive. + self._sct_list = sct_list + self._sct = sct + + @property + def version(self): + version = self._backend._lib.SCT_get_version(self._sct) + assert version == self._backend._lib.SCT_VERSION_V1 + return x509.certificate_transparency.Version.v1 + + @property + def log_id(self): + out = self._backend._ffi.new("unsigned char **") + log_id_length = self._backend._lib.SCT_get0_log_id(self._sct, out) + assert log_id_length >= 0 + return self._backend._ffi.buffer(out[0], log_id_length)[:] + + @property + def timestamp(self): + timestamp = self._backend._lib.SCT_get_timestamp(self._sct) + milliseconds = timestamp % 1000 + return datetime.datetime.utcfromtimestamp( + timestamp // 1000 + ).replace(microsecond=milliseconds * 1000) + + @property + def entry_type(self): + entry_type = self._backend._lib.SCT_get_log_entry_type(self._sct) + # We currently only support loading SCTs from the X.509 extension, so + # we only have precerts. + assert entry_type == self._backend._lib.CT_LOG_ENTRY_TYPE_PRECERT + return x509.certificate_transparency.LogEntryType.PRE_CERTIFICATE + + @property + def _signature(self): + ptrptr = self._backend._ffi.new("unsigned char **") + res = self._backend._lib.SCT_get0_signature(self._sct, ptrptr) + self._backend.openssl_assert(res > 0) + self._backend.openssl_assert(ptrptr[0] != self._backend._ffi.NULL) + return self._backend._ffi.buffer(ptrptr[0], res)[:] + + def __hash__(self): + return hash(self._signature) + + def __eq__(self, other): + if not isinstance(other, _SignedCertificateTimestamp): + return NotImplemented + + return self._signature == other._signature + + def __ne__(self, other): + return not self == other diff --git a/src/cryptography/hazmat/bindings/commoncrypto/__init__.py b/src/cryptography/hazmat/bindings/commoncrypto/__init__.py deleted file mode 100644 index 4b540884..00000000 --- a/src/cryptography/hazmat/bindings/commoncrypto/__init__.py +++ /dev/null @@ -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 diff --git a/src/cryptography/hazmat/bindings/commoncrypto/binding.py b/src/cryptography/hazmat/bindings/commoncrypto/binding.py deleted file mode 100644 index 1695c041..00000000 --- a/src/cryptography/hazmat/bindings/commoncrypto/binding.py +++ /dev/null @@ -1,18 +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 - - def __init__(self): - pass diff --git a/src/cryptography/hazmat/bindings/openssl/_conditional.py b/src/cryptography/hazmat/bindings/openssl/_conditional.py new file mode 100644 index 00000000..3bc879c4 --- /dev/null +++ b/src/cryptography/hazmat/bindings/openssl/_conditional.py @@ -0,0 +1,361 @@ +# 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 + + +def cryptography_has_ec2m(): + return [ + "EC_POINT_set_affine_coordinates_GF2m", + "EC_POINT_get_affine_coordinates_GF2m", + "EC_POINT_set_compressed_coordinates_GF2m", + ] + + +def cryptography_has_rsa_r_pkcs_decoding_error(): + return [ + "RSA_R_PKCS_DECODING_ERROR" + ] + + +def cryptography_has_rsa_oaep_md(): + return [ + "EVP_PKEY_CTX_set_rsa_oaep_md", + ] + + +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", + ] + + +def cryptography_has_compression(): + return [ + "SSL_get_current_compression", + "SSL_get_current_expansion", + "SSL_COMP_get_name", + ] + + +def cryptography_has_102_verification(): + return [ + 'X509_V_ERR_SUITE_B_INVALID_VERSION', + 'X509_V_ERR_SUITE_B_INVALID_ALGORITHM', + 'X509_V_ERR_SUITE_B_INVALID_CURVE', + 'X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM', + 'X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED', + 'X509_V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256', + "X509_V_FLAG_SUITEB_128_LOS_ONLY", + "X509_V_FLAG_SUITEB_192_LOS", + "X509_V_FLAG_SUITEB_128_LOS", + ] + + +def cryptography_has_110_verification_params(): + return [ + "X509_CHECK_FLAG_NEVER_CHECK_SUBJECT" + ] + + +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 [ + "Cryptography_setup_ssl_threads", + ] + + +def cryptography_has_scrypt(): + return [ + "EVP_PBE_scrypt", + ] + + +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_x448(): + return [ + "EVP_PKEY_X448", + "NID_X448", + ] + + +def cryptography_has_ed448(): + return [ + "EVP_PKEY_ED448", + "NID_ED448", + ] + + +def cryptography_has_ed25519(): + return [ + "NID_ED25519", + "EVP_PKEY_ED25519", + ] + + +def cryptography_has_poly1305(): + return [ + "NID_poly1305", + "EVP_PKEY_POLY1305", + ] + + +def cryptography_has_oneshot_evp_digest_sign_verify(): + return [ + "EVP_DigestSign", + "EVP_DigestVerify", + ] + + +def cryptography_has_evp_digestfinal_xof(): + return [ + "EVP_DigestFinalXOF", + ] + + +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_mode_set", + "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", + "SSL_SESSION_get_max_early_data", + "SSL_write_early_data", + "SSL_read_early_data", + "SSL_CTX_set_max_early_data", + ] + + +def cryptography_has_keylog(): + return [ + "SSL_CTX_set_keylog_callback", + "SSL_CTX_get_keylog_callback", + ] + + +def cryptography_has_raw_key(): + return [ + "EVP_PKEY_new_raw_private_key", + "EVP_PKEY_new_raw_public_key", + "EVP_PKEY_get_raw_private_key", + "EVP_PKEY_get_raw_public_key", + ] + + +def cryptography_has_evp_r_memory_limit_exceeded(): + return [ + "EVP_R_MEMORY_LIMIT_EXCEEDED", + ] + + +def cryptography_has_engine(): + return [ + "ENGINE_by_id", + "ENGINE_init", + "ENGINE_finish", + "ENGINE_get_default_RAND", + "ENGINE_set_default_RAND", + "ENGINE_unregister_RAND", + "ENGINE_ctrl_cmd", + "ENGINE_free", + "ENGINE_get_name", + "Cryptography_add_osrandom_engine", + ] + + +def cryptography_has_verified_chain(): + return [ + "SSL_get0_verified_chain", + ] + + +# 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_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_COMPRESSION": cryptography_has_compression, + "Cryptography_HAS_102_VERIFICATION": cryptography_has_102_verification, + "Cryptography_HAS_110_VERIFICATION_PARAMS": ( + cryptography_has_110_verification_params + ), + "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_EVP_PKEY_DHX": cryptography_has_evp_pkey_dhx, + "Cryptography_HAS_MEM_FUNCTIONS": cryptography_has_mem_functions, + "Cryptography_HAS_SCT": cryptography_has_sct, + "Cryptography_HAS_X509_STORE_CTX_GET_ISSUER": ( + cryptography_has_x509_store_ctx_get_issuer + ), + "Cryptography_HAS_X25519": cryptography_has_x25519, + "Cryptography_HAS_X448": cryptography_has_x448, + "Cryptography_HAS_ED448": cryptography_has_ed448, + "Cryptography_HAS_ED25519": cryptography_has_ed25519, + "Cryptography_HAS_POLY1305": cryptography_has_poly1305, + "Cryptography_HAS_ONESHOT_EVP_DIGEST_SIGN_VERIFY": ( + cryptography_has_oneshot_evp_digest_sign_verify + ), + "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, + "Cryptography_HAS_KEYLOG": cryptography_has_keylog, + "Cryptography_HAS_RAW_KEY": cryptography_has_raw_key, + "Cryptography_HAS_EVP_DIGESTFINAL_XOF": ( + cryptography_has_evp_digestfinal_xof + ), + "Cryptography_HAS_EVP_R_MEMORY_LIMIT_EXCEEDED": ( + cryptography_has_evp_r_memory_limit_exceeded + ), + "Cryptography_HAS_ENGINE": cryptography_has_engine, + "Cryptography_HAS_VERIFIED_CHAIN": cryptography_has_verified_chain, +} diff --git a/src/cryptography/hazmat/bindings/openssl/binding.py b/src/cryptography/hazmat/bindings/openssl/binding.py index 35ea4979..4e23cd53 100644 --- a/src/cryptography/hazmat/bindings/openssl/binding.py +++ b/src/cryptography/hazmat/bindings/openssl/binding.py @@ -4,140 +4,178 @@ from __future__ import absolute_import, division, print_function -import os +import collections import threading +import types +import cryptography +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 + +_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): + errors = [] + while True: + code = lib.ERR_get_error() + if code == 0: + break + + err_lib = lib.ERR_GET_LIB(code) + err_func = lib.ERR_GET_FUNC(code) + 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. 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 build_conditional_library(lib, conditional_names): + conditional_lib = types.ModuleType("lib") + conditional_lib._original_lib = lib + excluded_names = set() + for condition, names_cb in conditional_names.items(): + if not getattr(lib, condition): + excluded_names.update(names_cb()) + + for attr in dir(lib): + if attr not in excluded_names: + setattr(conditional_lib, attr, getattr(lib, attr)) + + return conditional_lib class Binding(object): """ OpenSSL API wrapper. """ - lib = lib + lib = None ffi = ffi _lib_loaded = False - _locks = None - _lock_cb_handle = None - _rand_method = 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") - _retained = [] def __init__(self): self._ensure_ffi_initialized() @classmethod - def _ensure_ffi_initialized(cls): - if cls._lib_loaded: - return + def _register_osrandom_engine(cls): + # 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() + if cls.lib.Cryptography_HAS_ENGINE: + result = cls.lib.Cryptography_add_osrandom_engine() + _openssl_assert(cls.lib, result in (1, 2)) + @classmethod + def _ensure_ffi_initialized(cls): with cls._init_lock: if not cls._lib_loaded: + cls.lib = build_conditional_library(lib, CONDITIONAL_NAMES) cls._lib_loaded = True - res = cls._register_osrandom_engine() - assert res != 0 - - @classmethod - def _register_osrandom_engine(cls): - if cls._retained: - return 2 - - def retain(it): - cls._retained.append(it) - return it - method = cls.ffi.new("RAND_METHOD*") - retain(method) - method.seed = cls.ffi.NULL - - @retain - @cls.ffi.callback("int (*)(unsigned char *buf, int num)", error=0) - def osrandom_rand_bytes(buf, size): - signed = cls.ffi.cast("char*", buf) - result = os.urandom(size) - signed[0:size] = result - return 1 - - @retain - @cls.ffi.callback("int (*)(unsigned char *buf, int num)", error=0) - def osrandom_pseudo_rand_bytes(buf, size): - result = osrandom_rand_bytes(buf, size) - if result == 0: - return -1 - else: - return result - - @retain - @cls.ffi.callback("int (*)(void)", error=0) - def osrandom_rand_status(): - return 1 - - @retain - @cls.ffi.callback("ENGINE_GEN_INT_FUNC_PTR", error=0) - def osrandom_init(engine): - return 1 - - @retain - @cls.ffi.callback("ENGINE_GEN_INT_FUNC_PTR", error=0) - def osrandom_finish(engine): - return 1 - - method.bytes = osrandom_rand_bytes - method.cleanup = cls.ffi.NULL - method.add = cls.ffi.NULL - method.pseudorand = osrandom_pseudo_rand_bytes - method.status = osrandom_rand_status - - e = cls.lib.ENGINE_new() - result = (cls.lib.ENGINE_set_id(e, cls._osrandom_engine_id) - and cls.lib.ENGINE_set_name(e, cls._osrandom_engine_name) - and cls.lib.ENGINE_set_RAND(e, method) - and cls.lib.ENGINE_set_init_function(e, osrandom_init) - and cls.lib.ENGINE_set_finish_function(e, osrandom_finish) - and cls.lib.ENGINE_add(e)) - if not cls.lib.ENGINE_free(e): - return 0 - assert cls.lib.ENGINE_by_id(cls._osrandom_engine_id) != cls.ffi.NULL - return result + # initialize the SSL library + cls.lib.SSL_library_init() + # adds all ciphers/digests for EVP + cls.lib.OpenSSL_add_all_algorithms() + # loads error strings for libcrypto and libssl functions + cls.lib.SSL_load_error_strings() + cls._register_osrandom_engine() @classmethod def init_static_locks(cls): with cls._lock_init_lock: cls._ensure_ffi_initialized() - - if not cls._lock_cb_handle: - cls._lock_cb_handle = cls.ffi.callback( - "void(int, int, const char *, int)", - 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) + + +def _verify_package_version(version): + # Occasionally we run into situations where the version of the Python + # package does not match the version of the shared object that is loaded. + # This may occur in environments where multiple versions of cryptography + # are installed and available in the python path. To avoid errors cropping + # up later this code checks that the currently imported package and the + # shared object that were loaded have the same version and raise an + # ImportError if they do not + so_package_version = ffi.string(lib.CRYPTOGRAPHY_PACKAGE_VERSION) + if version.encode("ascii") != so_package_version: + raise ImportError( + "The version of cryptography does not match the loaded " + "shared object. This can happen if you have multiple copies of " + "cryptography installed in your Python path. Please try creating " + "a new virtual environment to resolve this issue. " + "Loaded python version: {}, shared object version: {}".format( + version, so_package_version + ) + ) - 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 - ) - ) +_verify_package_version(cryptography.__version__) + +# OpenSSL is not thread safe until the locks are initialized. We call this +# method in module scope so that it executes with the import lock. On +# Pythons < 3.4 this import lock is a global lock, which can prevent a race +# condition registering the OpenSSL locks. On Python 3.4+ the import lock +# is per module so this approach will not work. +Binding.init_static_locks() diff --git a/src/cryptography/hazmat/primitives/asymmetric/dh.py b/src/cryptography/hazmat/primitives/asymmetric/dh.py index 12d53eed..4fc99524 100644 --- a/src/cryptography/hazmat/primitives/asymmetric/dh.py +++ b/src/cryptography/hazmat/primitives/asymmetric/dh.py @@ -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 diff --git a/src/cryptography/hazmat/primitives/asymmetric/dsa.py b/src/cryptography/hazmat/primitives/asymmetric/dsa.py index 733a967c..e380a441 100644 --- a/src/cryptography/hazmat/primitives/asymmetric/dsa.py +++ b/src/cryptography/hazmat/primitives/asymmetric/dsa.py @@ -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): @@ -91,9 +97,6 @@ class DSAPublicKey(object): Returns an AsymmetricVerificationContext used for signing data. """ - -@six.add_metaclass(abc.ABCMeta) -class DSAPublicKeyWithSerialization(DSAPublicKey): @abc.abstractmethod def public_numbers(self): """ @@ -106,6 +109,15 @@ class DSAPublicKeyWithSerialization(DSAPublicKey): Returns the key serialized as bytes. """ + @abc.abstractmethod + def verify(self, signature, data, algorithm): + """ + Verifies the signature of the data. + """ + + +DSAPublicKeyWithSerialization = DSAPublicKey + def generate_parameters(key_size, backend): return backend.generate_dsa_parameters(key_size) @@ -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): diff --git a/src/cryptography/hazmat/primitives/asymmetric/ec.py b/src/cryptography/hazmat/primitives/asymmetric/ec.py index 631fcbf7..eef922dc 100644 --- a/src/cryptography/hazmat/primitives/asymmetric/ec.py +++ b/src/cryptography/hazmat/primitives/asymmetric/ec.py @@ -5,10 +5,34 @@ from __future__ import absolute_import, division, print_function import abc +import warnings 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") + BRAINPOOLP256R1 = ObjectIdentifier("1.3.36.3.3.2.8.1.1.7") + BRAINPOOLP384R1 = ObjectIdentifier("1.3.36.3.3.2.8.1.1.11") + BRAINPOOLP512R1 = ObjectIdentifier("1.3.36.3.3.2.8.1.1.13") + SECT163K1 = ObjectIdentifier("1.3.132.0.1") + SECT163R2 = ObjectIdentifier("1.3.132.0.15") + SECT233K1 = ObjectIdentifier("1.3.132.0.26") + SECT233R1 = ObjectIdentifier("1.3.132.0.27") + SECT283K1 = ObjectIdentifier("1.3.132.0.16") + SECT283R1 = ObjectIdentifier("1.3.132.0.17") + SECT409K1 = ObjectIdentifier("1.3.132.0.36") + SECT409R1 = ObjectIdentifier("1.3.132.0.37") + SECT571K1 = ObjectIdentifier("1.3.132.0.38") + SECT571R1 = ObjectIdentifier("1.3.132.0.39") @six.add_metaclass(abc.ABCMeta) @@ -22,7 +46,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. """ @@ -44,6 +68,13 @@ class EllipticCurvePrivateKey(object): """ @abc.abstractmethod + def exchange(self, algorithm, peer_public_key): + """ + Performs a key exchange operation using the provided algorithm with the + provided peer's public key. + """ + + @abc.abstractmethod def public_key(self): """ The EllipticCurvePublicKey for this private key. @@ -55,6 +86,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): @@ -85,9 +128,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. + """ -@six.add_metaclass(abc.ABCMeta) -class EllipticCurvePublicKeyWithSerialization(EllipticCurvePublicKey): @abc.abstractmethod def public_numbers(self): """ @@ -100,11 +146,36 @@ class EllipticCurvePublicKeyWithSerialization(EllipticCurvePublicKey): Returns the key serialized as bytes. """ + @abc.abstractmethod + def verify(self, signature, data, signature_algorithm): + """ + Verifies the signature of the data. + """ + + @classmethod + def from_encoded_point(cls, curve, data): + utils._check_bytes("data", data) + + if not isinstance(curve, EllipticCurve): + raise TypeError("curve must be an EllipticCurve instance") + + if len(data) == 0: + raise ValueError("data must not be an empty byte string") + + if six.indexbytes(data, 0) not in [0x02, 0x03, 0x04]: + raise ValueError("Unsupported elliptic curve point type") + + from cryptography.hazmat.backends.openssl.backend import backend + return backend.load_elliptic_curve_public_bytes(curve, data) + + +EllipticCurvePublicKeyWithSerialization = EllipticCurvePublicKey + @utils.register_interface(EllipticCurve) class SECT571R1(object): name = "sect571r1" - key_size = 571 + key_size = 570 @utils.register_interface(EllipticCurve) @@ -197,6 +268,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, @@ -219,6 +308,10 @@ _CURVE_TYPES = { "sect283r1": SECT283R1, "sect409r1": SECT409R1, "sect571r1": SECT571R1, + + "brainpoolP256r1": BrainpoolP256R1, + "brainpoolP384r1": BrainpoolP384R1, + "brainpoolP512r1": BrainpoolP512R1, } @@ -234,6 +327,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 ( @@ -252,6 +358,47 @@ class EllipticCurvePublicNumbers(object): def public_key(self, backend): return backend.load_elliptic_curve_public_numbers(self) + def encode_point(self): + warnings.warn( + "encode_point has been deprecated on EllipticCurvePublicNumbers" + " and will be removed in a future version. Please use " + "EllipticCurvePublicKey.public_bytes to obtain both " + "compressed and uncompressed point encoding.", + utils.PersistentlyDeprecated2019, + stacklevel=2, + ) + # key_size is in bits. Convert to bytes and round up + byte_length = (self.curve.key_size + 7) // 8 + return ( + b'\x04' + utils.int_to_bytes(self.x, byte_length) + + utils.int_to_bytes(self.y, byte_length) + ) + + @classmethod + def from_encoded_point(cls, curve, data): + if not isinstance(curve, EllipticCurve): + raise TypeError("curve must be an EllipticCurve instance") + + warnings.warn( + "Support for unsafe construction of public numbers from " + "encoded data will be removed in a future version. " + "Please use EllipticCurvePublicKey.from_encoded_point", + utils.PersistentlyDeprecated2019, + stacklevel=2, + ) + + if data.startswith(b'\x04'): + # key_size is in bits. Convert to bytes and round up + byte_length = (curve.key_size + 7) // 8 + if len(data) == 2 * byte_length + 1: + x = utils.int_from_bytes(data[1:byte_length + 1], 'big') + y = utils.int_from_bytes(data[byte_length + 1:], 'big') + return cls(x, y, curve) + else: + raise ValueError('Invalid elliptic curve point data length') + else: + raise ValueError('Unsupported elliptic curve point type') + curve = utils.read_only_property("_curve") x = utils.read_only_property("_x") y = utils.read_only_property("_y") @@ -270,6 +417,15 @@ 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}, " + "y={0.y}>".format(self) + ) + class EllipticCurvePrivateNumbers(object): def __init__(self, private_value, public_numbers): @@ -302,3 +458,43 @@ 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 + + +_OID_TO_CURVE = { + EllipticCurveOID.SECP192R1: SECP192R1, + EllipticCurveOID.SECP224R1: SECP224R1, + EllipticCurveOID.SECP256K1: SECP256K1, + EllipticCurveOID.SECP256R1: SECP256R1, + EllipticCurveOID.SECP384R1: SECP384R1, + EllipticCurveOID.SECP521R1: SECP521R1, + EllipticCurveOID.BRAINPOOLP256R1: BrainpoolP256R1, + EllipticCurveOID.BRAINPOOLP384R1: BrainpoolP384R1, + EllipticCurveOID.BRAINPOOLP512R1: BrainpoolP512R1, + EllipticCurveOID.SECT163K1: SECT163K1, + EllipticCurveOID.SECT163R2: SECT163R2, + EllipticCurveOID.SECT233K1: SECT233K1, + EllipticCurveOID.SECT233R1: SECT233R1, + EllipticCurveOID.SECT283K1: SECT283K1, + EllipticCurveOID.SECT283R1: SECT283R1, + EllipticCurveOID.SECT409K1: SECT409K1, + EllipticCurveOID.SECT409R1: SECT409R1, + EllipticCurveOID.SECT571K1: SECT571K1, + EllipticCurveOID.SECT571R1: SECT571R1, +} + + +def get_curve_for_oid(oid): + try: + return _OID_TO_CURVE[oid] + except KeyError: + raise LookupError( + "The provided object identifier has no matching elliptic " + "curve class" + ) diff --git a/src/cryptography/hazmat/primitives/asymmetric/ed25519.py b/src/cryptography/hazmat/primitives/asymmetric/ed25519.py new file mode 100644 index 00000000..d89445fa --- /dev/null +++ b/src/cryptography/hazmat/primitives/asymmetric/ed25519.py @@ -0,0 +1,84 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import absolute_import, division, print_function + +import abc + +import six + +from cryptography.exceptions import UnsupportedAlgorithm, _Reasons + + +_ED25519_KEY_SIZE = 32 +_ED25519_SIG_SIZE = 64 + + +@six.add_metaclass(abc.ABCMeta) +class Ed25519PublicKey(object): + @classmethod + def from_public_bytes(cls, data): + from cryptography.hazmat.backends.openssl.backend import backend + if not backend.ed25519_supported(): + raise UnsupportedAlgorithm( + "ed25519 is not supported by this version of OpenSSL.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM + ) + + return backend.ed25519_load_public_bytes(data) + + @abc.abstractmethod + def public_bytes(self, encoding, format): + """ + The serialized bytes of the public key. + """ + + @abc.abstractmethod + def verify(self, signature, data): + """ + Verify the signature. + """ + + +@six.add_metaclass(abc.ABCMeta) +class Ed25519PrivateKey(object): + @classmethod + def generate(cls): + from cryptography.hazmat.backends.openssl.backend import backend + if not backend.ed25519_supported(): + raise UnsupportedAlgorithm( + "ed25519 is not supported by this version of OpenSSL.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM + ) + + return backend.ed25519_generate_key() + + @classmethod + def from_private_bytes(cls, data): + from cryptography.hazmat.backends.openssl.backend import backend + if not backend.ed25519_supported(): + raise UnsupportedAlgorithm( + "ed25519 is not supported by this version of OpenSSL.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM + ) + + return backend.ed25519_load_private_bytes(data) + + @abc.abstractmethod + def public_key(self): + """ + The Ed25519PublicKey derived from the private key. + """ + + @abc.abstractmethod + def private_bytes(self, encoding, format, encryption_algorithm): + """ + The serialized bytes of the private key. + """ + + @abc.abstractmethod + def sign(self, data): + """ + Signs the data. + """ diff --git a/src/cryptography/hazmat/primitives/asymmetric/ed448.py b/src/cryptography/hazmat/primitives/asymmetric/ed448.py new file mode 100644 index 00000000..939157ab --- /dev/null +++ b/src/cryptography/hazmat/primitives/asymmetric/ed448.py @@ -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 + +import abc + +import six + +from cryptography.exceptions import UnsupportedAlgorithm, _Reasons + + +@six.add_metaclass(abc.ABCMeta) +class Ed448PublicKey(object): + @classmethod + def from_public_bytes(cls, data): + from cryptography.hazmat.backends.openssl.backend import backend + if not backend.ed448_supported(): + raise UnsupportedAlgorithm( + "ed448 is not supported by this version of OpenSSL.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM + ) + + return backend.ed448_load_public_bytes(data) + + @abc.abstractmethod + def public_bytes(self, encoding, format): + """ + The serialized bytes of the public key. + """ + + @abc.abstractmethod + def verify(self, signature, data): + """ + Verify the signature. + """ + + +@six.add_metaclass(abc.ABCMeta) +class Ed448PrivateKey(object): + @classmethod + def generate(cls): + from cryptography.hazmat.backends.openssl.backend import backend + if not backend.ed448_supported(): + raise UnsupportedAlgorithm( + "ed448 is not supported by this version of OpenSSL.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM + ) + return backend.ed448_generate_key() + + @classmethod + def from_private_bytes(cls, data): + from cryptography.hazmat.backends.openssl.backend import backend + if not backend.ed448_supported(): + raise UnsupportedAlgorithm( + "ed448 is not supported by this version of OpenSSL.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM + ) + + return backend.ed448_load_private_bytes(data) + + @abc.abstractmethod + def public_key(self): + """ + The Ed448PublicKey derived from the private key. + """ + + @abc.abstractmethod + def sign(self, data): + """ + Signs the data. + """ + + @abc.abstractmethod + def private_bytes(self, encoding, format, encryption_algorithm): + """ + The serialized bytes of the private key. + """ diff --git a/src/cryptography/hazmat/primitives/asymmetric/padding.py b/src/cryptography/hazmat/primitives/asymmetric/padding.py index c796d8e4..828e03bc 100644 --- a/src/cryptography/hazmat/primitives/asymmetric/padding.py +++ b/src/cryptography/hazmat/primitives/asymmetric/padding.py @@ -10,6 +10,7 @@ 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 +66,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 = (key.key_size + 6) // 8 + salt_length = emlen - hash_algorithm.digest_size - 2 + assert salt_length >= 0 + return salt_length diff --git a/src/cryptography/hazmat/primitives/asymmetric/rsa.py b/src/cryptography/hazmat/primitives/asymmetric/rsa.py index 772473fd..f20cdf9c 100644 --- a/src/cryptography/hazmat/primitives/asymmetric/rsa.py +++ b/src/cryptography/hazmat/primitives/asymmetric/rsa.py @@ -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): @@ -76,9 +86,6 @@ class RSAPublicKey(object): The bit length of the public modulus. """ - -@six.add_metaclass(abc.ABCMeta) -class RSAPublicKeyWithSerialization(RSAPublicKey): @abc.abstractmethod def public_numbers(self): """ @@ -91,6 +98,15 @@ class RSAPublicKeyWithSerialization(RSAPublicKey): Returns the key serialized as bytes. """ + @abc.abstractmethod + def verify(self, signature, data, padding, algorithm): + """ + Verifies the signature of the data. + """ + + +RSAPublicKeyWithSerialization = RSAPublicKey + def generate_private_key(public_exponent, key_size, backend): if not isinstance(backend, RSABackend): @@ -168,12 +184,12 @@ def _modinv(e, m): """ Modular Multiplicative Inverse. Returns x such that: (x*e) mod m == 1 """ - x1, y1, x2, y2 = 1, 0, 0, 1 + x1, x2 = 1, 0 a, b = e, m while b > 0: q, r = divmod(a, b) - xn, yn = x1 - q * x2, y1 - q * y2 - a, b, x1, y1, x2, y2 = b, r, x2, y2, xn, yn + xn = x1 - q * x2 + a, b, x1, x2 = b, r, x2, xn return x1 % m @@ -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) @@ -307,6 +323,17 @@ class RSAPrivateNumbers(object): def __ne__(self, other): return not self == other + def __hash__(self): + return hash(( + self.p, + self.q, + self.d, + self.dmp1, + self.dmq1, + self.iqmp, + self.public_numbers, + )) + class RSAPublicNumbers(object): def __init__(self, e, n): @@ -336,3 +363,6 @@ class RSAPublicNumbers(object): def __ne__(self, other): return not self == other + + def __hash__(self): + return hash((self.e, self.n)) diff --git a/src/cryptography/hazmat/primitives/asymmetric/utils.py b/src/cryptography/hazmat/primitives/asymmetric/utils.py index a03025bb..14d2abee 100644 --- a/src/cryptography/hazmat/primitives/asymmetric/utils.py +++ b/src/cryptography/hazmat/primitives/asymmetric/utils.py @@ -4,44 +4,34 @@ from __future__ import absolute_import, division, print_function -from pyasn1.codec.der import decoder, encoder -from pyasn1.error import PyAsn1Error -from pyasn1.type import namedtype, univ +from cryptography import utils +from cryptography.hazmat._der import ( + DERReader, INTEGER, SEQUENCE, encode_der, encode_der_integer +) +from cryptography.hazmat.primitives import hashes -import six - -class _DSSSigValue(univ.Sequence): - componentType = namedtype.NamedTypes( - namedtype.NamedType('r', univ.Integer()), - namedtype.NamedType('s', univ.Integer()) - ) +def decode_dss_signature(signature): + with DERReader(signature).read_single_element(SEQUENCE) as seq: + r = seq.read_element(INTEGER).as_integer() + s = seq.read_element(INTEGER).as_integer() + return r, s -def decode_rfc6979_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." - ) +def encode_dss_signature(r, s): + return encode_der( + SEQUENCE, + encode_der(INTEGER, encode_der_integer(r)), + encode_der(INTEGER, encode_der_integer(s)), + ) - r = int(data.getComponentByName('r')) - s = int(data.getComponentByName('s')) - return (r, s) +class Prehashed(object): + def __init__(self, algorithm): + if not isinstance(algorithm, hashes.HashAlgorithm): + raise TypeError("Expected instance of HashAlgorithm.") -def encode_rfc6979_signature(r, s): - if ( - not isinstance(r, six.integer_types) or - not isinstance(s, six.integer_types) - ): - raise ValueError("Both r and s must be integers") + self._algorithm = algorithm + self._digest_size = algorithm.digest_size - sig = _DSSSigValue() - sig.setComponentByName('r', r) - sig.setComponentByName('s', s) - return encoder.encode(sig) + digest_size = utils.read_only_property("_digest_size") diff --git a/src/cryptography/hazmat/primitives/asymmetric/x25519.py b/src/cryptography/hazmat/primitives/asymmetric/x25519.py new file mode 100644 index 00000000..61a95ffa --- /dev/null +++ b/src/cryptography/hazmat/primitives/asymmetric/x25519.py @@ -0,0 +1,73 @@ +# 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, encoding, format): + """ + The serialized bytes of the public key. + """ + + +@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 + if not backend.x25519_supported(): + raise UnsupportedAlgorithm( + "X25519 is not supported by this version of OpenSSL.", + _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM + ) + + return backend.x25519_load_private_bytes(data) + + @abc.abstractmethod + def public_key(self): + """ + The serialized bytes of the public key. + """ + + @abc.abstractmethod + def private_bytes(self, encoding, format, encryption_algorithm): + """ + The serialized bytes of the private key. + """ + + @abc.abstractmethod + def exchange(self, peer_public_key): + """ + Performs a key exchange operation using the provided peer's public key. + """ diff --git a/src/cryptography/hazmat/primitives/asymmetric/x448.py b/src/cryptography/hazmat/primitives/asymmetric/x448.py new file mode 100644 index 00000000..475e678f --- /dev/null +++ b/src/cryptography/hazmat/primitives/asymmetric/x448.py @@ -0,0 +1,73 @@ +# 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 X448PublicKey(object): + @classmethod + def from_public_bytes(cls, data): + from cryptography.hazmat.backends.openssl.backend import backend + if not backend.x448_supported(): + raise UnsupportedAlgorithm( + "X448 is not supported by this version of OpenSSL.", + _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM + ) + + return backend.x448_load_public_bytes(data) + + @abc.abstractmethod + def public_bytes(self, encoding, format): + """ + The serialized bytes of the public key. + """ + + +@six.add_metaclass(abc.ABCMeta) +class X448PrivateKey(object): + @classmethod + def generate(cls): + from cryptography.hazmat.backends.openssl.backend import backend + if not backend.x448_supported(): + raise UnsupportedAlgorithm( + "X448 is not supported by this version of OpenSSL.", + _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM + ) + return backend.x448_generate_key() + + @classmethod + def from_private_bytes(cls, data): + from cryptography.hazmat.backends.openssl.backend import backend + if not backend.x448_supported(): + raise UnsupportedAlgorithm( + "X448 is not supported by this version of OpenSSL.", + _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM + ) + + return backend.x448_load_private_bytes(data) + + @abc.abstractmethod + def public_key(self): + """ + The serialized bytes of the public key. + """ + + @abc.abstractmethod + def private_bytes(self, encoding, format, encryption_algorithm): + """ + The serialized bytes of the private key. + """ + + @abc.abstractmethod + def exchange(self, peer_public_key): + """ + Performs a key exchange operation using the provided peer's public key. + """ diff --git a/src/cryptography/hazmat/primitives/ciphers/__init__.py b/src/cryptography/hazmat/primitives/ciphers/__init__.py index b5dd0ed7..171b1c69 100644 --- a/src/cryptography/hazmat/primitives/ciphers/__init__.py +++ b/src/cryptography/hazmat/primitives/ciphers/__init__.py @@ -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", ] diff --git a/src/cryptography/hazmat/primitives/ciphers/aead.py b/src/cryptography/hazmat/primitives/ciphers/aead.py new file mode 100644 index 00000000..72cb30c3 --- /dev/null +++ b/src/cryptography/hazmat/primitives/ciphers/aead.py @@ -0,0 +1,182 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import absolute_import, division, print_function + +import os + +from cryptography import exceptions, utils +from cryptography.hazmat.backends.openssl import aead +from cryptography.hazmat.backends.openssl.backend import backend + + +class ChaCha20Poly1305(object): + _MAX_SIZE = 2 ** 32 + + def __init__(self, key): + if not backend.aead_cipher_supported(self): + raise exceptions.UnsupportedAlgorithm( + "ChaCha20Poly1305 is not supported by this version of OpenSSL", + exceptions._Reasons.UNSUPPORTED_CIPHER + ) + utils._check_byteslike("key", key) + + if len(key) != 32: + raise ValueError("ChaCha20Poly1305 key must be 32 bytes.") + + self._key = key + + @classmethod + def generate_key(cls): + return os.urandom(32) + + def encrypt(self, nonce, data, associated_data): + if associated_data is None: + associated_data = b"" + + if len(data) > self._MAX_SIZE or len(associated_data) > self._MAX_SIZE: + # This is OverflowError to match what cffi would raise + raise OverflowError( + "Data or associated data too long. Max 2**32 bytes" + ) + + self._check_params(nonce, data, associated_data) + return aead._encrypt( + backend, self, nonce, data, associated_data, 16 + ) + + def decrypt(self, nonce, data, associated_data): + if associated_data is None: + associated_data = b"" + + self._check_params(nonce, data, associated_data) + return aead._decrypt( + backend, self, nonce, data, associated_data, 16 + ) + + def _check_params(self, nonce, data, associated_data): + utils._check_byteslike("nonce", nonce) + utils._check_bytes("data", data) + utils._check_bytes("associated_data", associated_data) + if len(nonce) != 12: + raise ValueError("Nonce must be 12 bytes") + + +class AESCCM(object): + _MAX_SIZE = 2 ** 32 + + def __init__(self, key, tag_length=16): + utils._check_byteslike("key", key) + if len(key) not in (16, 24, 32): + raise ValueError("AESCCM key must be 128, 192, or 256 bits.") + + self._key = key + if not isinstance(tag_length, int): + raise TypeError("tag_length must be an integer") + + if tag_length not in (4, 6, 8, 10, 12, 14, 16): + raise ValueError("Invalid tag_length") + + self._tag_length = tag_length + + @classmethod + def generate_key(cls, bit_length): + if not isinstance(bit_length, int): + raise TypeError("bit_length must be an integer") + + if bit_length not in (128, 192, 256): + raise ValueError("bit_length must be 128, 192, or 256") + + return os.urandom(bit_length // 8) + + def encrypt(self, nonce, data, associated_data): + if associated_data is None: + associated_data = b"" + + if len(data) > self._MAX_SIZE or len(associated_data) > self._MAX_SIZE: + # This is OverflowError to match what cffi would raise + raise OverflowError( + "Data or associated data too long. Max 2**32 bytes" + ) + + self._check_params(nonce, data, associated_data) + self._validate_lengths(nonce, len(data)) + return aead._encrypt( + backend, self, nonce, data, associated_data, self._tag_length + ) + + def decrypt(self, nonce, data, associated_data): + if associated_data is None: + associated_data = b"" + + self._check_params(nonce, data, associated_data) + return aead._decrypt( + backend, self, nonce, data, associated_data, self._tag_length + ) + + def _validate_lengths(self, nonce, data_len): + # For information about computing this, see + # https://tools.ietf.org/html/rfc3610#section-2.1 + l_val = 15 - len(nonce) + if 2 ** (8 * l_val) < data_len: + raise ValueError("Data too long for nonce") + + def _check_params(self, nonce, data, associated_data): + utils._check_byteslike("nonce", nonce) + utils._check_bytes("data", data) + utils._check_bytes("associated_data", associated_data) + if not 7 <= len(nonce) <= 13: + raise ValueError("Nonce must be between 7 and 13 bytes") + + +class AESGCM(object): + _MAX_SIZE = 2 ** 32 + + def __init__(self, key): + utils._check_byteslike("key", key) + if len(key) not in (16, 24, 32): + raise ValueError("AESGCM key must be 128, 192, or 256 bits.") + + self._key = key + + @classmethod + def generate_key(cls, bit_length): + if not isinstance(bit_length, int): + raise TypeError("bit_length must be an integer") + + if bit_length not in (128, 192, 256): + raise ValueError("bit_length must be 128, 192, or 256") + + return os.urandom(bit_length // 8) + + def encrypt(self, nonce, data, associated_data): + if associated_data is None: + associated_data = b"" + + if len(data) > self._MAX_SIZE or len(associated_data) > self._MAX_SIZE: + # This is OverflowError to match what cffi would raise + raise OverflowError( + "Data or associated data too long. Max 2**32 bytes" + ) + + self._check_params(nonce, data, associated_data) + return aead._encrypt( + backend, self, nonce, data, associated_data, 16 + ) + + def decrypt(self, nonce, data, associated_data): + if associated_data is None: + associated_data = b"" + + self._check_params(nonce, data, associated_data) + return aead._decrypt( + backend, self, nonce, data, associated_data, 16 + ) + + def _check_params(self, nonce, data, associated_data): + utils._check_byteslike("nonce", nonce) + utils._check_bytes("data", data) + utils._check_bytes("associated_data", associated_data) + if len(nonce) == 0: + raise ValueError("Nonce must be at least 1 byte") diff --git a/src/cryptography/hazmat/primitives/ciphers/algorithms.py b/src/cryptography/hazmat/primitives/ciphers/algorithms.py index b71dddbb..f4d5160b 100644 --- a/src/cryptography/hazmat/primitives/ciphers/algorithms.py +++ b/src/cryptography/hazmat/primitives/ciphers/algorithms.py @@ -8,12 +8,16 @@ from cryptography import utils from cryptography.hazmat.primitives.ciphers import ( BlockCipherAlgorithm, CipherAlgorithm ) +from cryptography.hazmat.primitives.ciphers.modes import ModeWithNonce def _verify_key_size(algorithm, key): + # Verify that the key is instance of bytes + utils._check_byteslike("key", key) + # Verify that the key size matches the expected key size if len(key) * 8 not in algorithm.key_sizes: - raise ValueError("Invalid key size ({0}) for {1}.".format( + raise ValueError("Invalid key size ({}) for {}.".format( len(key) * 8, algorithm.name )) return key @@ -24,7 +28,8 @@ def _verify_key_size(algorithm, key): class AES(object): name = "AES" block_size = 128 - key_sizes = frozenset([128, 192, 256]) + # 512 added to support AES-256-XTS, which uses 512-bit keys + key_sizes = frozenset([128, 192, 256, 512]) def __init__(self, key): self.key = _verify_key_size(self, key) @@ -101,7 +106,7 @@ class CAST5(object): @utils.register_interface(CipherAlgorithm) class ARC4(object): name = "RC4" - key_sizes = frozenset([40, 56, 64, 80, 128, 192, 256]) + key_sizes = frozenset([40, 56, 64, 80, 128, 160, 192, 256]) def __init__(self, key): self.key = _verify_key_size(self, key) @@ -138,3 +143,25 @@ class SEED(object): @property def key_size(self): return len(self.key) * 8 + + +@utils.register_interface(CipherAlgorithm) +@utils.register_interface(ModeWithNonce) +class ChaCha20(object): + name = "ChaCha20" + key_sizes = frozenset([256]) + + def __init__(self, key, nonce): + self.key = _verify_key_size(self, key) + utils._check_byteslike("nonce", nonce) + + if len(nonce) != 16: + raise ValueError("nonce must be 128-bits (16 bytes)") + + self._nonce = nonce + + nonce = utils.read_only_property("_nonce") + + @property + def key_size(self): + return len(self.key) * 8 diff --git a/src/cryptography/hazmat/primitives/ciphers/base.py b/src/cryptography/hazmat/primitives/ciphers/base.py index 8f3028fc..4d5f8d60 100644 --- a/src/cryptography/hazmat/primitives/ciphers/base.py +++ b/src/cryptography/hazmat/primitives/ciphers/base.py @@ -51,6 +51,13 @@ class CipherContext(object): """ @abc.abstractmethod + def update_into(self, data, buf): + """ + Processes the provided bytes and writes the resulting data into the + provided buffer. Returns the number of bytes written. + """ + + @abc.abstractmethod def finalize(self): """ Returns the results of processing the final block as bytes. @@ -67,6 +74,16 @@ class AEADCipherContext(object): @six.add_metaclass(abc.ABCMeta) +class AEADDecryptionContext(object): + @abc.abstractmethod + def finalize_with_tag(self, tag): + """ + Returns the results of processing the final block as bytes and allows + delayed passing of the authentication tag. + """ + + +@six.add_metaclass(abc.ABCMeta) class AEADEncryptionContext(object): @abc.abstractproperty def tag(self): @@ -106,11 +123,6 @@ class Cipher(object): return self._wrap_ctx(ctx, encrypt=True) def decryptor(self): - if isinstance(self.mode, modes.ModeWithAuthenticationTag): - if self.mode.tag is None: - raise ValueError( - "Authentication tag must be provided when decrypting." - ) ctx = self._backend.create_symmetric_decryption_ctx( self.algorithm, self.mode ) @@ -136,6 +148,11 @@ class _CipherContext(object): raise AlreadyFinalized("Context was already finalized.") return self._ctx.update(data) + def update_into(self, data, buf): + if self._ctx is None: + raise AlreadyFinalized("Context was already finalized.") + return self._ctx.update_into(data, buf) + def finalize(self): if self._ctx is None: raise AlreadyFinalized("Context was already finalized.") @@ -146,18 +163,35 @@ class _CipherContext(object): @utils.register_interface(AEADCipherContext) @utils.register_interface(CipherContext) +@utils.register_interface(AEADDecryptionContext) class _AEADCipherContext(object): def __init__(self, ctx): self._ctx = ctx + self._bytes_processed = 0 + self._aad_bytes_processed = 0 self._tag = None self._updated = False - def update(self, data): + def _check_limit(self, data_size): if self._ctx is None: raise AlreadyFinalized("Context was already finalized.") self._updated = True + self._bytes_processed += data_size + if self._bytes_processed > self._ctx._mode._MAX_ENCRYPTED_BYTES: + raise ValueError( + "{} has a maximum encrypted byte limit of {}".format( + self._ctx._mode.name, self._ctx._mode._MAX_ENCRYPTED_BYTES + ) + ) + + def update(self, data): + self._check_limit(len(data)) return self._ctx.update(data) + def update_into(self, data, buf): + self._check_limit(len(data)) + return self._ctx.update_into(data, buf) + def finalize(self): if self._ctx is None: raise AlreadyFinalized("Context was already finalized.") @@ -166,11 +200,28 @@ class _AEADCipherContext(object): self._ctx = None return data + def finalize_with_tag(self, tag): + if self._ctx is None: + raise AlreadyFinalized("Context was already finalized.") + data = self._ctx.finalize_with_tag(tag) + self._tag = self._ctx.tag + self._ctx = None + return data + def authenticate_additional_data(self, data): if self._ctx is None: raise AlreadyFinalized("Context was already finalized.") if self._updated: raise AlreadyUpdated("Update has been called on this context.") + + self._aad_bytes_processed += len(data) + if self._aad_bytes_processed > self._ctx._mode._MAX_AAD_BYTES: + raise ValueError( + "{} has a maximum AAD byte limit of {}".format( + self._ctx._mode.name, self._ctx._mode._MAX_AAD_BYTES + ) + ) + self._ctx.authenticate_additional_data(data) diff --git a/src/cryptography/hazmat/primitives/ciphers/modes.py b/src/cryptography/hazmat/primitives/ciphers/modes.py index e31c9060..78fa1c48 100644 --- a/src/cryptography/hazmat/primitives/ciphers/modes.py +++ b/src/cryptography/hazmat/primitives/ciphers/modes.py @@ -37,6 +37,15 @@ class ModeWithInitializationVector(object): @six.add_metaclass(abc.ABCMeta) +class ModeWithTweak(object): + @abc.abstractproperty + def tweak(self): + """ + The value of the tweak for this mode as bytes. + """ + + +@six.add_metaclass(abc.ABCMeta) class ModeWithNonce(object): @abc.abstractproperty def nonce(self): @@ -54,31 +63,66 @@ class ModeWithAuthenticationTag(object): """ +def _check_aes_key_length(self, algorithm): + if algorithm.key_size > 256 and algorithm.name == "AES": + raise ValueError( + "Only 128, 192, and 256 bit keys are allowed for this AES mode" + ) + + def _check_iv_length(self, algorithm): if len(self.initialization_vector) * 8 != algorithm.block_size: - raise ValueError("Invalid IV size ({0}) for {1}.".format( + raise ValueError("Invalid IV size ({}) for {}.".format( len(self.initialization_vector), self.name )) +def _check_iv_and_key_length(self, algorithm): + _check_aes_key_length(self, algorithm) + _check_iv_length(self, algorithm) + + @utils.register_interface(Mode) @utils.register_interface(ModeWithInitializationVector) class CBC(object): name = "CBC" def __init__(self, initialization_vector): + utils._check_byteslike("initialization_vector", initialization_vector) self._initialization_vector = initialization_vector initialization_vector = utils.read_only_property("_initialization_vector") - validate_for_algorithm = _check_iv_length + validate_for_algorithm = _check_iv_and_key_length + + +@utils.register_interface(Mode) +@utils.register_interface(ModeWithTweak) +class XTS(object): + name = "XTS" + + def __init__(self, tweak): + utils._check_byteslike("tweak", tweak) + + if len(tweak) != 16: + raise ValueError("tweak must be 128-bits (16 bytes)") + + self._tweak = tweak + + tweak = utils.read_only_property("_tweak") + + def validate_for_algorithm(self, algorithm): + if algorithm.key_size not in (256, 512): + raise ValueError( + "The XTS specification requires a 256-bit key for AES-128-XTS" + " and 512-bit key for AES-256-XTS" + ) @utils.register_interface(Mode) class ECB(object): name = "ECB" - def validate_for_algorithm(self, algorithm): - pass + validate_for_algorithm = _check_aes_key_length @utils.register_interface(Mode) @@ -87,10 +131,11 @@ class OFB(object): name = "OFB" def __init__(self, initialization_vector): + utils._check_byteslike("initialization_vector", initialization_vector) self._initialization_vector = initialization_vector initialization_vector = utils.read_only_property("_initialization_vector") - validate_for_algorithm = _check_iv_length + validate_for_algorithm = _check_iv_and_key_length @utils.register_interface(Mode) @@ -99,10 +144,11 @@ class CFB(object): name = "CFB" def __init__(self, initialization_vector): + utils._check_byteslike("initialization_vector", initialization_vector) self._initialization_vector = initialization_vector initialization_vector = utils.read_only_property("_initialization_vector") - validate_for_algorithm = _check_iv_length + validate_for_algorithm = _check_iv_and_key_length @utils.register_interface(Mode) @@ -111,10 +157,11 @@ class CFB8(object): name = "CFB8" def __init__(self, initialization_vector): + utils._check_byteslike("initialization_vector", initialization_vector) self._initialization_vector = initialization_vector initialization_vector = utils.read_only_property("_initialization_vector") - validate_for_algorithm = _check_iv_length + validate_for_algorithm = _check_iv_and_key_length @utils.register_interface(Mode) @@ -123,13 +170,15 @@ class CTR(object): name = "CTR" def __init__(self, nonce): + utils._check_byteslike("nonce", nonce) self._nonce = nonce nonce = utils.read_only_property("_nonce") def validate_for_algorithm(self, algorithm): + _check_aes_key_length(self, algorithm) if len(self.nonce) * 8 != algorithm.block_size: - raise ValueError("Invalid nonce size ({0}) for {1}.".format( + raise ValueError("Invalid nonce size ({}) for {}.".format( len(self.nonce), self.name )) @@ -139,24 +188,31 @@ class CTR(object): @utils.register_interface(ModeWithAuthenticationTag) class GCM(object): name = "GCM" + _MAX_ENCRYPTED_BYTES = (2 ** 39 - 256) // 8 + _MAX_AAD_BYTES = (2 ** 64) // 8 def __init__(self, initialization_vector, tag=None, min_tag_length=16): # len(initialization_vector) must in [1, 2 ** 64), but it's impossible # to actually construct a bytes object that large, so we don't check # for it - if min_tag_length < 4: - raise ValueError("min_tag_length must be >= 4") - if tag is not None and len(tag) < min_tag_length: - raise ValueError( - "Authentication tag must be {0} bytes or longer.".format( - min_tag_length) - ) - + utils._check_byteslike("initialization_vector", initialization_vector) + if len(initialization_vector) == 0: + raise ValueError("initialization_vector must be at least 1 byte") self._initialization_vector = initialization_vector + if tag is not None: + utils._check_bytes("tag", tag) + if min_tag_length < 4: + raise ValueError("min_tag_length must be >= 4") + if len(tag) < min_tag_length: + raise ValueError( + "Authentication tag must be {} bytes or longer.".format( + min_tag_length) + ) self._tag = tag + self._min_tag_length = min_tag_length tag = utils.read_only_property("_tag") initialization_vector = utils.read_only_property("_initialization_vector") def validate_for_algorithm(self, algorithm): - pass + _check_aes_key_length(self, algorithm) diff --git a/src/cryptography/hazmat/primitives/cmac.py b/src/cryptography/hazmat/primitives/cmac.py index c2038a30..95a8d975 100644 --- a/src/cryptography/hazmat/primitives/cmac.py +++ b/src/cryptography/hazmat/primitives/cmac.py @@ -9,10 +9,9 @@ from cryptography.exceptions import ( AlreadyFinalized, UnsupportedAlgorithm, _Reasons ) from cryptography.hazmat.backends.interfaces import CMACBackend -from cryptography.hazmat.primitives import ciphers, interfaces +from cryptography.hazmat.primitives import ciphers -@utils.register_interface(interfaces.MACContext) class CMAC(object): def __init__(self, algorithm, backend, ctx=None): if not isinstance(backend, CMACBackend): @@ -36,8 +35,8 @@ class CMAC(object): def update(self, data): if self._ctx is None: raise AlreadyFinalized("Context was already finalized.") - if not isinstance(data, bytes): - raise TypeError("data must be bytes.") + + utils._check_bytes("data", data) self._ctx.update(data) def finalize(self): @@ -48,8 +47,7 @@ class CMAC(object): return digest def verify(self, signature): - if not isinstance(signature, bytes): - raise TypeError("signature must be bytes.") + utils._check_bytes("signature", signature) if self._ctx is None: raise AlreadyFinalized("Context was already finalized.") diff --git a/src/cryptography/hazmat/primitives/constant_time.py b/src/cryptography/hazmat/primitives/constant_time.py index 5a682ca9..7f41b9ef 100644 --- a/src/cryptography/hazmat/primitives/constant_time.py +++ b/src/cryptography/hazmat/primitives/constant_time.py @@ -6,21 +6,9 @@ from __future__ import absolute_import, division, print_function import hmac -from cryptography.hazmat.bindings._constant_time import lib +def bytes_eq(a, b): + if not isinstance(a, bytes) or not isinstance(b, bytes): + raise TypeError("a and b must be bytes.") -if hasattr(hmac, "compare_digest"): - def bytes_eq(a, b): - if not isinstance(a, bytes) or not isinstance(b, bytes): - raise TypeError("a and b must be bytes.") - - return hmac.compare_digest(a, b) - -else: - def bytes_eq(a, b): - if not isinstance(a, bytes) or not isinstance(b, bytes): - raise TypeError("a and b must be bytes.") - - return lib.Cryptography_constant_time_bytes_eq( - a, len(a), b, len(b) - ) == 1 + return hmac.compare_digest(a, b) diff --git a/src/cryptography/hazmat/primitives/hashes.py b/src/cryptography/hazmat/primitives/hashes.py index 6bc8500b..9be2b600 100644 --- a/src/cryptography/hazmat/primitives/hashes.py +++ b/src/cryptography/hazmat/primitives/hashes.py @@ -29,12 +29,6 @@ class HashAlgorithm(object): The size of the resulting digest in bytes. """ - @abc.abstractproperty - def block_size(self): - """ - The internal block size of the hash algorithm in bytes. - """ - @six.add_metaclass(abc.ABCMeta) class HashContext(object): @@ -63,6 +57,13 @@ class HashContext(object): """ +@six.add_metaclass(abc.ABCMeta) +class ExtendableOutputFunction(object): + """ + An interface for extendable output functions. + """ + + @utils.register_interface(HashContext) class Hash(object): def __init__(self, algorithm, backend, ctx=None): @@ -88,8 +89,7 @@ class Hash(object): def update(self, data): if self._ctx is None: raise AlreadyFinalized("Context was already finalized.") - if not isinstance(data, bytes): - raise TypeError("data must be bytes.") + utils._check_byteslike("data", data) self._ctx.update(data) def copy(self): @@ -115,6 +115,20 @@ class SHA1(object): @utils.register_interface(HashAlgorithm) +class SHA512_224(object): # noqa: N801 + name = "sha512-224" + digest_size = 28 + block_size = 128 + + +@utils.register_interface(HashAlgorithm) +class SHA512_256(object): # noqa: N801 + name = "sha512-256" + digest_size = 32 + block_size = 128 + + +@utils.register_interface(HashAlgorithm) class SHA224(object): name = "sha224" digest_size = 28 @@ -143,17 +157,61 @@ class SHA512(object): @utils.register_interface(HashAlgorithm) -class RIPEMD160(object): - name = "ripemd160" - digest_size = 20 - block_size = 64 +class SHA3_224(object): # noqa: N801 + name = "sha3-224" + digest_size = 28 @utils.register_interface(HashAlgorithm) -class Whirlpool(object): - name = "whirlpool" +class SHA3_256(object): # noqa: N801 + name = "sha3-256" + digest_size = 32 + + +@utils.register_interface(HashAlgorithm) +class SHA3_384(object): # noqa: N801 + name = "sha3-384" + digest_size = 48 + + +@utils.register_interface(HashAlgorithm) +class SHA3_512(object): # noqa: N801 + name = "sha3-512" digest_size = 64 - block_size = 64 + + +@utils.register_interface(HashAlgorithm) +@utils.register_interface(ExtendableOutputFunction) +class SHAKE128(object): + name = "shake128" + + def __init__(self, digest_size): + if not isinstance(digest_size, six.integer_types): + raise TypeError("digest_size must be an integer") + + if digest_size < 1: + raise ValueError("digest_size must be a positive integer") + + self._digest_size = digest_size + + digest_size = utils.read_only_property("_digest_size") + + +@utils.register_interface(HashAlgorithm) +@utils.register_interface(ExtendableOutputFunction) +class SHAKE256(object): + name = "shake256" + + def __init__(self, digest_size): + if not isinstance(digest_size, six.integer_types): + raise TypeError("digest_size must be an integer") + + if digest_size < 1: + raise ValueError("digest_size must be a positive integer") + + self._digest_size = digest_size + + digest_size = utils.read_only_property("_digest_size") @utils.register_interface(HashAlgorithm) @@ -161,3 +219,37 @@ class MD5(object): name = "md5" digest_size = 16 block_size = 64 + + +@utils.register_interface(HashAlgorithm) +class BLAKE2b(object): + name = "blake2b" + _max_digest_size = 64 + _min_digest_size = 1 + block_size = 128 + + def __init__(self, digest_size): + + if digest_size != 64: + raise ValueError("Digest size must be 64") + + self._digest_size = digest_size + + digest_size = utils.read_only_property("_digest_size") + + +@utils.register_interface(HashAlgorithm) +class BLAKE2s(object): + name = "blake2s" + block_size = 64 + _max_digest_size = 32 + _min_digest_size = 1 + + def __init__(self, digest_size): + + if digest_size != 32: + raise ValueError("Digest size must be 32") + + self._digest_size = digest_size + + digest_size = utils.read_only_property("_digest_size") diff --git a/src/cryptography/hazmat/primitives/hmac.py b/src/cryptography/hazmat/primitives/hmac.py index 15b9ee6e..9eceeac2 100644 --- a/src/cryptography/hazmat/primitives/hmac.py +++ b/src/cryptography/hazmat/primitives/hmac.py @@ -9,10 +9,9 @@ from cryptography.exceptions import ( AlreadyFinalized, UnsupportedAlgorithm, _Reasons ) from cryptography.hazmat.backends.interfaces import HMACBackend -from cryptography.hazmat.primitives import hashes, interfaces +from cryptography.hazmat.primitives import hashes -@utils.register_interface(interfaces.MACContext) @utils.register_interface(hashes.HashContext) class HMAC(object): def __init__(self, key, algorithm, backend, ctx=None): @@ -38,8 +37,7 @@ class HMAC(object): def update(self, data): if self._ctx is None: raise AlreadyFinalized("Context was already finalized.") - if not isinstance(data, bytes): - raise TypeError("data must be bytes.") + utils._check_byteslike("data", data) self._ctx.update(data) def copy(self): @@ -60,8 +58,7 @@ class HMAC(object): return digest def verify(self, signature): - if not isinstance(signature, bytes): - raise TypeError("signature must be bytes.") + utils._check_bytes("signature", signature) if self._ctx is None: raise AlreadyFinalized("Context was already finalized.") diff --git a/src/cryptography/hazmat/primitives/interfaces/__init__.py b/src/cryptography/hazmat/primitives/interfaces/__init__.py deleted file mode 100644 index 4c95190b..00000000 --- a/src/cryptography/hazmat/primitives/interfaces/__init__.py +++ /dev/null @@ -1,37 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import absolute_import, division, print_function - -import abc - -import six - - -@six.add_metaclass(abc.ABCMeta) -class MACContext(object): - @abc.abstractmethod - def update(self, data): - """ - Processes the provided bytes. - """ - - @abc.abstractmethod - def finalize(self): - """ - Returns the message authentication code as bytes. - """ - - @abc.abstractmethod - def copy(self): - """ - Return a MACContext that is a copy of the current context. - """ - - @abc.abstractmethod - def verify(self, signature): - """ - Checks if the generated message authentication code matches the - signature. - """ diff --git a/src/cryptography/hazmat/primitives/kdf/concatkdf.py b/src/cryptography/hazmat/primitives/kdf/concatkdf.py index c6399e4f..7cb63856 100644 --- a/src/cryptography/hazmat/primitives/kdf/concatkdf.py +++ b/src/cryptography/hazmat/primitives/kdf/concatkdf.py @@ -24,17 +24,15 @@ def _common_args_checks(algorithm, length, otherinfo): max_length = algorithm.digest_size * (2 ** 32 - 1) if length > max_length: raise ValueError( - "Can not derive keys larger than {0} bits.".format( + "Can not derive keys larger than {} bits.".format( max_length )) - if not (otherinfo is None or isinstance(otherinfo, bytes)): - raise TypeError("otherinfo must be bytes.") + if otherinfo is not None: + utils._check_bytes("otherinfo", otherinfo) def _concatkdf_derive(key_material, length, auxfn, otherinfo): - if not isinstance(key_material, bytes): - raise TypeError("key_material must be bytes.") - + utils._check_byteslike("key_material", key_material) output = [b""] outlen = 0 counter = 1 @@ -96,10 +94,11 @@ class ConcatKDFHMAC(object): if self._otherinfo is None: self._otherinfo = b"" - if not (salt is None or isinstance(salt, bytes)): - raise TypeError("salt must be bytes.") if salt is None: salt = b"\x00" * algorithm.block_size + else: + utils._check_bytes("salt", salt) + self._salt = salt if not isinstance(backend, HMACBackend): diff --git a/src/cryptography/hazmat/primitives/kdf/hkdf.py b/src/cryptography/hazmat/primitives/kdf/hkdf.py index f738bbdc..01f0f288 100644 --- a/src/cryptography/hazmat/primitives/kdf/hkdf.py +++ b/src/cryptography/hazmat/primitives/kdf/hkdf.py @@ -26,11 +26,10 @@ class HKDF(object): self._algorithm = algorithm - if not (salt is None or isinstance(salt, bytes)): - raise TypeError("salt must be bytes.") - if salt is None: - salt = b"\x00" * (self._algorithm.digest_size // 8) + salt = b"\x00" * self._algorithm.digest_size + else: + utils._check_bytes("salt", salt) self._salt = salt @@ -44,9 +43,7 @@ class HKDF(object): return h.finalize() def derive(self, key_material): - if not isinstance(key_material, bytes): - raise TypeError("key_material must be bytes.") - + utils._check_byteslike("key_material", key_material) return self._hkdf_expand.derive(self._extract(key_material)) def verify(self, key_material, expected_key): @@ -67,21 +64,20 @@ class HKDFExpand(object): self._backend = backend - max_length = 255 * (algorithm.digest_size // 8) + max_length = 255 * algorithm.digest_size if length > max_length: raise ValueError( - "Can not derive keys larger than {0} octets.".format( + "Can not derive keys larger than {} octets.".format( max_length )) self._length = length - if not (info is None or isinstance(info, bytes)): - raise TypeError("info must be bytes.") - if info is None: info = b"" + else: + utils._check_bytes("info", info) self._info = info @@ -91,7 +87,7 @@ class HKDFExpand(object): output = [b""] counter = 1 - while (self._algorithm.digest_size // 8) * len(output) < self._length: + while self._algorithm.digest_size * (len(output) - 1) < self._length: h = hmac.HMAC(key_material, self._algorithm, backend=self._backend) h.update(output[-1]) h.update(self._info) @@ -102,9 +98,7 @@ class HKDFExpand(object): return b"".join(output)[:self._length] def derive(self, key_material): - if not isinstance(key_material, bytes): - raise TypeError("key_material must be bytes.") - + utils._check_byteslike("key_material", key_material) if self._used: raise AlreadyFinalized diff --git a/src/cryptography/hazmat/primitives/kdf/kbkdf.py b/src/cryptography/hazmat/primitives/kdf/kbkdf.py new file mode 100644 index 00000000..56783a85 --- /dev/null +++ b/src/cryptography/hazmat/primitives/kdf/kbkdf.py @@ -0,0 +1,145 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import absolute_import, division, print_function + +from enum import Enum + +from six.moves import range + +from cryptography import utils +from cryptography.exceptions import ( + AlreadyFinalized, InvalidKey, UnsupportedAlgorithm, _Reasons +) +from cryptography.hazmat.backends.interfaces import HMACBackend +from cryptography.hazmat.primitives import constant_time, hashes, hmac +from cryptography.hazmat.primitives.kdf import KeyDerivationFunction + + +class Mode(Enum): + CounterMode = "ctr" + + +class CounterLocation(Enum): + BeforeFixed = "before_fixed" + AfterFixed = "after_fixed" + + +@utils.register_interface(KeyDerivationFunction) +class KBKDFHMAC(object): + def __init__(self, algorithm, mode, length, rlen, llen, + location, label, context, fixed, backend): + if not isinstance(backend, HMACBackend): + raise UnsupportedAlgorithm( + "Backend object does not implement HMACBackend.", + _Reasons.BACKEND_MISSING_INTERFACE + ) + + if not isinstance(algorithm, hashes.HashAlgorithm): + raise UnsupportedAlgorithm( + "Algorithm supplied is not a supported hash algorithm.", + _Reasons.UNSUPPORTED_HASH + ) + + if not backend.hmac_supported(algorithm): + raise UnsupportedAlgorithm( + "Algorithm supplied is not a supported hmac algorithm.", + _Reasons.UNSUPPORTED_HASH + ) + + if not isinstance(mode, Mode): + raise TypeError("mode must be of type Mode") + + if not isinstance(location, CounterLocation): + raise TypeError("location must be of type CounterLocation") + + if (label or context) and fixed: + raise ValueError("When supplying fixed data, " + "label and context are ignored.") + + if rlen is None or not self._valid_byte_length(rlen): + raise ValueError("rlen must be between 1 and 4") + + if llen is None and fixed is None: + raise ValueError("Please specify an llen") + + if llen is not None and not isinstance(llen, int): + raise TypeError("llen must be an integer") + + if label is None: + label = b'' + + if context is None: + context = b'' + + utils._check_bytes("label", label) + utils._check_bytes("context", context) + self._algorithm = algorithm + self._mode = mode + self._length = length + self._rlen = rlen + self._llen = llen + self._location = location + self._label = label + self._context = context + self._backend = backend + self._used = False + self._fixed_data = fixed + + def _valid_byte_length(self, value): + if not isinstance(value, int): + raise TypeError('value must be of type int') + + value_bin = utils.int_to_bytes(1, value) + if not 1 <= len(value_bin) <= 4: + return False + return True + + def derive(self, key_material): + if self._used: + raise AlreadyFinalized + + utils._check_byteslike("key_material", key_material) + self._used = True + + # inverse floor division (equivalent to ceiling) + rounds = -(-self._length // self._algorithm.digest_size) + + output = [b''] + + # For counter mode, the number of iterations shall not be + # larger than 2^r-1, where r <= 32 is the binary length of the counter + # This ensures that the counter values used as an input to the + # PRF will not repeat during a particular call to the KDF function. + r_bin = utils.int_to_bytes(1, self._rlen) + if rounds > pow(2, len(r_bin) * 8) - 1: + raise ValueError('There are too many iterations.') + + for i in range(1, rounds + 1): + h = hmac.HMAC(key_material, self._algorithm, backend=self._backend) + + counter = utils.int_to_bytes(i, self._rlen) + if self._location == CounterLocation.BeforeFixed: + h.update(counter) + + h.update(self._generate_fixed_input()) + + if self._location == CounterLocation.AfterFixed: + h.update(counter) + + output.append(h.finalize()) + + return b''.join(output)[:self._length] + + def _generate_fixed_input(self): + if self._fixed_data and isinstance(self._fixed_data, bytes): + return self._fixed_data + + l_val = utils.int_to_bytes(self._length * 8, self._llen) + + return b"".join([self._label, b"\x00", self._context, l_val]) + + def verify(self, key_material, expected_key): + if not constant_time.bytes_eq(self.derive(key_material), expected_key): + raise InvalidKey diff --git a/src/cryptography/hazmat/primitives/kdf/pbkdf2.py b/src/cryptography/hazmat/primitives/kdf/pbkdf2.py index f8ce7a3b..07d8ac67 100644 --- a/src/cryptography/hazmat/primitives/kdf/pbkdf2.py +++ b/src/cryptography/hazmat/primitives/kdf/pbkdf2.py @@ -24,15 +24,14 @@ class PBKDF2HMAC(object): if not backend.pbkdf2_hmac_supported(algorithm): raise UnsupportedAlgorithm( - "{0} is not supported for PBKDF2 by this backend.".format( + "{} is not supported for PBKDF2 by this backend.".format( algorithm.name), _Reasons.UNSUPPORTED_HASH ) self._used = False self._algorithm = algorithm self._length = length - if not isinstance(salt, bytes): - raise TypeError("salt must be bytes.") + utils._check_bytes("salt", salt) self._salt = salt self._iterations = iterations self._backend = backend @@ -42,8 +41,7 @@ class PBKDF2HMAC(object): raise AlreadyFinalized("PBKDF2 instances can only be used once.") self._used = True - if not isinstance(key_material, bytes): - raise TypeError("key_material must be bytes.") + utils._check_byteslike("key_material", key_material) return self._backend.derive_pbkdf2_hmac( self._algorithm, self._length, diff --git a/src/cryptography/hazmat/primitives/kdf/scrypt.py b/src/cryptography/hazmat/primitives/kdf/scrypt.py new file mode 100644 index 00000000..df9745e6 --- /dev/null +++ b/src/cryptography/hazmat/primitives/kdf/scrypt.py @@ -0,0 +1,63 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import absolute_import, division, print_function + +import sys + +from cryptography import utils +from cryptography.exceptions import ( + AlreadyFinalized, InvalidKey, UnsupportedAlgorithm, _Reasons +) +from cryptography.hazmat.backends.interfaces import ScryptBackend +from cryptography.hazmat.primitives import constant_time +from cryptography.hazmat.primitives.kdf import KeyDerivationFunction + + +# This is used by the scrypt tests to skip tests that require more memory +# than the MEM_LIMIT +_MEM_LIMIT = sys.maxsize // 2 + + +@utils.register_interface(KeyDerivationFunction) +class Scrypt(object): + def __init__(self, salt, length, n, r, p, backend): + if not isinstance(backend, ScryptBackend): + raise UnsupportedAlgorithm( + "Backend object does not implement ScryptBackend.", + _Reasons.BACKEND_MISSING_INTERFACE + ) + + self._length = length + utils._check_bytes("salt", salt) + if n < 2 or (n & (n - 1)) != 0: + raise ValueError("n must be greater than 1 and be a power of 2.") + + if r < 1: + raise ValueError("r must be greater than or equal to 1.") + + if p < 1: + raise ValueError("p must be greater than or equal to 1.") + + self._used = False + self._salt = salt + self._n = n + self._r = r + self._p = p + self._backend = backend + + def derive(self, key_material): + if self._used: + raise AlreadyFinalized("Scrypt instances can only be used once.") + self._used = True + + utils._check_byteslike("key_material", key_material) + return self._backend.derive_scrypt( + key_material, self._salt, self._length, self._n, self._r, self._p + ) + + def verify(self, key_material, expected_key): + derived_key = self.derive(key_material) + if not constant_time.bytes_eq(derived_key, expected_key): + raise InvalidKey("Keys do not match.") diff --git a/src/cryptography/hazmat/primitives/kdf/x963kdf.py b/src/cryptography/hazmat/primitives/kdf/x963kdf.py new file mode 100644 index 00000000..9eb50b0f --- /dev/null +++ b/src/cryptography/hazmat/primitives/kdf/x963kdf.py @@ -0,0 +1,68 @@ +# 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 struct + +from cryptography import utils +from cryptography.exceptions import ( + AlreadyFinalized, InvalidKey, UnsupportedAlgorithm, _Reasons +) +from cryptography.hazmat.backends.interfaces import HashBackend +from cryptography.hazmat.primitives import constant_time, hashes +from cryptography.hazmat.primitives.kdf import KeyDerivationFunction + + +def _int_to_u32be(n): + return struct.pack('>I', n) + + +@utils.register_interface(KeyDerivationFunction) +class X963KDF(object): + def __init__(self, algorithm, length, sharedinfo, backend): + + max_len = algorithm.digest_size * (2 ** 32 - 1) + if length > max_len: + raise ValueError( + "Can not derive keys larger than {} bits.".format(max_len)) + if sharedinfo is not None: + utils._check_bytes("sharedinfo", sharedinfo) + + self._algorithm = algorithm + self._length = length + self._sharedinfo = sharedinfo + + if not isinstance(backend, HashBackend): + raise UnsupportedAlgorithm( + "Backend object does not implement HashBackend.", + _Reasons.BACKEND_MISSING_INTERFACE + ) + self._backend = backend + self._used = False + + def derive(self, key_material): + if self._used: + raise AlreadyFinalized + self._used = True + utils._check_byteslike("key_material", key_material) + output = [b""] + outlen = 0 + counter = 1 + + while self._length > outlen: + h = hashes.Hash(self._algorithm, self._backend) + h.update(key_material) + h.update(_int_to_u32be(counter)) + if self._sharedinfo is not None: + h.update(self._sharedinfo) + output.append(h.finalize()) + outlen += len(output[-1]) + counter += 1 + + return b"".join(output)[:self._length] + + def verify(self, key_material, expected_key): + if not constant_time.bytes_eq(self.derive(key_material), expected_key): + raise InvalidKey diff --git a/src/cryptography/hazmat/primitives/keywrap.py b/src/cryptography/hazmat/primitives/keywrap.py new file mode 100644 index 00000000..f55c519c --- /dev/null +++ b/src/cryptography/hazmat/primitives/keywrap.py @@ -0,0 +1,154 @@ +# 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 struct + +from cryptography.hazmat.primitives.ciphers import Cipher +from cryptography.hazmat.primitives.ciphers.algorithms import AES +from cryptography.hazmat.primitives.ciphers.modes import ECB +from cryptography.hazmat.primitives.constant_time import bytes_eq + + +def _wrap_core(wrapping_key, a, r, backend): + # RFC 3394 Key Wrap - 2.2.1 (index method) + encryptor = Cipher(AES(wrapping_key), ECB(), backend).encryptor() + n = len(r) + for j in range(6): + for i in range(n): + # every encryption operation is a discrete 16 byte chunk (because + # AES has a 128-bit block size) and since we're using ECB it is + # safe to reuse the encryptor for the entire operation + b = encryptor.update(a + r[i]) + # pack/unpack are safe as these are always 64-bit chunks + a = struct.pack( + ">Q", struct.unpack(">Q", b[:8])[0] ^ ((n * j) + i + 1) + ) + r[i] = b[-8:] + + assert encryptor.finalize() == b"" + + return a + b"".join(r) + + +def aes_key_wrap(wrapping_key, key_to_wrap, backend): + if len(wrapping_key) not in [16, 24, 32]: + raise ValueError("The wrapping key must be a valid AES key length") + + if len(key_to_wrap) < 16: + raise ValueError("The key to wrap must be at least 16 bytes") + + if len(key_to_wrap) % 8 != 0: + raise ValueError("The key to wrap must be a multiple of 8 bytes") + + a = b"\xa6\xa6\xa6\xa6\xa6\xa6\xa6\xa6" + r = [key_to_wrap[i:i + 8] for i in range(0, len(key_to_wrap), 8)] + return _wrap_core(wrapping_key, a, r, backend) + + +def _unwrap_core(wrapping_key, a, r, backend): + # Implement RFC 3394 Key Unwrap - 2.2.2 (index method) + decryptor = Cipher(AES(wrapping_key), ECB(), backend).decryptor() + n = len(r) + for j in reversed(range(6)): + for i in reversed(range(n)): + # pack/unpack are safe as these are always 64-bit chunks + atr = struct.pack( + ">Q", struct.unpack(">Q", a)[0] ^ ((n * j) + i + 1) + ) + r[i] + # every decryption operation is a discrete 16 byte chunk so + # it is safe to reuse the decryptor for the entire operation + b = decryptor.update(atr) + a = b[:8] + r[i] = b[-8:] + + assert decryptor.finalize() == b"" + return a, r + + +def aes_key_wrap_with_padding(wrapping_key, key_to_wrap, backend): + if len(wrapping_key) not in [16, 24, 32]: + raise ValueError("The wrapping key must be a valid AES key length") + + aiv = b"\xA6\x59\x59\xA6" + struct.pack(">i", len(key_to_wrap)) + # pad the key to wrap if necessary + pad = (8 - (len(key_to_wrap) % 8)) % 8 + key_to_wrap = key_to_wrap + b"\x00" * pad + if len(key_to_wrap) == 8: + # RFC 5649 - 4.1 - exactly 8 octets after padding + encryptor = Cipher(AES(wrapping_key), ECB(), backend).encryptor() + b = encryptor.update(aiv + key_to_wrap) + assert encryptor.finalize() == b"" + return b + else: + r = [key_to_wrap[i:i + 8] for i in range(0, len(key_to_wrap), 8)] + return _wrap_core(wrapping_key, aiv, r, backend) + + +def aes_key_unwrap_with_padding(wrapping_key, wrapped_key, backend): + if len(wrapped_key) < 16: + raise InvalidUnwrap("Must be at least 16 bytes") + + if len(wrapping_key) not in [16, 24, 32]: + raise ValueError("The wrapping key must be a valid AES key length") + + if len(wrapped_key) == 16: + # RFC 5649 - 4.2 - exactly two 64-bit blocks + decryptor = Cipher(AES(wrapping_key), ECB(), backend).decryptor() + b = decryptor.update(wrapped_key) + assert decryptor.finalize() == b"" + a = b[:8] + data = b[8:] + n = 1 + else: + r = [wrapped_key[i:i + 8] for i in range(0, len(wrapped_key), 8)] + encrypted_aiv = r.pop(0) + n = len(r) + a, r = _unwrap_core(wrapping_key, encrypted_aiv, r, backend) + data = b"".join(r) + + # 1) Check that MSB(32,A) = A65959A6. + # 2) Check that 8*(n-1) < LSB(32,A) <= 8*n. If so, let + # MLI = LSB(32,A). + # 3) Let b = (8*n)-MLI, and then check that the rightmost b octets of + # the output data are zero. + (mli,) = struct.unpack(">I", a[4:]) + b = (8 * n) - mli + if ( + not bytes_eq(a[:4], b"\xa6\x59\x59\xa6") or not + 8 * (n - 1) < mli <= 8 * n or ( + b != 0 and not bytes_eq(data[-b:], b"\x00" * b) + ) + ): + raise InvalidUnwrap() + + if b == 0: + return data + else: + return data[:-b] + + +def aes_key_unwrap(wrapping_key, wrapped_key, backend): + if len(wrapped_key) < 24: + raise InvalidUnwrap("Must be at least 24 bytes") + + if len(wrapped_key) % 8 != 0: + raise InvalidUnwrap("The wrapped key must be a multiple of 8 bytes") + + if len(wrapping_key) not in [16, 24, 32]: + raise ValueError("The wrapping key must be a valid AES key length") + + aiv = b"\xa6\xa6\xa6\xa6\xa6\xa6\xa6\xa6" + r = [wrapped_key[i:i + 8] for i in range(0, len(wrapped_key), 8)] + a = r.pop(0) + a, r = _unwrap_core(wrapping_key, a, r, backend) + if not bytes_eq(a, aiv): + raise InvalidUnwrap() + + return b"".join(r) + + +class InvalidUnwrap(Exception): + pass diff --git a/src/cryptography/hazmat/primitives/padding.py b/src/cryptography/hazmat/primitives/padding.py index f6491eb7..170c8021 100644 --- a/src/cryptography/hazmat/primitives/padding.py +++ b/src/cryptography/hazmat/primitives/padding.py @@ -28,14 +28,73 @@ class PaddingContext(object): """ -class PKCS7(object): - def __init__(self, block_size): - if not (0 <= block_size < 256): - raise ValueError("block_size must be in range(0, 256).") +def _byte_padding_check(block_size): + if not (0 <= block_size <= 2040): + raise ValueError("block_size must be in range(0, 2041).") + + if block_size % 8 != 0: + raise ValueError("block_size must be a multiple of 8.") + + +def _byte_padding_update(buffer_, data, block_size): + if buffer_ is None: + raise AlreadyFinalized("Context was already finalized.") + + utils._check_bytes("data", data) + + buffer_ += data + + finished_blocks = len(buffer_) // (block_size // 8) + + result = buffer_[:finished_blocks * (block_size // 8)] + buffer_ = buffer_[finished_blocks * (block_size // 8):] + + return buffer_, result + + +def _byte_padding_pad(buffer_, block_size, paddingfn): + if buffer_ is None: + raise AlreadyFinalized("Context was already finalized.") + + pad_size = block_size // 8 - len(buffer_) + return buffer_ + paddingfn(pad_size) + + +def _byte_unpadding_update(buffer_, data, block_size): + if buffer_ is None: + raise AlreadyFinalized("Context was already finalized.") + + utils._check_bytes("data", data) - if block_size % 8 != 0: - raise ValueError("block_size must be a multiple of 8.") + buffer_ += data + finished_blocks = max(len(buffer_) // (block_size // 8) - 1, 0) + + result = buffer_[:finished_blocks * (block_size // 8)] + buffer_ = buffer_[finished_blocks * (block_size // 8):] + + return buffer_, result + + +def _byte_unpadding_check(buffer_, block_size, checkfn): + if buffer_ is None: + raise AlreadyFinalized("Context was already finalized.") + + if len(buffer_) != block_size // 8: + raise ValueError("Invalid padding bytes.") + + valid = checkfn(buffer_, block_size // 8) + + if not valid: + raise ValueError("Invalid padding bytes.") + + pad_size = six.indexbytes(buffer_, -1) + return buffer_[:-pad_size] + + +class PKCS7(object): + def __init__(self, block_size): + _byte_padding_check(block_size) self.block_size = block_size def padder(self): @@ -53,27 +112,16 @@ class _PKCS7PaddingContext(object): self._buffer = b"" def update(self, data): - if self._buffer is None: - raise AlreadyFinalized("Context was already finalized.") - - if not isinstance(data, bytes): - raise TypeError("data must be bytes.") - - self._buffer += data - - finished_blocks = len(self._buffer) // (self.block_size // 8) - - result = self._buffer[:finished_blocks * (self.block_size // 8)] - self._buffer = self._buffer[finished_blocks * (self.block_size // 8):] - + self._buffer, result = _byte_padding_update( + self._buffer, data, self.block_size) return result - def finalize(self): - if self._buffer is None: - raise AlreadyFinalized("Context was already finalized.") + def _padding(self, size): + return six.int2byte(size) * size - pad_size = self.block_size // 8 - len(self._buffer) - result = self._buffer + six.int2byte(pad_size) * pad_size + def finalize(self): + result = _byte_padding_pad( + self._buffer, self.block_size, self._padding) self._buffer = None return result @@ -86,39 +134,67 @@ class _PKCS7UnpaddingContext(object): self._buffer = b"" def update(self, data): - if self._buffer is None: - raise AlreadyFinalized("Context was already finalized.") + self._buffer, result = _byte_unpadding_update( + self._buffer, data, self.block_size) + return result + + def finalize(self): + result = _byte_unpadding_check( + self._buffer, self.block_size, + lib.Cryptography_check_pkcs7_padding) + self._buffer = None + return result - if not isinstance(data, bytes): - raise TypeError("data must be bytes.") - self._buffer += data +class ANSIX923(object): + def __init__(self, block_size): + _byte_padding_check(block_size) + self.block_size = block_size + + def padder(self): + return _ANSIX923PaddingContext(self.block_size) + + def unpadder(self): + return _ANSIX923UnpaddingContext(self.block_size) - finished_blocks = max( - len(self._buffer) // (self.block_size // 8) - 1, - 0 - ) - result = self._buffer[:finished_blocks * (self.block_size // 8)] - self._buffer = self._buffer[finished_blocks * (self.block_size // 8):] +@utils.register_interface(PaddingContext) +class _ANSIX923PaddingContext(object): + def __init__(self, block_size): + self.block_size = block_size + # TODO: more copies than necessary, we should use zero-buffer (#193) + self._buffer = b"" + def update(self, data): + self._buffer, result = _byte_padding_update( + self._buffer, data, self.block_size) return result + def _padding(self, size): + return six.int2byte(0) * (size - 1) + six.int2byte(size) + def finalize(self): - if self._buffer is None: - raise AlreadyFinalized("Context was already finalized.") + result = _byte_padding_pad( + self._buffer, self.block_size, self._padding) + self._buffer = None + return result - if len(self._buffer) != self.block_size // 8: - raise ValueError("Invalid padding bytes.") - valid = lib.Cryptography_check_pkcs7_padding( - self._buffer, self.block_size // 8 - ) +@utils.register_interface(PaddingContext) +class _ANSIX923UnpaddingContext(object): + def __init__(self, block_size): + self.block_size = block_size + # TODO: more copies than necessary, we should use zero-buffer (#193) + self._buffer = b"" - if not valid: - raise ValueError("Invalid padding bytes.") + def update(self, data): + self._buffer, result = _byte_unpadding_update( + self._buffer, data, self.block_size) + return result - pad_size = six.indexbytes(self._buffer, -1) - res = self._buffer[:-pad_size] + def finalize(self): + result = _byte_unpadding_check( + self._buffer, self.block_size, + lib.Cryptography_check_ansix923_padding) self._buffer = None - return res + return result diff --git a/src/cryptography/hazmat/primitives/poly1305.py b/src/cryptography/hazmat/primitives/poly1305.py new file mode 100644 index 00000000..d92f62ad --- /dev/null +++ b/src/cryptography/hazmat/primitives/poly1305.py @@ -0,0 +1,55 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import absolute_import, division, print_function + + +from cryptography import utils +from cryptography.exceptions import ( + AlreadyFinalized, UnsupportedAlgorithm, _Reasons +) + + +class Poly1305(object): + def __init__(self, key): + from cryptography.hazmat.backends.openssl.backend import backend + if not backend.poly1305_supported(): + raise UnsupportedAlgorithm( + "poly1305 is not supported by this version of OpenSSL.", + _Reasons.UNSUPPORTED_MAC + ) + self._ctx = backend.create_poly1305_ctx(key) + + def update(self, data): + if self._ctx is None: + raise AlreadyFinalized("Context was already finalized.") + utils._check_byteslike("data", data) + self._ctx.update(data) + + def finalize(self): + if self._ctx is None: + raise AlreadyFinalized("Context was already finalized.") + mac = self._ctx.finalize() + self._ctx = None + return mac + + def verify(self, tag): + utils._check_bytes("tag", tag) + if self._ctx is None: + raise AlreadyFinalized("Context was already finalized.") + + ctx, self._ctx = self._ctx, None + ctx.verify(tag) + + @classmethod + def generate_tag(cls, key, data): + p = Poly1305(key) + p.update(data) + return p.finalize() + + @classmethod + def verify_tag(cls, key, data, tag): + p = Poly1305(key) + p.update(data) + p.verify(tag) diff --git a/src/cryptography/hazmat/primitives/serialization.py b/src/cryptography/hazmat/primitives/serialization.py deleted file mode 100644 index 8699fa91..00000000 --- a/src/cryptography/hazmat/primitives/serialization.py +++ /dev/null @@ -1,203 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import absolute_import, division, print_function - -import abc -import base64 -import struct -from enum import Enum - -import six - -from cryptography import utils -from cryptography.exceptions import UnsupportedAlgorithm -from cryptography.hazmat.primitives.asymmetric import dsa, ec, rsa - - -def load_pem_private_key(data, password, backend): - return backend.load_pem_private_key(data, password) - - -def load_pem_public_key(data, backend): - return backend.load_pem_public_key(data) - - -def load_der_private_key(data, password, backend): - return backend.load_der_private_key(data, password) - - -def load_der_public_key(data, backend): - return backend.load_der_public_key(data) - - -def load_ssh_public_key(data, backend): - key_parts = data.split(b' ') - - if len(key_parts) != 2 and len(key_parts) != 3: - raise ValueError( - 'Key is not in the proper format or contains extra data.') - - key_type = key_parts[0] - - if key_type == b'ssh-rsa': - loader = _load_ssh_rsa_public_key - elif key_type == b'ssh-dss': - loader = _load_ssh_dss_public_key - elif key_type in [ - b'ecdsa-sha2-nistp256', b'ecdsa-sha2-nistp384', b'ecdsa-sha2-nistp521', - ]: - loader = _load_ssh_ecdsa_public_key - else: - raise UnsupportedAlgorithm('Key type is not supported.') - - key_body = key_parts[1] - - try: - decoded_data = base64.b64decode(key_body) - except TypeError: - raise ValueError('Key is not in the proper format.') - - inner_key_type, rest = _read_next_string(decoded_data) - - if inner_key_type != key_type: - raise ValueError( - 'Key header and key body contain different key type values.' - ) - - return loader(key_type, rest, backend) - - -def _load_ssh_rsa_public_key(key_type, decoded_data, backend): - e, rest = _read_next_mpint(decoded_data) - n, rest = _read_next_mpint(rest) - - if rest: - raise ValueError('Key body contains extra bytes.') - - return rsa.RSAPublicNumbers(e, n).public_key(backend) - - -def _load_ssh_dss_public_key(key_type, decoded_data, backend): - p, rest = _read_next_mpint(decoded_data) - q, rest = _read_next_mpint(rest) - g, rest = _read_next_mpint(rest) - y, rest = _read_next_mpint(rest) - - if rest: - raise ValueError('Key body contains extra bytes.') - - parameter_numbers = dsa.DSAParameterNumbers(p, q, g) - public_numbers = dsa.DSAPublicNumbers(y, parameter_numbers) - - return public_numbers.public_key(backend) - - -def _load_ssh_ecdsa_public_key(expected_key_type, decoded_data, backend): - curve_name, rest = _read_next_string(decoded_data) - data, rest = _read_next_string(rest) - - if expected_key_type != b"ecdsa-sha2-" + curve_name: - raise ValueError( - 'Key header and key body contain different key type values.' - ) - - if rest: - raise ValueError('Key body contains extra bytes.') - - if curve_name == b"nistp256": - curve = ec.SECP256R1() - elif curve_name == b"nistp384": - curve = ec.SECP384R1() - elif curve_name == b"nistp521": - curve = ec.SECP521R1() - - if six.indexbytes(data, 0) != 4: - raise NotImplementedError( - "Compressed elliptic curve points are not supported" - ) - - # key_size is in bits, and sometimes it's not evenly divisible by 8, so we - # add 7 to round up the number of bytes. - if len(data) != 1 + 2 * ((curve.key_size + 7) // 8): - raise ValueError("Malformed key bytes") - - x = _int_from_bytes(data[1:1 + (curve.key_size + 7) // 8], byteorder='big') - y = _int_from_bytes(data[1 + (curve.key_size + 7) // 8:], byteorder='big') - return ec.EllipticCurvePublicNumbers(x, y, curve).public_key(backend) - - -def _read_next_string(data): - """ - Retrieves the next RFC 4251 string value from the data. - - While the RFC calls these strings, in Python they are bytes objects. - """ - str_len, = struct.unpack('>I', data[:4]) - return data[4:4 + str_len], data[4 + str_len:] - - -def _read_next_mpint(data): - """ - Reads the next mpint from the data. - - Currently, all mpints are interpreted as unsigned. - """ - mpint_data, rest = _read_next_string(data) - - return _int_from_bytes(mpint_data, byteorder='big', signed=False), rest - - -if hasattr(int, "from_bytes"): - _int_from_bytes = int.from_bytes -else: - def _int_from_bytes(data, byteorder, signed=False): - assert byteorder == 'big' - assert not signed - - if len(data) % 4 != 0: - data = (b'\x00' * (4 - (len(data) % 4))) + data - - result = 0 - - while len(data) > 0: - digit, = struct.unpack('>I', data[:4]) - result = (result << 32) + digit - data = data[4:] - - return result - - -class Encoding(Enum): - PEM = "PEM" - DER = "DER" - - -class PrivateFormat(Enum): - PKCS8 = "PKCS8" - TraditionalOpenSSL = "TraditionalOpenSSL" - - -class PublicFormat(Enum): - SubjectPublicKeyInfo = "X.509 subjectPublicKeyInfo with PKCS#1" - PKCS1 = "Raw PKCS#1" - - -@six.add_metaclass(abc.ABCMeta) -class KeySerializationEncryption(object): - pass - - -@utils.register_interface(KeySerializationEncryption) -class BestAvailableEncryption(object): - def __init__(self, password): - if not isinstance(password, bytes) or len(password) == 0: - raise ValueError("Password must be 1 or more bytes.") - - self.password = password - - -@utils.register_interface(KeySerializationEncryption) -class NoEncryption(object): - pass diff --git a/src/cryptography/hazmat/primitives/serialization/__init__.py b/src/cryptography/hazmat/primitives/serialization/__init__.py new file mode 100644 index 00000000..b910751b --- /dev/null +++ b/src/cryptography/hazmat/primitives/serialization/__init__.py @@ -0,0 +1,24 @@ +# 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.primitives.serialization.base import ( + BestAvailableEncryption, Encoding, KeySerializationEncryption, + NoEncryption, ParameterFormat, PrivateFormat, PublicFormat, + load_der_parameters, load_der_private_key, load_der_public_key, + load_pem_parameters, load_pem_private_key, load_pem_public_key, +) +from cryptography.hazmat.primitives.serialization.ssh import ( + load_ssh_public_key +) + + +__all__ = [ + "load_der_parameters", "load_der_private_key", "load_der_public_key", + "load_pem_parameters", "load_pem_private_key", "load_pem_public_key", + "load_ssh_public_key", "Encoding", "PrivateFormat", "PublicFormat", + "ParameterFormat", "KeySerializationEncryption", "BestAvailableEncryption", + "NoEncryption", +] diff --git a/src/cryptography/hazmat/primitives/serialization/base.py b/src/cryptography/hazmat/primitives/serialization/base.py new file mode 100644 index 00000000..4218ea82 --- /dev/null +++ b/src/cryptography/hazmat/primitives/serialization/base.py @@ -0,0 +1,82 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import absolute_import, division, print_function + +import abc +from enum import Enum + +import six + +from cryptography import utils + + +def load_pem_private_key(data, password, backend): + return backend.load_pem_private_key(data, password) + + +def load_pem_public_key(data, backend): + return backend.load_pem_public_key(data) + + +def load_pem_parameters(data, backend): + return backend.load_pem_parameters(data) + + +def load_der_private_key(data, password, backend): + return backend.load_der_private_key(data, password) + + +def load_der_public_key(data, backend): + return backend.load_der_public_key(data) + + +def load_der_parameters(data, backend): + return backend.load_der_parameters(data) + + +class Encoding(Enum): + PEM = "PEM" + DER = "DER" + OpenSSH = "OpenSSH" + Raw = "Raw" + X962 = "ANSI X9.62" + + +class PrivateFormat(Enum): + PKCS8 = "PKCS8" + TraditionalOpenSSL = "TraditionalOpenSSL" + Raw = "Raw" + + +class PublicFormat(Enum): + SubjectPublicKeyInfo = "X.509 subjectPublicKeyInfo with PKCS#1" + PKCS1 = "Raw PKCS#1" + OpenSSH = "OpenSSH" + Raw = "Raw" + CompressedPoint = "X9.62 Compressed Point" + UncompressedPoint = "X9.62 Uncompressed Point" + + +class ParameterFormat(Enum): + PKCS3 = "PKCS3" + + +@six.add_metaclass(abc.ABCMeta) +class KeySerializationEncryption(object): + pass + + +@utils.register_interface(KeySerializationEncryption) +class BestAvailableEncryption(object): + def __init__(self, password): + if not isinstance(password, bytes) or len(password) == 0: + raise ValueError("Password must be 1 or more bytes.") + + self.password = password + + +@utils.register_interface(KeySerializationEncryption) +class NoEncryption(object): + pass diff --git a/src/cryptography/hazmat/backends/commoncrypto/__init__.py b/src/cryptography/hazmat/primitives/serialization/pkcs12.py index 1d52a255..98161d57 100644 --- a/src/cryptography/hazmat/backends/commoncrypto/__init__.py +++ b/src/cryptography/hazmat/primitives/serialization/pkcs12.py @@ -4,7 +4,6 @@ from __future__ import absolute_import, division, print_function -from cryptography.hazmat.backends.commoncrypto.backend import backend - -__all__ = ["backend"] +def load_key_and_certificates(data, password, backend): + return backend.load_key_and_certificates_from_pkcs12(data, password) diff --git a/src/cryptography/hazmat/primitives/serialization/ssh.py b/src/cryptography/hazmat/primitives/serialization/ssh.py new file mode 100644 index 00000000..a1d6c8c9 --- /dev/null +++ b/src/cryptography/hazmat/primitives/serialization/ssh.py @@ -0,0 +1,153 @@ +# 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 base64 +import struct + +import six + +from cryptography import utils +from cryptography.exceptions import UnsupportedAlgorithm +from cryptography.hazmat.primitives.asymmetric import dsa, ec, ed25519, rsa + + +def load_ssh_public_key(data, backend): + key_parts = data.split(b' ', 2) + + if len(key_parts) < 2: + raise ValueError( + 'Key is not in the proper format or contains extra data.') + + key_type = key_parts[0] + + if key_type == b'ssh-rsa': + loader = _load_ssh_rsa_public_key + elif key_type == b'ssh-dss': + loader = _load_ssh_dss_public_key + elif key_type in [ + b'ecdsa-sha2-nistp256', b'ecdsa-sha2-nistp384', b'ecdsa-sha2-nistp521', + ]: + loader = _load_ssh_ecdsa_public_key + elif key_type == b'ssh-ed25519': + loader = _load_ssh_ed25519_public_key + else: + raise UnsupportedAlgorithm('Key type is not supported.') + + key_body = key_parts[1] + + try: + decoded_data = base64.b64decode(key_body) + except TypeError: + raise ValueError('Key is not in the proper format.') + + inner_key_type, rest = _ssh_read_next_string(decoded_data) + + if inner_key_type != key_type: + raise ValueError( + 'Key header and key body contain different key type values.' + ) + + return loader(key_type, rest, backend) + + +def _load_ssh_rsa_public_key(key_type, decoded_data, backend): + e, rest = _ssh_read_next_mpint(decoded_data) + n, rest = _ssh_read_next_mpint(rest) + + if rest: + raise ValueError('Key body contains extra bytes.') + + return rsa.RSAPublicNumbers(e, n).public_key(backend) + + +def _load_ssh_dss_public_key(key_type, decoded_data, backend): + p, rest = _ssh_read_next_mpint(decoded_data) + q, rest = _ssh_read_next_mpint(rest) + g, rest = _ssh_read_next_mpint(rest) + y, rest = _ssh_read_next_mpint(rest) + + if rest: + raise ValueError('Key body contains extra bytes.') + + parameter_numbers = dsa.DSAParameterNumbers(p, q, g) + public_numbers = dsa.DSAPublicNumbers(y, parameter_numbers) + + return public_numbers.public_key(backend) + + +def _load_ssh_ecdsa_public_key(expected_key_type, decoded_data, backend): + curve_name, rest = _ssh_read_next_string(decoded_data) + data, rest = _ssh_read_next_string(rest) + + if expected_key_type != b"ecdsa-sha2-" + curve_name: + raise ValueError( + 'Key header and key body contain different key type values.' + ) + + if rest: + raise ValueError('Key body contains extra bytes.') + + curve = { + b"nistp256": ec.SECP256R1, + b"nistp384": ec.SECP384R1, + b"nistp521": ec.SECP521R1, + }[curve_name]() + + if six.indexbytes(data, 0) != 4: + raise NotImplementedError( + "Compressed elliptic curve points are not supported" + ) + + return ec.EllipticCurvePublicKey.from_encoded_point(curve, data) + + +def _load_ssh_ed25519_public_key(expected_key_type, decoded_data, backend): + data, rest = _ssh_read_next_string(decoded_data) + + if rest: + raise ValueError('Key body contains extra bytes.') + + return ed25519.Ed25519PublicKey.from_public_bytes(data) + + +def _ssh_read_next_string(data): + """ + Retrieves the next RFC 4251 string value from the data. + + While the RFC calls these strings, in Python they are bytes objects. + """ + if len(data) < 4: + raise ValueError("Key is not in the proper format") + + str_len, = struct.unpack('>I', data[:4]) + if len(data) < str_len + 4: + raise ValueError("Key is not in the proper format") + + return data[4:4 + str_len], data[4 + str_len:] + + +def _ssh_read_next_mpint(data): + """ + Reads the next mpint from the data. + + Currently, all mpints are interpreted as unsigned. + """ + mpint_data, rest = _ssh_read_next_string(data) + + return ( + utils.int_from_bytes(mpint_data, byteorder='big', signed=False), rest + ) + + +def _ssh_write_string(data): + return struct.pack(">I", len(data)) + data + + +def _ssh_write_mpint(value): + data = utils.int_to_bytes(value) + if six.indexbytes(data, 0) & 0x80: + data = b"\x00" + data + return _ssh_write_string(data) diff --git a/src/cryptography/hazmat/primitives/twofactor/hotp.py b/src/cryptography/hazmat/primitives/twofactor/hotp.py index 12bc7661..4ad1bdc2 100644 --- a/src/cryptography/hazmat/primitives/twofactor/hotp.py +++ b/src/cryptography/hazmat/primitives/twofactor/hotp.py @@ -19,14 +19,15 @@ from cryptography.hazmat.primitives.twofactor.utils import _generate_uri class HOTP(object): - def __init__(self, key, length, algorithm, backend): + def __init__(self, key, length, algorithm, backend, + enforce_key_length=True): if not isinstance(backend, HMACBackend): raise UnsupportedAlgorithm( "Backend object does not implement HMACBackend.", _Reasons.BACKEND_MISSING_INTERFACE ) - if len(key) < 16: + if len(key) < 16 and enforce_key_length is True: raise ValueError("Key length has to be at least 128 bits.") if not isinstance(length, six.integer_types): diff --git a/src/cryptography/hazmat/primitives/twofactor/totp.py b/src/cryptography/hazmat/primitives/twofactor/totp.py index 60705901..499f2824 100644 --- a/src/cryptography/hazmat/primitives/twofactor/totp.py +++ b/src/cryptography/hazmat/primitives/twofactor/totp.py @@ -15,7 +15,8 @@ from cryptography.hazmat.primitives.twofactor.utils import _generate_uri class TOTP(object): - def __init__(self, key, length, algorithm, time_step, backend): + def __init__(self, key, length, algorithm, time_step, backend, + enforce_key_length=True): if not isinstance(backend, HMACBackend): raise UnsupportedAlgorithm( "Backend object does not implement HMACBackend.", @@ -23,7 +24,7 @@ class TOTP(object): ) self._time_step = time_step - self._hotp = HOTP(key, length, algorithm, backend) + self._hotp = HOTP(key, length, algorithm, backend, enforce_key_length) def generate(self, time): counter = int(time / self._time_step) diff --git a/src/cryptography/utils.py b/src/cryptography/utils.py index 0bf8c0ea..ff4f81d2 100644 --- a/src/cryptography/utils.py +++ b/src/cryptography/utils.py @@ -5,12 +5,35 @@ from __future__ import absolute_import, division, print_function import abc +import binascii import inspect import sys import warnings -DeprecatedIn09 = DeprecationWarning +# We use a UserWarning subclass, instead of DeprecationWarning, because CPython +# decided deprecation warnings should be invisble by default. +class CryptographyDeprecationWarning(UserWarning): + pass + + +# Several APIs were deprecated with no specific end-of-life date because of the +# ubiquity of their use. They should not be removed until we agree on when that +# cycle ends. +PersistentlyDeprecated2017 = CryptographyDeprecationWarning +PersistentlyDeprecated2019 = CryptographyDeprecationWarning + + +def _check_bytes(name, value): + if not isinstance(value, bytes): + raise TypeError("{} must be bytes".format(name)) + + +def _check_byteslike(name, value): + try: + memoryview(value) + except TypeError: + raise TypeError("{} must be bytes-like".format(name)) def read_only_property(name): @@ -25,36 +48,74 @@ def register_interface(iface): return register_decorator +def register_interface_if(predicate, iface): + def register_decorator(klass): + if predicate: + verify_interface(iface, klass) + iface.register(klass) + return klass + return register_decorator + + +if hasattr(int, "from_bytes"): + int_from_bytes = int.from_bytes +else: + def int_from_bytes(data, byteorder, signed=False): + assert byteorder == 'big' + assert not signed + + return int(binascii.hexlify(data), 16) + + +if hasattr(int, "to_bytes"): + def int_to_bytes(integer, length=None): + return integer.to_bytes( + length or (integer.bit_length() + 7) // 8 or 1, 'big' + ) +else: + def int_to_bytes(integer, length=None): + hex_string = '%x' % integer + if length is None: + n = len(hex_string) + else: + n = length * 2 + return binascii.unhexlify(hex_string.zfill(n + (n & 1))) + + class InterfaceNotImplemented(Exception): pass +if hasattr(inspect, "signature"): + signature = inspect.signature +else: + signature = inspect.getargspec + + def verify_interface(iface, klass): for method in iface.__abstractmethods__: if not hasattr(klass, method): raise InterfaceNotImplemented( - "{0} is missing a {1!r} method".format(klass, method) + "{} is missing a {!r} method".format(klass, method) ) if isinstance(getattr(iface, method), abc.abstractproperty): # Can't properly verify these yet. continue - spec = inspect.getargspec(getattr(iface, method)) - actual = inspect.getargspec(getattr(klass, method)) - if spec != actual: + sig = signature(getattr(iface, method)) + actual = signature(getattr(klass, method)) + if sig != actual: raise InterfaceNotImplemented( - "{0}.{1}'s signature differs from the expected. Expected: " - "{2!r}. Received: {3!r}".format( - klass, method, spec, actual + "{}.{}'s signature differs from the expected. Expected: " + "{!r}. Received: {!r}".format( + klass, method, sig, actual ) ) -if sys.version_info >= (2, 7): - def bit_length(x): - return x.bit_length() -else: - def bit_length(x): - return len(bin(x)) - (2 + (x <= 0)) +# No longer needed as of 2.2, but retained because we have external consumers +# who use it. +def bit_length(x): + return x.bit_length() class _DeprecatedValue(object): @@ -78,6 +139,13 @@ class _ModuleWithDeprecations(object): def __setattr__(self, attr, value): setattr(self._module, attr, value) + def __delattr__(self, attr): + obj = getattr(self._module, attr) + if isinstance(obj, _DeprecatedValue): + warnings.warn(obj.message, obj.warning_class, stacklevel=2) + + delattr(self._module, attr) + def __dir__(self): return ["_module"] + dir(self._module) @@ -85,5 +153,19 @@ class _ModuleWithDeprecations(object): def deprecated(value, module_name, message, warning_class): module = sys.modules[module_name] if not isinstance(module, _ModuleWithDeprecations): - sys.modules[module_name] = module = _ModuleWithDeprecations(module) + sys.modules[module_name] = _ModuleWithDeprecations(module) return _DeprecatedValue(value, message, warning_class) + + +def cached_property(func): + cached_name = "_cached_{}".format(func) + sentinel = object() + + def inner(instance): + cache = getattr(instance, cached_name, sentinel) + if cache is not sentinel: + return cache + result = func(instance) + setattr(instance, cached_name, result) + return result + return property(inner) diff --git a/src/cryptography/x509.py b/src/cryptography/x509.py deleted file mode 100644 index 0f72abb3..00000000 --- a/src/cryptography/x509.py +++ /dev/null @@ -1,1489 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import absolute_import, division, print_function - -import abc -import ipaddress -from enum import Enum - -import six - -from cryptography import utils -from cryptography.hazmat.primitives import hashes - - -_OID_NAMES = { - "2.5.4.3": "commonName", - "2.5.4.6": "countryName", - "2.5.4.7": "localityName", - "2.5.4.8": "stateOrProvinceName", - "2.5.4.10": "organizationName", - "2.5.4.11": "organizationalUnitName", - "2.5.4.5": "serialNumber", - "2.5.4.4": "surname", - "2.5.4.42": "givenName", - "2.5.4.12": "title", - "2.5.4.44": "generationQualifier", - "2.5.4.46": "dnQualifier", - "2.5.4.65": "pseudonym", - "0.9.2342.19200300.100.1.25": "domainComponent", - "1.2.840.113549.1.9.1": "emailAddress", - "1.2.840.113549.1.1.4": "md5WithRSAEncryption", - "1.2.840.113549.1.1.5": "sha1WithRSAEncryption", - "1.2.840.113549.1.1.14": "sha224WithRSAEncryption", - "1.2.840.113549.1.1.11": "sha256WithRSAEncryption", - "1.2.840.113549.1.1.12": "sha384WithRSAEncryption", - "1.2.840.113549.1.1.13": "sha512WithRSAEncryption", - "1.2.840.10045.4.1": "ecdsa-with-SHA1", - "1.2.840.10045.4.3.1": "ecdsa-with-SHA224", - "1.2.840.10045.4.3.2": "ecdsa-with-SHA256", - "1.2.840.10045.4.3.3": "ecdsa-with-SHA384", - "1.2.840.10045.4.3.4": "ecdsa-with-SHA512", - "1.2.840.10040.4.3": "dsa-with-sha1", - "2.16.840.1.101.3.4.3.1": "dsa-with-sha224", - "2.16.840.1.101.3.4.3.2": "dsa-with-sha256", - "1.3.6.1.5.5.7.3.1": "serverAuth", - "1.3.6.1.5.5.7.3.2": "clientAuth", - "1.3.6.1.5.5.7.3.3": "codeSigning", - "1.3.6.1.5.5.7.3.4": "emailProtection", - "1.3.6.1.5.5.7.3.8": "timeStamping", - "1.3.6.1.5.5.7.3.9": "OCSPSigning", - "2.5.29.9": "subjectDirectoryAttributes", - "2.5.29.14": "subjectKeyIdentifier", - "2.5.29.15": "keyUsage", - "2.5.29.17": "subjectAltName", - "2.5.29.18": "issuerAltName", - "2.5.29.19": "basicConstraints", - "2.5.29.21": "cRLReason", - "2.5.29.24": "invalidityDate", - "2.5.29.29": "certificateIssuer", - "2.5.29.30": "nameConstraints", - "2.5.29.31": "cRLDistributionPoints", - "2.5.29.32": "certificatePolicies", - "2.5.29.33": "policyMappings", - "2.5.29.35": "authorityKeyIdentifier", - "2.5.29.36": "policyConstraints", - "2.5.29.37": "extendedKeyUsage", - "2.5.29.46": "freshestCRL", - "2.5.29.54": "inhibitAnyPolicy", - "1.3.6.1.5.5.7.1.1": "authorityInfoAccess", - "1.3.6.1.5.5.7.1.11": "subjectInfoAccess", - "1.3.6.1.5.5.7.48.1.5": "OCSPNoCheck", - "1.3.6.1.5.5.7.48.1": "OCSP", - "1.3.6.1.5.5.7.48.2": "caIssuers", - "1.3.6.1.5.5.7.2.1": "id-qt-cps", - "1.3.6.1.5.5.7.2.2": "id-qt-unotice", -} - - -_GENERAL_NAMES = { - 0: "otherName", - 1: "rfc822Name", - 2: "dNSName", - 3: "x400Address", - 4: "directoryName", - 5: "ediPartyName", - 6: "uniformResourceIdentifier", - 7: "iPAddress", - 8: "registeredID", -} - - -class Version(Enum): - v1 = 0 - v3 = 2 - - -def load_pem_x509_certificate(data, backend): - return backend.load_pem_x509_certificate(data) - - -def load_der_x509_certificate(data, backend): - return backend.load_der_x509_certificate(data) - - -def load_pem_x509_csr(data, backend): - return backend.load_pem_x509_csr(data) - - -def load_der_x509_csr(data, backend): - return backend.load_der_x509_csr(data) - - -class InvalidVersion(Exception): - def __init__(self, msg, parsed_version): - super(InvalidVersion, self).__init__(msg) - self.parsed_version = parsed_version - - -class DuplicateExtension(Exception): - def __init__(self, msg, oid): - super(DuplicateExtension, self).__init__(msg) - self.oid = oid - - -class UnsupportedExtension(Exception): - def __init__(self, msg, oid): - super(UnsupportedExtension, self).__init__(msg) - self.oid = oid - - -class ExtensionNotFound(Exception): - def __init__(self, msg, oid): - super(ExtensionNotFound, self).__init__(msg) - self.oid = oid - - -class UnsupportedGeneralNameType(Exception): - def __init__(self, msg, type): - super(UnsupportedGeneralNameType, self).__init__(msg) - self.type = type - - -class NameAttribute(object): - def __init__(self, oid, value): - if not isinstance(oid, ObjectIdentifier): - raise TypeError( - "oid argument must be an ObjectIdentifier instance." - ) - - if not isinstance(value, six.text_type): - raise TypeError( - "value argument must be a text type." - ) - - self._oid = oid - self._value = value - - oid = utils.read_only_property("_oid") - value = utils.read_only_property("_value") - - def __eq__(self, other): - if not isinstance(other, NameAttribute): - return NotImplemented - - return ( - self.oid == other.oid and - self.value == other.value - ) - - def __ne__(self, other): - return not self == other - - def __repr__(self): - return "<NameAttribute(oid={0.oid}, value={0.value!r})>".format(self) - - -class ObjectIdentifier(object): - def __init__(self, dotted_string): - self._dotted_string = 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, - _OID_NAMES.get(self._dotted_string, "Unknown OID") - ) - - def __hash__(self): - return hash(self.dotted_string) - - dotted_string = utils.read_only_property("_dotted_string") - - -class Name(object): - def __init__(self, attributes): - self._attributes = attributes - - def get_attributes_for_oid(self, oid): - return [i for i in self if i.oid == oid] - - def __eq__(self, other): - if not isinstance(other, Name): - return NotImplemented - - return self._attributes == other._attributes - - def __ne__(self, other): - return not self == other - - def __iter__(self): - return iter(self._attributes) - - def __len__(self): - return len(self._attributes) - - def __repr__(self): - return "<Name({0!r})>".format(self._attributes) - - -OID_SUBJECT_DIRECTORY_ATTRIBUTES = ObjectIdentifier("2.5.29.9") -OID_SUBJECT_KEY_IDENTIFIER = ObjectIdentifier("2.5.29.14") -OID_KEY_USAGE = ObjectIdentifier("2.5.29.15") -OID_SUBJECT_ALTERNATIVE_NAME = ObjectIdentifier("2.5.29.17") -OID_ISSUER_ALTERNATIVE_NAME = ObjectIdentifier("2.5.29.18") -OID_BASIC_CONSTRAINTS = ObjectIdentifier("2.5.29.19") -OID_CRL_REASON = ObjectIdentifier("2.5.29.21") -OID_INVALIDITY_DATE = ObjectIdentifier("2.5.29.24") -OID_CERTIFICATE_ISSUER = ObjectIdentifier("2.5.29.29") -OID_NAME_CONSTRAINTS = ObjectIdentifier("2.5.29.30") -OID_CRL_DISTRIBUTION_POINTS = ObjectIdentifier("2.5.29.31") -OID_CERTIFICATE_POLICIES = ObjectIdentifier("2.5.29.32") -OID_POLICY_MAPPINGS = ObjectIdentifier("2.5.29.33") -OID_AUTHORITY_KEY_IDENTIFIER = ObjectIdentifier("2.5.29.35") -OID_POLICY_CONSTRAINTS = ObjectIdentifier("2.5.29.36") -OID_EXTENDED_KEY_USAGE = ObjectIdentifier("2.5.29.37") -OID_FRESHEST_CRL = ObjectIdentifier("2.5.29.46") -OID_INHIBIT_ANY_POLICY = ObjectIdentifier("2.5.29.54") -OID_AUTHORITY_INFORMATION_ACCESS = ObjectIdentifier("1.3.6.1.5.5.7.1.1") -OID_SUBJECT_INFORMATION_ACCESS = ObjectIdentifier("1.3.6.1.5.5.7.1.11") -OID_OCSP_NO_CHECK = ObjectIdentifier("1.3.6.1.5.5.7.48.1.5") - - -class Extensions(object): - def __init__(self, extensions): - self._extensions = extensions - - def get_extension_for_oid(self, oid): - for ext in self: - if ext.oid == oid: - return ext - - raise ExtensionNotFound("No {0} extension was found".format(oid), oid) - - def __iter__(self): - return iter(self._extensions) - - def __len__(self): - return len(self._extensions) - - -class Extension(object): - def __init__(self, oid, critical, value): - if not isinstance(oid, ObjectIdentifier): - raise TypeError( - "oid argument must be an ObjectIdentifier instance." - ) - - if not isinstance(critical, bool): - raise TypeError("critical must be a boolean value") - - self._oid = oid - self._critical = critical - self._value = value - - oid = utils.read_only_property("_oid") - critical = utils.read_only_property("_critical") - value = utils.read_only_property("_value") - - def __repr__(self): - return ("<Extension(oid={0.oid}, critical={0.critical}, " - "value={0.value})>").format(self) - - def __eq__(self, other): - if not isinstance(other, Extension): - return NotImplemented - - return ( - self.oid == other.oid and - self.critical == other.critical and - self.value == other.value - ) - - def __ne__(self, other): - return not self == other - - -class ExtendedKeyUsage(object): - def __init__(self, usages): - if not all(isinstance(x, ObjectIdentifier) for x in usages): - raise TypeError( - "Every item in the usages list must be an ObjectIdentifier" - ) - - self._usages = usages - - def __iter__(self): - return iter(self._usages) - - def __len__(self): - return len(self._usages) - - def __repr__(self): - return "<ExtendedKeyUsage({0})>".format(self._usages) - - def __eq__(self, other): - if not isinstance(other, ExtendedKeyUsage): - return NotImplemented - - return self._usages == other._usages - - def __ne__(self, other): - return not self == other - - -class OCSPNoCheck(object): - pass - - -class BasicConstraints(object): - def __init__(self, ca, path_length): - if not isinstance(ca, bool): - raise TypeError("ca must be a boolean value") - - if path_length is not None and not ca: - raise ValueError("path_length must be None when ca is False") - - if ( - path_length is not None and - (not isinstance(path_length, six.integer_types) or path_length < 0) - ): - raise TypeError( - "path_length must be a non-negative integer or None" - ) - - self._ca = ca - self._path_length = path_length - - ca = utils.read_only_property("_ca") - path_length = utils.read_only_property("_path_length") - - def __repr__(self): - return ("<BasicConstraints(ca={0.ca}, " - "path_length={0.path_length})>").format(self) - - def __eq__(self, other): - if not isinstance(other, BasicConstraints): - return NotImplemented - - return self.ca == other.ca and self.path_length == other.path_length - - def __ne__(self, other): - return not self == other - - -class KeyUsage(object): - def __init__(self, digital_signature, content_commitment, key_encipherment, - data_encipherment, key_agreement, key_cert_sign, crl_sign, - encipher_only, decipher_only): - if not key_agreement and (encipher_only or decipher_only): - raise ValueError( - "encipher_only and decipher_only can only be true when " - "key_agreement is true" - ) - - self._digital_signature = digital_signature - self._content_commitment = content_commitment - self._key_encipherment = key_encipherment - self._data_encipherment = data_encipherment - self._key_agreement = key_agreement - self._key_cert_sign = key_cert_sign - self._crl_sign = crl_sign - self._encipher_only = encipher_only - self._decipher_only = decipher_only - - digital_signature = utils.read_only_property("_digital_signature") - content_commitment = utils.read_only_property("_content_commitment") - key_encipherment = utils.read_only_property("_key_encipherment") - data_encipherment = utils.read_only_property("_data_encipherment") - key_agreement = utils.read_only_property("_key_agreement") - key_cert_sign = utils.read_only_property("_key_cert_sign") - crl_sign = utils.read_only_property("_crl_sign") - - @property - def encipher_only(self): - if not self.key_agreement: - raise ValueError( - "encipher_only is undefined unless key_agreement is true" - ) - else: - return self._encipher_only - - @property - def decipher_only(self): - if not self.key_agreement: - raise ValueError( - "decipher_only is undefined unless key_agreement is true" - ) - else: - return self._decipher_only - - def __repr__(self): - try: - encipher_only = self.encipher_only - decipher_only = self.decipher_only - except ValueError: - encipher_only = None - decipher_only = None - - return ("<KeyUsage(digital_signature={0.digital_signature}, " - "content_commitment={0.content_commitment}, " - "key_encipherment={0.key_encipherment}, " - "data_encipherment={0.data_encipherment}, " - "key_agreement={0.key_agreement}, " - "key_cert_sign={0.key_cert_sign}, crl_sign={0.crl_sign}, " - "encipher_only={1}, decipher_only={2})>").format( - self, encipher_only, decipher_only) - - def __eq__(self, other): - if not isinstance(other, KeyUsage): - return NotImplemented - - return ( - self.digital_signature == other.digital_signature and - self.content_commitment == other.content_commitment and - self.key_encipherment == other.key_encipherment and - self.data_encipherment == other.data_encipherment and - self.key_agreement == other.key_agreement and - self.key_cert_sign == other.key_cert_sign and - self.crl_sign == other.crl_sign and - self._encipher_only == other._encipher_only and - self._decipher_only == other._decipher_only - ) - - def __ne__(self, other): - return not self == other - - -class AuthorityInformationAccess(object): - def __init__(self, descriptions): - if not all(isinstance(x, AccessDescription) for x in descriptions): - raise TypeError( - "Every item in the descriptions list must be an " - "AccessDescription" - ) - - self._descriptions = descriptions - - def __iter__(self): - return iter(self._descriptions) - - def __len__(self): - return len(self._descriptions) - - def __repr__(self): - return "<AuthorityInformationAccess({0})>".format(self._descriptions) - - def __eq__(self, other): - if not isinstance(other, AuthorityInformationAccess): - return NotImplemented - - return self._descriptions == other._descriptions - - def __ne__(self, other): - return not self == other - - -class AccessDescription(object): - def __init__(self, access_method, access_location): - if not (access_method == OID_OCSP or access_method == OID_CA_ISSUERS): - raise ValueError( - "access_method must be OID_OCSP or OID_CA_ISSUERS" - ) - - if not isinstance(access_location, GeneralName): - raise TypeError("access_location must be a GeneralName") - - self._access_method = access_method - self._access_location = access_location - - def __repr__(self): - return ( - "<AccessDescription(access_method={0.access_method}, access_locati" - "on={0.access_location})>".format(self) - ) - - def __eq__(self, other): - if not isinstance(other, AccessDescription): - return NotImplemented - - return ( - self.access_method == other.access_method and - self.access_location == other.access_location - ) - - def __ne__(self, other): - return not self == other - - access_method = utils.read_only_property("_access_method") - access_location = utils.read_only_property("_access_location") - - -class CertificatePolicies(object): - def __init__(self, policies): - if not all(isinstance(x, PolicyInformation) for x in policies): - raise TypeError( - "Every item in the policies list must be a " - "PolicyInformation" - ) - - self._policies = policies - - def __iter__(self): - return iter(self._policies) - - def __len__(self): - return len(self._policies) - - def __repr__(self): - return "<CertificatePolicies({0})>".format(self._policies) - - def __eq__(self, other): - if not isinstance(other, CertificatePolicies): - return NotImplemented - - return self._policies == other._policies - - def __ne__(self, other): - return not self == other - - -class PolicyInformation(object): - def __init__(self, policy_identifier, policy_qualifiers): - if not isinstance(policy_identifier, ObjectIdentifier): - raise TypeError("policy_identifier must be an ObjectIdentifier") - - self._policy_identifier = policy_identifier - if policy_qualifiers and not all( - isinstance( - x, (six.text_type, UserNotice) - ) for x in policy_qualifiers - ): - raise TypeError( - "policy_qualifiers must be a list of strings and/or UserNotice" - " objects or None" - ) - - self._policy_qualifiers = policy_qualifiers - - def __repr__(self): - return ( - "<PolicyInformation(policy_identifier={0.policy_identifier}, polic" - "y_qualifiers={0.policy_qualifiers})>".format(self) - ) - - def __eq__(self, other): - if not isinstance(other, PolicyInformation): - return NotImplemented - - return ( - self.policy_identifier == other.policy_identifier and - self.policy_qualifiers == other.policy_qualifiers - ) - - def __ne__(self, other): - return not self == other - - policy_identifier = utils.read_only_property("_policy_identifier") - policy_qualifiers = utils.read_only_property("_policy_qualifiers") - - -class UserNotice(object): - def __init__(self, notice_reference, explicit_text): - if notice_reference and not isinstance( - notice_reference, NoticeReference - ): - raise TypeError( - "notice_reference must be None or a NoticeReference" - ) - - self._notice_reference = notice_reference - self._explicit_text = explicit_text - - def __repr__(self): - return ( - "<UserNotice(notice_reference={0.notice_reference}, explicit_text=" - "{0.explicit_text!r})>".format(self) - ) - - def __eq__(self, other): - if not isinstance(other, UserNotice): - return NotImplemented - - return ( - self.notice_reference == other.notice_reference and - self.explicit_text == other.explicit_text - ) - - def __ne__(self, other): - return not self == other - - notice_reference = utils.read_only_property("_notice_reference") - explicit_text = utils.read_only_property("_explicit_text") - - -class NoticeReference(object): - def __init__(self, organization, notice_numbers): - self._organization = organization - if not isinstance(notice_numbers, list) or not all( - isinstance(x, int) for x in notice_numbers - ): - raise TypeError( - "notice_numbers must be a list of integers" - ) - - self._notice_numbers = notice_numbers - - def __repr__(self): - return ( - "<NoticeReference(organization={0.organization!r}, notice_numbers=" - "{0.notice_numbers})>".format(self) - ) - - def __eq__(self, other): - if not isinstance(other, NoticeReference): - return NotImplemented - - return ( - self.organization == other.organization and - self.notice_numbers == other.notice_numbers - ) - - def __ne__(self, other): - return not self == other - - organization = utils.read_only_property("_organization") - notice_numbers = utils.read_only_property("_notice_numbers") - - -class SubjectKeyIdentifier(object): - def __init__(self, digest): - self._digest = digest - - digest = utils.read_only_property("_digest") - - def __repr__(self): - return "<SubjectKeyIdentifier(digest={0!r})>".format(self.digest) - - def __eq__(self, other): - if not isinstance(other, SubjectKeyIdentifier): - return NotImplemented - - return ( - self.digest == other.digest - ) - - def __ne__(self, other): - return not self == other - - -class NameConstraints(object): - def __init__(self, permitted_subtrees, excluded_subtrees): - if permitted_subtrees is not None: - if not all( - isinstance(x, GeneralName) for x in permitted_subtrees - ): - raise TypeError( - "permitted_subtrees must be a list of GeneralName objects " - "or None" - ) - - self._validate_ip_name(permitted_subtrees) - - if excluded_subtrees is not None: - if not all( - isinstance(x, GeneralName) for x in excluded_subtrees - ): - raise TypeError( - "excluded_subtrees must be a list of GeneralName objects " - "or None" - ) - - self._validate_ip_name(excluded_subtrees) - - if permitted_subtrees is None and excluded_subtrees is None: - raise ValueError( - "At least one of permitted_subtrees and excluded_subtrees " - "must not be None" - ) - - self._permitted_subtrees = permitted_subtrees - self._excluded_subtrees = excluded_subtrees - - def __eq__(self, other): - if not isinstance(other, NameConstraints): - return NotImplemented - - return ( - self.excluded_subtrees == other.excluded_subtrees and - self.permitted_subtrees == other.permitted_subtrees - ) - - def __ne__(self, other): - return not self == other - - def _validate_ip_name(self, tree): - if any(isinstance(name, IPAddress) and not isinstance( - name.value, (ipaddress.IPv4Network, ipaddress.IPv6Network) - ) for name in tree): - raise TypeError( - "IPAddress name constraints must be an IPv4Network or" - " IPv6Network object" - ) - - def __repr__(self): - return ( - u"<NameConstraints(permitted_subtrees={0.permitted_subtrees}, " - u"excluded_subtrees={0.excluded_subtrees})>".format(self) - ) - - permitted_subtrees = utils.read_only_property("_permitted_subtrees") - excluded_subtrees = utils.read_only_property("_excluded_subtrees") - - -class CRLDistributionPoints(object): - def __init__(self, distribution_points): - if not all( - isinstance(x, DistributionPoint) for x in distribution_points - ): - raise TypeError( - "distribution_points must be a list of DistributionPoint " - "objects" - ) - - self._distribution_points = distribution_points - - def __iter__(self): - return iter(self._distribution_points) - - def __len__(self): - return len(self._distribution_points) - - def __repr__(self): - return "<CRLDistributionPoints({0})>".format(self._distribution_points) - - def __eq__(self, other): - if not isinstance(other, CRLDistributionPoints): - return NotImplemented - - return self._distribution_points == other._distribution_points - - def __ne__(self, other): - return not self == other - - -class DistributionPoint(object): - def __init__(self, full_name, relative_name, reasons, crl_issuer): - if full_name and relative_name: - raise ValueError( - "At least one of full_name and relative_name must be None" - ) - - if full_name and not all( - isinstance(x, GeneralName) for x in full_name - ): - raise TypeError( - "full_name must be a list of GeneralName objects" - ) - - if relative_name and not isinstance(relative_name, Name): - raise TypeError("relative_name must be a Name") - - if crl_issuer and not all( - isinstance(x, GeneralName) for x in crl_issuer - ): - raise TypeError( - "crl_issuer must be None or a list of general names" - ) - - if reasons and (not isinstance(reasons, frozenset) or not all( - isinstance(x, ReasonFlags) for x in reasons - )): - raise TypeError("reasons must be None or frozenset of ReasonFlags") - - if reasons and ( - ReasonFlags.unspecified in reasons or - ReasonFlags.remove_from_crl in reasons - ): - raise ValueError( - "unspecified and remove_from_crl are not valid reasons in a " - "DistributionPoint" - ) - - if reasons and not crl_issuer and not (full_name or relative_name): - raise ValueError( - "You must supply crl_issuer, full_name, or relative_name when " - "reasons is not None" - ) - - self._full_name = full_name - self._relative_name = relative_name - self._reasons = reasons - self._crl_issuer = crl_issuer - - def __repr__(self): - return ( - "<DistributionPoint(full_name={0.full_name}, relative_name={0.rela" - "tive_name}, reasons={0.reasons}, crl_issuer={0.crl_is" - "suer})>".format(self) - ) - - def __eq__(self, other): - if not isinstance(other, DistributionPoint): - return NotImplemented - - return ( - self.full_name == other.full_name and - self.relative_name == other.relative_name and - self.reasons == other.reasons and - self.crl_issuer == other.crl_issuer - ) - - def __ne__(self, other): - return not self == other - - full_name = utils.read_only_property("_full_name") - relative_name = utils.read_only_property("_relative_name") - reasons = utils.read_only_property("_reasons") - crl_issuer = utils.read_only_property("_crl_issuer") - - -class ReasonFlags(Enum): - unspecified = "unspecified" - key_compromise = "keyCompromise" - ca_compromise = "cACompromise" - affiliation_changed = "affiliationChanged" - superseded = "superseded" - cessation_of_operation = "cessationOfOperation" - certificate_hold = "certificateHold" - privilege_withdrawn = "privilegeWithdrawn" - aa_compromise = "aACompromise" - remove_from_crl = "removeFromCRL" - - -class InhibitAnyPolicy(object): - def __init__(self, skip_certs): - if not isinstance(skip_certs, six.integer_types): - raise TypeError("skip_certs must be an integer") - - if skip_certs < 0: - raise ValueError("skip_certs must be a non-negative integer") - - self._skip_certs = skip_certs - - def __repr__(self): - return "<InhibitAnyPolicy(skip_certs={0.skip_certs})>".format(self) - - def __eq__(self, other): - if not isinstance(other, InhibitAnyPolicy): - return NotImplemented - - return self.skip_certs == other.skip_certs - - def __ne__(self, other): - return not self == other - - skip_certs = utils.read_only_property("_skip_certs") - - -@six.add_metaclass(abc.ABCMeta) -class GeneralName(object): - @abc.abstractproperty - def value(self): - """ - Return the value of the object - """ - - -@utils.register_interface(GeneralName) -class RFC822Name(object): - def __init__(self, value): - if not isinstance(value, six.text_type): - raise TypeError("value must be a unicode string") - - self._value = value - - value = utils.read_only_property("_value") - - def __repr__(self): - return "<RFC822Name(value={0})>".format(self.value) - - def __eq__(self, other): - if not isinstance(other, RFC822Name): - return NotImplemented - - return self.value == other.value - - def __ne__(self, other): - return not self == other - - -@utils.register_interface(GeneralName) -class DNSName(object): - def __init__(self, value): - if not isinstance(value, six.text_type): - raise TypeError("value must be a unicode string") - - self._value = value - - value = utils.read_only_property("_value") - - def __repr__(self): - return "<DNSName(value={0})>".format(self.value) - - def __eq__(self, other): - if not isinstance(other, DNSName): - return NotImplemented - - return self.value == other.value - - def __ne__(self, other): - return not self == other - - -@utils.register_interface(GeneralName) -class UniformResourceIdentifier(object): - def __init__(self, value): - if not isinstance(value, six.text_type): - raise TypeError("value must be a unicode string") - - self._value = value - - value = utils.read_only_property("_value") - - def __repr__(self): - return "<UniformResourceIdentifier(value={0})>".format(self.value) - - def __eq__(self, other): - if not isinstance(other, UniformResourceIdentifier): - return NotImplemented - - return self.value == other.value - - def __ne__(self, other): - return not self == other - - -@utils.register_interface(GeneralName) -class DirectoryName(object): - def __init__(self, value): - if not isinstance(value, Name): - raise TypeError("value must be a Name") - - self._value = value - - value = utils.read_only_property("_value") - - def __repr__(self): - return "<DirectoryName(value={0})>".format(self.value) - - def __eq__(self, other): - if not isinstance(other, DirectoryName): - return NotImplemented - - return self.value == other.value - - def __ne__(self, other): - return not self == other - - -@utils.register_interface(GeneralName) -class RegisteredID(object): - def __init__(self, value): - if not isinstance(value, ObjectIdentifier): - raise TypeError("value must be an ObjectIdentifier") - - self._value = value - - value = utils.read_only_property("_value") - - def __repr__(self): - return "<RegisteredID(value={0})>".format(self.value) - - def __eq__(self, other): - if not isinstance(other, RegisteredID): - return NotImplemented - - return self.value == other.value - - def __ne__(self, other): - return not self == other - - -@utils.register_interface(GeneralName) -class IPAddress(object): - def __init__(self, value): - if not isinstance( - value, - ( - ipaddress.IPv4Address, - ipaddress.IPv6Address, - ipaddress.IPv4Network, - ipaddress.IPv6Network - ) - ): - raise TypeError( - "value must be an instance of ipaddress.IPv4Address, " - "ipaddress.IPv6Address, ipaddress.IPv4Network, or " - "ipaddress.IPv6Network" - ) - - self._value = value - - value = utils.read_only_property("_value") - - def __repr__(self): - return "<IPAddress(value={0})>".format(self.value) - - def __eq__(self, other): - if not isinstance(other, IPAddress): - return NotImplemented - - return self.value == other.value - - def __ne__(self, other): - return not self == other - - -class GeneralNames(object): - def __init__(self, general_names): - if not all(isinstance(x, GeneralName) for x in general_names): - raise TypeError( - "Every item in the general_names list must be an " - "object conforming to the GeneralName interface" - ) - - self._general_names = general_names - - def __iter__(self): - return iter(self._general_names) - - def __len__(self): - return len(self._general_names) - - def get_values_for_type(self, type): - return [i.value for i in self if isinstance(i, type)] - - def __repr__(self): - return "<GeneralNames({0})>".format(self._general_names) - - def __eq__(self, other): - if not isinstance(other, GeneralNames): - return NotImplemented - - return self._general_names == other._general_names - - def __ne__(self, other): - return not self == other - - -class SubjectAlternativeName(object): - def __init__(self, general_names): - self._general_names = GeneralNames(general_names) - - def __iter__(self): - return iter(self._general_names) - - def __len__(self): - return len(self._general_names) - - def get_values_for_type(self, type): - return self._general_names.get_values_for_type(type) - - def __repr__(self): - return "<SubjectAlternativeName({0})>".format(self._general_names) - - def __eq__(self, other): - if not isinstance(other, SubjectAlternativeName): - return NotImplemented - - return self._general_names == other._general_names - - def __ne__(self, other): - return not self == other - - -class IssuerAlternativeName(object): - def __init__(self, general_names): - self._general_names = GeneralNames(general_names) - - def __iter__(self): - return iter(self._general_names) - - def __len__(self): - return len(self._general_names) - - def get_values_for_type(self, type): - return self._general_names.get_values_for_type(type) - - def __repr__(self): - return "<IssuerAlternativeName({0})>".format(self._general_names) - - def __eq__(self, other): - if not isinstance(other, IssuerAlternativeName): - return NotImplemented - - return self._general_names == other._general_names - - def __ne__(self, other): - return not self == other - - -class AuthorityKeyIdentifier(object): - def __init__(self, key_identifier, authority_cert_issuer, - authority_cert_serial_number): - if authority_cert_issuer or authority_cert_serial_number: - if not authority_cert_issuer or not authority_cert_serial_number: - raise ValueError( - "authority_cert_issuer and authority_cert_serial_number " - "must both be present or both None" - ) - - if not all( - isinstance(x, GeneralName) for x in authority_cert_issuer - ): - raise TypeError( - "authority_cert_issuer must be a list of GeneralName " - "objects" - ) - - if not isinstance(authority_cert_serial_number, six.integer_types): - raise TypeError( - "authority_cert_serial_number must be an integer" - ) - - self._key_identifier = key_identifier - self._authority_cert_issuer = authority_cert_issuer - self._authority_cert_serial_number = authority_cert_serial_number - - def __repr__(self): - return ( - "<AuthorityKeyIdentifier(key_identifier={0.key_identifier!r}, " - "authority_cert_issuer={0.authority_cert_issuer}, " - "authority_cert_serial_number={0.authority_cert_serial_number}" - ")>".format(self) - ) - - def __eq__(self, other): - if not isinstance(other, AuthorityKeyIdentifier): - return NotImplemented - - return ( - self.key_identifier == other.key_identifier and - self.authority_cert_issuer == other.authority_cert_issuer and - self.authority_cert_serial_number == - other.authority_cert_serial_number - ) - - def __ne__(self, other): - return not self == other - - key_identifier = utils.read_only_property("_key_identifier") - authority_cert_issuer = utils.read_only_property("_authority_cert_issuer") - authority_cert_serial_number = utils.read_only_property( - "_authority_cert_serial_number" - ) - - -OID_COMMON_NAME = ObjectIdentifier("2.5.4.3") -OID_COUNTRY_NAME = ObjectIdentifier("2.5.4.6") -OID_LOCALITY_NAME = ObjectIdentifier("2.5.4.7") -OID_STATE_OR_PROVINCE_NAME = ObjectIdentifier("2.5.4.8") -OID_ORGANIZATION_NAME = ObjectIdentifier("2.5.4.10") -OID_ORGANIZATIONAL_UNIT_NAME = ObjectIdentifier("2.5.4.11") -OID_SERIAL_NUMBER = ObjectIdentifier("2.5.4.5") -OID_SURNAME = ObjectIdentifier("2.5.4.4") -OID_GIVEN_NAME = ObjectIdentifier("2.5.4.42") -OID_TITLE = ObjectIdentifier("2.5.4.12") -OID_GENERATION_QUALIFIER = ObjectIdentifier("2.5.4.44") -OID_DN_QUALIFIER = ObjectIdentifier("2.5.4.46") -OID_PSEUDONYM = ObjectIdentifier("2.5.4.65") -OID_DOMAIN_COMPONENT = ObjectIdentifier("0.9.2342.19200300.100.1.25") -OID_EMAIL_ADDRESS = ObjectIdentifier("1.2.840.113549.1.9.1") - -OID_RSA_WITH_MD5 = ObjectIdentifier("1.2.840.113549.1.1.4") -OID_RSA_WITH_SHA1 = ObjectIdentifier("1.2.840.113549.1.1.5") -OID_RSA_WITH_SHA224 = ObjectIdentifier("1.2.840.113549.1.1.14") -OID_RSA_WITH_SHA256 = ObjectIdentifier("1.2.840.113549.1.1.11") -OID_RSA_WITH_SHA384 = ObjectIdentifier("1.2.840.113549.1.1.12") -OID_RSA_WITH_SHA512 = ObjectIdentifier("1.2.840.113549.1.1.13") -OID_ECDSA_WITH_SHA1 = ObjectIdentifier("1.2.840.10045.4.1") -OID_ECDSA_WITH_SHA224 = ObjectIdentifier("1.2.840.10045.4.3.1") -OID_ECDSA_WITH_SHA256 = ObjectIdentifier("1.2.840.10045.4.3.2") -OID_ECDSA_WITH_SHA384 = ObjectIdentifier("1.2.840.10045.4.3.3") -OID_ECDSA_WITH_SHA512 = ObjectIdentifier("1.2.840.10045.4.3.4") -OID_DSA_WITH_SHA1 = ObjectIdentifier("1.2.840.10040.4.3") -OID_DSA_WITH_SHA224 = ObjectIdentifier("2.16.840.1.101.3.4.3.1") -OID_DSA_WITH_SHA256 = ObjectIdentifier("2.16.840.1.101.3.4.3.2") - -_SIG_OIDS_TO_HASH = { - OID_RSA_WITH_MD5.dotted_string: hashes.MD5(), - OID_RSA_WITH_SHA1.dotted_string: hashes.SHA1(), - OID_RSA_WITH_SHA224.dotted_string: hashes.SHA224(), - OID_RSA_WITH_SHA256.dotted_string: hashes.SHA256(), - OID_RSA_WITH_SHA384.dotted_string: hashes.SHA384(), - OID_RSA_WITH_SHA512.dotted_string: hashes.SHA512(), - OID_ECDSA_WITH_SHA1.dotted_string: hashes.SHA1(), - OID_ECDSA_WITH_SHA224.dotted_string: hashes.SHA224(), - OID_ECDSA_WITH_SHA256.dotted_string: hashes.SHA256(), - OID_ECDSA_WITH_SHA384.dotted_string: hashes.SHA384(), - OID_ECDSA_WITH_SHA512.dotted_string: hashes.SHA512(), - OID_DSA_WITH_SHA1.dotted_string: hashes.SHA1(), - OID_DSA_WITH_SHA224.dotted_string: hashes.SHA224(), - OID_DSA_WITH_SHA256.dotted_string: hashes.SHA256() -} - -OID_SERVER_AUTH = ObjectIdentifier("1.3.6.1.5.5.7.3.1") -OID_CLIENT_AUTH = ObjectIdentifier("1.3.6.1.5.5.7.3.2") -OID_CODE_SIGNING = ObjectIdentifier("1.3.6.1.5.5.7.3.3") -OID_EMAIL_PROTECTION = ObjectIdentifier("1.3.6.1.5.5.7.3.4") -OID_TIME_STAMPING = ObjectIdentifier("1.3.6.1.5.5.7.3.8") -OID_OCSP_SIGNING = ObjectIdentifier("1.3.6.1.5.5.7.3.9") - -OID_CA_ISSUERS = ObjectIdentifier("1.3.6.1.5.5.7.48.2") -OID_OCSP = ObjectIdentifier("1.3.6.1.5.5.7.48.1") - -OID_CPS_QUALIFIER = ObjectIdentifier("1.3.6.1.5.5.7.2.1") -OID_CPS_USER_NOTICE = ObjectIdentifier("1.3.6.1.5.5.7.2.2") -OID_ANY_POLICY = ObjectIdentifier("2.5.29.32.0") - - -@six.add_metaclass(abc.ABCMeta) -class Certificate(object): - @abc.abstractmethod - def fingerprint(self, algorithm): - """ - Returns bytes using digest passed. - """ - - @abc.abstractproperty - def serial(self): - """ - Returns certificate serial number - """ - - @abc.abstractproperty - def version(self): - """ - Returns the certificate version - """ - - @abc.abstractmethod - def public_key(self): - """ - Returns the public key - """ - - @abc.abstractproperty - def not_valid_before(self): - """ - Not before time (represented as UTC datetime) - """ - - @abc.abstractproperty - def not_valid_after(self): - """ - Not after time (represented as UTC datetime) - """ - - @abc.abstractproperty - def issuer(self): - """ - Returns the issuer name object. - """ - - @abc.abstractproperty - def subject(self): - """ - Returns the subject name object. - """ - - @abc.abstractproperty - def signature_hash_algorithm(self): - """ - Returns a HashAlgorithm corresponding to the type of the digest signed - in the certificate. - """ - - @abc.abstractproperty - def extensions(self): - """ - Returns an Extensions object. - """ - - @abc.abstractmethod - def __eq__(self, other): - """ - Checks equality. - """ - - @abc.abstractmethod - def __ne__(self, other): - """ - Checks not equal. - """ - - @abc.abstractmethod - def public_bytes(self, encoding): - """ - Serializes the certificate to PEM or DER format. - """ - - -@six.add_metaclass(abc.ABCMeta) -class CertificateRevocationList(object): - - @abc.abstractmethod - def fingerprint(self, algorithm): - """ - Returns bytes using digest passed. - """ - - @abc.abstractproperty - def signature_hash_algorithm(self): - """ - Returns a HashAlgorithm corresponding to the type of the digest signed - in the certificate. - """ - - @abc.abstractproperty - def issuer(self): - """ - Returns the X509Name with the issuer of this CRL. - """ - - @abc.abstractproperty - def next_update(self): - """ - Returns the date of next update for this CRL. - """ - - @abc.abstractproperty - def last_update(self): - """ - Returns the date of last update for this CRL. - """ - - @abc.abstractproperty - def revoked_certificates(self): - """ - Returns a list of RevokedCertificate objects for this CRL. - """ - - @abc.abstractproperty - def extensions(self): - """ - Returns an Extensions object containing a list of CRL extensions. - """ - - @abc.abstractmethod - def __eq__(self, other): - """ - Checks equality. - """ - - @abc.abstractmethod - def __ne__(self, other): - """ - Checks not equal. - """ - - -@six.add_metaclass(abc.ABCMeta) -class CertificateSigningRequest(object): - @abc.abstractmethod - def public_key(self): - """ - Returns the public key - """ - - @abc.abstractproperty - def subject(self): - """ - Returns the subject name object. - """ - - @abc.abstractproperty - def signature_hash_algorithm(self): - """ - Returns a HashAlgorithm corresponding to the type of the digest signed - in the certificate. - """ - - @abc.abstractproperty - def extensions(self): - """ - Returns the extensions in the signing request. - """ - - @abc.abstractmethod - def public_bytes(self, encoding): - """ - Encodes the request to PEM or DER format. - """ - - -@six.add_metaclass(abc.ABCMeta) -class RevokedCertificate(object): - @abc.abstractproperty - def serial_number(self): - """ - Returns the serial number of the revoked certificate. - """ - - @abc.abstractproperty - def revocation_date(self): - """ - Returns the date of when this certificate was revoked. - """ - - @abc.abstractproperty - def extensions(self): - """ - Returns an Extensions object containing a list of Revoked extensions. - """ - - -class CertificateSigningRequestBuilder(object): - def __init__(self, subject_name=None, extensions=[]): - """ - Creates an empty X.509 certificate request (v1). - """ - self._subject_name = subject_name - self._extensions = extensions - - def subject_name(self, name): - """ - Sets the certificate requestor's distinguished name. - """ - if not isinstance(name, Name): - raise TypeError('Expecting x509.Name object.') - if self._subject_name is not None: - raise ValueError('The subject name may only be set once.') - return CertificateSigningRequestBuilder(name, self._extensions) - - def add_extension(self, extension, critical): - """ - Adds an X.509 extension to the certificate request. - """ - if isinstance(extension, BasicConstraints): - extension = Extension(OID_BASIC_CONSTRAINTS, critical, extension) - elif isinstance(extension, SubjectAlternativeName): - extension = Extension( - OID_SUBJECT_ALTERNATIVE_NAME, critical, extension - ) - else: - raise NotImplementedError('Unsupported X.509 extension.') - # TODO: This is quadratic in the number of extensions - for e in self._extensions: - if e.oid == extension.oid: - raise ValueError('This extension has already been set.') - return CertificateSigningRequestBuilder( - self._subject_name, self._extensions + [extension] - ) - - def sign(self, private_key, algorithm, backend): - """ - Signs the request using the requestor's private key. - """ - return backend.create_x509_csr(self, private_key, algorithm) diff --git a/src/cryptography/x509/__init__.py b/src/cryptography/x509/__init__.py new file mode 100644 index 00000000..b761e264 --- /dev/null +++ b/src/cryptography/x509/__init__.py @@ -0,0 +1,189 @@ +# 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.x509 import certificate_transparency +from cryptography.x509.base import ( + Certificate, CertificateBuilder, CertificateRevocationList, + CertificateRevocationListBuilder, + CertificateSigningRequest, CertificateSigningRequestBuilder, + InvalidVersion, RevokedCertificate, RevokedCertificateBuilder, + Version, load_der_x509_certificate, load_der_x509_crl, load_der_x509_csr, + load_pem_x509_certificate, load_pem_x509_crl, load_pem_x509_csr, + random_serial_number, +) +from cryptography.x509.extensions import ( + AccessDescription, AuthorityInformationAccess, + AuthorityKeyIdentifier, BasicConstraints, CRLDistributionPoints, + CRLNumber, CRLReason, CertificateIssuer, CertificatePolicies, + DeltaCRLIndicator, DistributionPoint, DuplicateExtension, ExtendedKeyUsage, + Extension, ExtensionNotFound, ExtensionType, Extensions, FreshestCRL, + GeneralNames, InhibitAnyPolicy, InvalidityDate, IssuerAlternativeName, + IssuingDistributionPoint, KeyUsage, NameConstraints, NoticeReference, + OCSPNoCheck, OCSPNonce, PolicyConstraints, PolicyInformation, + PrecertPoison, PrecertificateSignedCertificateTimestamps, ReasonFlags, + SubjectAlternativeName, SubjectKeyIdentifier, TLSFeature, TLSFeatureType, + UnrecognizedExtension, UserNotice +) +from cryptography.x509.general_name import ( + DNSName, DirectoryName, GeneralName, IPAddress, OtherName, RFC822Name, + RegisteredID, UniformResourceIdentifier, UnsupportedGeneralNameType, + _GENERAL_NAMES +) +from cryptography.x509.name import ( + Name, NameAttribute, RelativeDistinguishedName +) +from cryptography.x509.oid import ( + AuthorityInformationAccessOID, CRLEntryExtensionOID, + CertificatePoliciesOID, ExtendedKeyUsageOID, ExtensionOID, NameOID, + ObjectIdentifier, SignatureAlgorithmOID, _SIG_OIDS_TO_HASH +) + + +OID_AUTHORITY_INFORMATION_ACCESS = ExtensionOID.AUTHORITY_INFORMATION_ACCESS +OID_AUTHORITY_KEY_IDENTIFIER = ExtensionOID.AUTHORITY_KEY_IDENTIFIER +OID_BASIC_CONSTRAINTS = ExtensionOID.BASIC_CONSTRAINTS +OID_CERTIFICATE_POLICIES = ExtensionOID.CERTIFICATE_POLICIES +OID_CRL_DISTRIBUTION_POINTS = ExtensionOID.CRL_DISTRIBUTION_POINTS +OID_EXTENDED_KEY_USAGE = ExtensionOID.EXTENDED_KEY_USAGE +OID_FRESHEST_CRL = ExtensionOID.FRESHEST_CRL +OID_INHIBIT_ANY_POLICY = ExtensionOID.INHIBIT_ANY_POLICY +OID_ISSUER_ALTERNATIVE_NAME = ExtensionOID.ISSUER_ALTERNATIVE_NAME +OID_KEY_USAGE = ExtensionOID.KEY_USAGE +OID_NAME_CONSTRAINTS = ExtensionOID.NAME_CONSTRAINTS +OID_OCSP_NO_CHECK = ExtensionOID.OCSP_NO_CHECK +OID_POLICY_CONSTRAINTS = ExtensionOID.POLICY_CONSTRAINTS +OID_POLICY_MAPPINGS = ExtensionOID.POLICY_MAPPINGS +OID_SUBJECT_ALTERNATIVE_NAME = ExtensionOID.SUBJECT_ALTERNATIVE_NAME +OID_SUBJECT_DIRECTORY_ATTRIBUTES = ExtensionOID.SUBJECT_DIRECTORY_ATTRIBUTES +OID_SUBJECT_INFORMATION_ACCESS = ExtensionOID.SUBJECT_INFORMATION_ACCESS +OID_SUBJECT_KEY_IDENTIFIER = ExtensionOID.SUBJECT_KEY_IDENTIFIER + +OID_DSA_WITH_SHA1 = SignatureAlgorithmOID.DSA_WITH_SHA1 +OID_DSA_WITH_SHA224 = SignatureAlgorithmOID.DSA_WITH_SHA224 +OID_DSA_WITH_SHA256 = SignatureAlgorithmOID.DSA_WITH_SHA256 +OID_ECDSA_WITH_SHA1 = SignatureAlgorithmOID.ECDSA_WITH_SHA1 +OID_ECDSA_WITH_SHA224 = SignatureAlgorithmOID.ECDSA_WITH_SHA224 +OID_ECDSA_WITH_SHA256 = SignatureAlgorithmOID.ECDSA_WITH_SHA256 +OID_ECDSA_WITH_SHA384 = SignatureAlgorithmOID.ECDSA_WITH_SHA384 +OID_ECDSA_WITH_SHA512 = SignatureAlgorithmOID.ECDSA_WITH_SHA512 +OID_RSA_WITH_MD5 = SignatureAlgorithmOID.RSA_WITH_MD5 +OID_RSA_WITH_SHA1 = SignatureAlgorithmOID.RSA_WITH_SHA1 +OID_RSA_WITH_SHA224 = SignatureAlgorithmOID.RSA_WITH_SHA224 +OID_RSA_WITH_SHA256 = SignatureAlgorithmOID.RSA_WITH_SHA256 +OID_RSA_WITH_SHA384 = SignatureAlgorithmOID.RSA_WITH_SHA384 +OID_RSA_WITH_SHA512 = SignatureAlgorithmOID.RSA_WITH_SHA512 +OID_RSASSA_PSS = SignatureAlgorithmOID.RSASSA_PSS + +OID_COMMON_NAME = NameOID.COMMON_NAME +OID_COUNTRY_NAME = NameOID.COUNTRY_NAME +OID_DOMAIN_COMPONENT = NameOID.DOMAIN_COMPONENT +OID_DN_QUALIFIER = NameOID.DN_QUALIFIER +OID_EMAIL_ADDRESS = NameOID.EMAIL_ADDRESS +OID_GENERATION_QUALIFIER = NameOID.GENERATION_QUALIFIER +OID_GIVEN_NAME = NameOID.GIVEN_NAME +OID_LOCALITY_NAME = NameOID.LOCALITY_NAME +OID_ORGANIZATIONAL_UNIT_NAME = NameOID.ORGANIZATIONAL_UNIT_NAME +OID_ORGANIZATION_NAME = NameOID.ORGANIZATION_NAME +OID_PSEUDONYM = NameOID.PSEUDONYM +OID_SERIAL_NUMBER = NameOID.SERIAL_NUMBER +OID_STATE_OR_PROVINCE_NAME = NameOID.STATE_OR_PROVINCE_NAME +OID_SURNAME = NameOID.SURNAME +OID_TITLE = NameOID.TITLE + +OID_CLIENT_AUTH = ExtendedKeyUsageOID.CLIENT_AUTH +OID_CODE_SIGNING = ExtendedKeyUsageOID.CODE_SIGNING +OID_EMAIL_PROTECTION = ExtendedKeyUsageOID.EMAIL_PROTECTION +OID_OCSP_SIGNING = ExtendedKeyUsageOID.OCSP_SIGNING +OID_SERVER_AUTH = ExtendedKeyUsageOID.SERVER_AUTH +OID_TIME_STAMPING = ExtendedKeyUsageOID.TIME_STAMPING + +OID_ANY_POLICY = CertificatePoliciesOID.ANY_POLICY +OID_CPS_QUALIFIER = CertificatePoliciesOID.CPS_QUALIFIER +OID_CPS_USER_NOTICE = CertificatePoliciesOID.CPS_USER_NOTICE + +OID_CERTIFICATE_ISSUER = CRLEntryExtensionOID.CERTIFICATE_ISSUER +OID_CRL_REASON = CRLEntryExtensionOID.CRL_REASON +OID_INVALIDITY_DATE = CRLEntryExtensionOID.INVALIDITY_DATE + +OID_CA_ISSUERS = AuthorityInformationAccessOID.CA_ISSUERS +OID_OCSP = AuthorityInformationAccessOID.OCSP + +__all__ = [ + "certificate_transparency", + "load_pem_x509_certificate", + "load_der_x509_certificate", + "load_pem_x509_csr", + "load_der_x509_csr", + "load_pem_x509_crl", + "load_der_x509_crl", + "random_serial_number", + "InvalidVersion", + "DeltaCRLIndicator", + "DuplicateExtension", + "ExtensionNotFound", + "UnsupportedGeneralNameType", + "NameAttribute", + "Name", + "RelativeDistinguishedName", + "ObjectIdentifier", + "ExtensionType", + "Extensions", + "Extension", + "ExtendedKeyUsage", + "FreshestCRL", + "IssuingDistributionPoint", + "TLSFeature", + "TLSFeatureType", + "OCSPNoCheck", + "BasicConstraints", + "CRLNumber", + "KeyUsage", + "AuthorityInformationAccess", + "AccessDescription", + "CertificatePolicies", + "PolicyInformation", + "UserNotice", + "NoticeReference", + "SubjectKeyIdentifier", + "NameConstraints", + "CRLDistributionPoints", + "DistributionPoint", + "ReasonFlags", + "InhibitAnyPolicy", + "SubjectAlternativeName", + "IssuerAlternativeName", + "AuthorityKeyIdentifier", + "GeneralNames", + "GeneralName", + "RFC822Name", + "DNSName", + "UniformResourceIdentifier", + "RegisteredID", + "DirectoryName", + "IPAddress", + "OtherName", + "Certificate", + "CertificateRevocationList", + "CertificateRevocationListBuilder", + "CertificateSigningRequest", + "RevokedCertificate", + "RevokedCertificateBuilder", + "CertificateSigningRequestBuilder", + "CertificateBuilder", + "Version", + "_SIG_OIDS_TO_HASH", + "OID_CA_ISSUERS", + "OID_OCSP", + "_GENERAL_NAMES", + "CertificateIssuer", + "CRLReason", + "InvalidityDate", + "UnrecognizedExtension", + "PolicyConstraints", + "PrecertificateSignedCertificateTimestamps", + "PrecertPoison", + "OCSPNonce", +] diff --git a/src/cryptography/x509/base.py b/src/cryptography/x509/base.py new file mode 100644 index 00000000..3983c9b3 --- /dev/null +++ b/src/cryptography/x509/base.py @@ -0,0 +1,758 @@ +# 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 datetime +import os +from enum import Enum + +import six + +from cryptography import utils +from cryptography.hazmat.primitives.asymmetric import ( + dsa, ec, ed25519, ed448, rsa +) +from cryptography.x509.extensions import Extension, ExtensionType +from cryptography.x509.name import Name + + +_EARLIEST_UTC_TIME = datetime.datetime(1950, 1, 1) + + +def _reject_duplicate_extension(extension, extensions): + # This is quadratic in the number of extensions + for e in extensions: + if e.oid == extension.oid: + raise ValueError('This extension has already been set.') + + +def _convert_to_naive_utc_time(time): + """Normalizes a datetime to a naive datetime in UTC. + + time -- datetime to normalize. Assumed to be in UTC if not timezone + aware. + """ + if time.tzinfo is not None: + offset = time.utcoffset() + offset = offset if offset else datetime.timedelta() + return time.replace(tzinfo=None) - offset + else: + return time + + +class Version(Enum): + v1 = 0 + v3 = 2 + + +def load_pem_x509_certificate(data, backend): + return backend.load_pem_x509_certificate(data) + + +def load_der_x509_certificate(data, backend): + return backend.load_der_x509_certificate(data) + + +def load_pem_x509_csr(data, backend): + return backend.load_pem_x509_csr(data) + + +def load_der_x509_csr(data, backend): + return backend.load_der_x509_csr(data) + + +def load_pem_x509_crl(data, backend): + return backend.load_pem_x509_crl(data) + + +def load_der_x509_crl(data, backend): + return backend.load_der_x509_crl(data) + + +class InvalidVersion(Exception): + def __init__(self, msg, parsed_version): + super(InvalidVersion, self).__init__(msg) + self.parsed_version = parsed_version + + +@six.add_metaclass(abc.ABCMeta) +class Certificate(object): + @abc.abstractmethod + def fingerprint(self, algorithm): + """ + Returns bytes using digest passed. + """ + + @abc.abstractproperty + def serial_number(self): + """ + Returns certificate serial number + """ + + @abc.abstractproperty + def version(self): + """ + Returns the certificate version + """ + + @abc.abstractmethod + def public_key(self): + """ + Returns the public key + """ + + @abc.abstractproperty + def not_valid_before(self): + """ + Not before time (represented as UTC datetime) + """ + + @abc.abstractproperty + def not_valid_after(self): + """ + Not after time (represented as UTC datetime) + """ + + @abc.abstractproperty + def issuer(self): + """ + Returns the issuer name object. + """ + + @abc.abstractproperty + def subject(self): + """ + Returns the subject name object. + """ + + @abc.abstractproperty + def signature_hash_algorithm(self): + """ + Returns a HashAlgorithm corresponding to the type of the digest signed + in the certificate. + """ + + @abc.abstractproperty + def signature_algorithm_oid(self): + """ + Returns the ObjectIdentifier of the signature algorithm. + """ + + @abc.abstractproperty + def extensions(self): + """ + Returns an Extensions object. + """ + + @abc.abstractproperty + def signature(self): + """ + Returns the signature bytes. + """ + + @abc.abstractproperty + def tbs_certificate_bytes(self): + """ + Returns the tbsCertificate payload bytes as defined in RFC 5280. + """ + + @abc.abstractmethod + def __eq__(self, other): + """ + Checks equality. + """ + + @abc.abstractmethod + def __ne__(self, other): + """ + Checks not equal. + """ + + @abc.abstractmethod + def __hash__(self): + """ + Computes a hash. + """ + + @abc.abstractmethod + def public_bytes(self, encoding): + """ + Serializes the certificate to PEM or DER format. + """ + + +@six.add_metaclass(abc.ABCMeta) +class CertificateRevocationList(object): + @abc.abstractmethod + def public_bytes(self, encoding): + """ + Serializes the CRL to PEM or DER format. + """ + + @abc.abstractmethod + def fingerprint(self, algorithm): + """ + Returns bytes using digest passed. + """ + + @abc.abstractmethod + def get_revoked_certificate_by_serial_number(self, serial_number): + """ + Returns an instance of RevokedCertificate or None if the serial_number + is not in the CRL. + """ + + @abc.abstractproperty + def signature_hash_algorithm(self): + """ + Returns a HashAlgorithm corresponding to the type of the digest signed + in the certificate. + """ + + @abc.abstractproperty + def signature_algorithm_oid(self): + """ + Returns the ObjectIdentifier of the signature algorithm. + """ + + @abc.abstractproperty + def issuer(self): + """ + Returns the X509Name with the issuer of this CRL. + """ + + @abc.abstractproperty + def next_update(self): + """ + Returns the date of next update for this CRL. + """ + + @abc.abstractproperty + def last_update(self): + """ + Returns the date of last update for this CRL. + """ + + @abc.abstractproperty + def extensions(self): + """ + Returns an Extensions object containing a list of CRL extensions. + """ + + @abc.abstractproperty + def signature(self): + """ + Returns the signature bytes. + """ + + @abc.abstractproperty + def tbs_certlist_bytes(self): + """ + Returns the tbsCertList payload bytes as defined in RFC 5280. + """ + + @abc.abstractmethod + def __eq__(self, other): + """ + Checks equality. + """ + + @abc.abstractmethod + def __ne__(self, other): + """ + Checks not equal. + """ + + @abc.abstractmethod + def __len__(self): + """ + Number of revoked certificates in the CRL. + """ + + @abc.abstractmethod + def __getitem__(self, idx): + """ + Returns a revoked certificate (or slice of revoked certificates). + """ + + @abc.abstractmethod + def __iter__(self): + """ + Iterator over the revoked certificates + """ + + @abc.abstractmethod + def is_signature_valid(self, public_key): + """ + Verifies signature of revocation list against given public key. + """ + + +@six.add_metaclass(abc.ABCMeta) +class CertificateSigningRequest(object): + @abc.abstractmethod + def __eq__(self, other): + """ + Checks equality. + """ + + @abc.abstractmethod + def __ne__(self, other): + """ + Checks not equal. + """ + + @abc.abstractmethod + def __hash__(self): + """ + Computes a hash. + """ + + @abc.abstractmethod + def public_key(self): + """ + Returns the public key + """ + + @abc.abstractproperty + def subject(self): + """ + Returns the subject name object. + """ + + @abc.abstractproperty + def signature_hash_algorithm(self): + """ + Returns a HashAlgorithm corresponding to the type of the digest signed + in the certificate. + """ + + @abc.abstractproperty + def signature_algorithm_oid(self): + """ + Returns the ObjectIdentifier of the signature algorithm. + """ + + @abc.abstractproperty + def extensions(self): + """ + Returns the extensions in the signing request. + """ + + @abc.abstractmethod + def public_bytes(self, encoding): + """ + Encodes the request to PEM or DER format. + """ + + @abc.abstractproperty + def signature(self): + """ + Returns the signature bytes. + """ + + @abc.abstractproperty + def tbs_certrequest_bytes(self): + """ + Returns the PKCS#10 CertificationRequestInfo bytes as defined in RFC + 2986. + """ + + @abc.abstractproperty + def is_signature_valid(self): + """ + Verifies signature of signing request. + """ + + +@six.add_metaclass(abc.ABCMeta) +class RevokedCertificate(object): + @abc.abstractproperty + def serial_number(self): + """ + Returns the serial number of the revoked certificate. + """ + + @abc.abstractproperty + def revocation_date(self): + """ + Returns the date of when this certificate was revoked. + """ + + @abc.abstractproperty + def extensions(self): + """ + Returns an Extensions object containing a list of Revoked extensions. + """ + + +class CertificateSigningRequestBuilder(object): + def __init__(self, subject_name=None, extensions=[]): + """ + Creates an empty X.509 certificate request (v1). + """ + self._subject_name = subject_name + self._extensions = extensions + + def subject_name(self, name): + """ + Sets the certificate requestor's distinguished name. + """ + if not isinstance(name, Name): + raise TypeError('Expecting x509.Name object.') + if self._subject_name is not None: + raise ValueError('The subject name may only be set once.') + return CertificateSigningRequestBuilder(name, self._extensions) + + def add_extension(self, extension, critical): + """ + Adds an X.509 extension to the certificate request. + """ + if not isinstance(extension, ExtensionType): + raise TypeError("extension must be an ExtensionType") + + extension = Extension(extension.oid, critical, extension) + _reject_duplicate_extension(extension, self._extensions) + + return CertificateSigningRequestBuilder( + self._subject_name, self._extensions + [extension] + ) + + def sign(self, private_key, algorithm, backend): + """ + Signs the request using the requestor's private key. + """ + if self._subject_name is None: + raise ValueError("A CertificateSigningRequest must have a subject") + return backend.create_x509_csr(self, private_key, algorithm) + + +class CertificateBuilder(object): + def __init__(self, issuer_name=None, subject_name=None, + public_key=None, serial_number=None, not_valid_before=None, + not_valid_after=None, extensions=[]): + self._version = Version.v3 + self._issuer_name = issuer_name + self._subject_name = subject_name + self._public_key = public_key + self._serial_number = serial_number + self._not_valid_before = not_valid_before + self._not_valid_after = not_valid_after + self._extensions = extensions + + def issuer_name(self, name): + """ + Sets the CA's distinguished name. + """ + if not isinstance(name, Name): + raise TypeError('Expecting x509.Name object.') + if self._issuer_name is not None: + raise ValueError('The issuer name may only be set once.') + return CertificateBuilder( + name, self._subject_name, self._public_key, + self._serial_number, self._not_valid_before, + self._not_valid_after, self._extensions + ) + + def subject_name(self, name): + """ + Sets the requestor's distinguished name. + """ + if not isinstance(name, Name): + raise TypeError('Expecting x509.Name object.') + if self._subject_name is not None: + raise ValueError('The subject name may only be set once.') + return CertificateBuilder( + self._issuer_name, name, self._public_key, + self._serial_number, self._not_valid_before, + self._not_valid_after, self._extensions + ) + + def public_key(self, key): + """ + Sets the requestor's public key (as found in the signing request). + """ + if not isinstance(key, (dsa.DSAPublicKey, rsa.RSAPublicKey, + ec.EllipticCurvePublicKey, + ed25519.Ed25519PublicKey, + ed448.Ed448PublicKey)): + raise TypeError('Expecting one of DSAPublicKey, RSAPublicKey,' + ' EllipticCurvePublicKey, Ed25519PublicKey or' + ' Ed448PublicKey.') + if self._public_key is not None: + raise ValueError('The public key may only be set once.') + return CertificateBuilder( + self._issuer_name, self._subject_name, key, + self._serial_number, self._not_valid_before, + self._not_valid_after, self._extensions + ) + + def serial_number(self, number): + """ + Sets the certificate serial number. + """ + if not isinstance(number, six.integer_types): + raise TypeError('Serial number must be of integral type.') + if self._serial_number is not None: + raise ValueError('The serial number may only be set once.') + if number <= 0: + raise ValueError('The serial number should be positive.') + + # ASN.1 integers are always signed, so most significant bit must be + # zero. + if number.bit_length() >= 160: # As defined in RFC 5280 + raise ValueError('The serial number should not be more than 159 ' + 'bits.') + return CertificateBuilder( + self._issuer_name, self._subject_name, + self._public_key, number, self._not_valid_before, + self._not_valid_after, self._extensions + ) + + def not_valid_before(self, time): + """ + Sets the certificate activation time. + """ + if not isinstance(time, datetime.datetime): + raise TypeError('Expecting datetime object.') + if self._not_valid_before is not None: + raise ValueError('The not valid before may only be set once.') + time = _convert_to_naive_utc_time(time) + if time < _EARLIEST_UTC_TIME: + raise ValueError('The not valid before date must be on or after' + ' 1950 January 1).') + if self._not_valid_after is not None and time > self._not_valid_after: + raise ValueError( + 'The not valid before date must be before the not valid after ' + 'date.' + ) + return CertificateBuilder( + self._issuer_name, self._subject_name, + self._public_key, self._serial_number, time, + self._not_valid_after, self._extensions + ) + + def not_valid_after(self, time): + """ + Sets the certificate expiration time. + """ + if not isinstance(time, datetime.datetime): + raise TypeError('Expecting datetime object.') + if self._not_valid_after is not None: + raise ValueError('The not valid after may only be set once.') + time = _convert_to_naive_utc_time(time) + if time < _EARLIEST_UTC_TIME: + raise ValueError('The not valid after date must be on or after' + ' 1950 January 1.') + if (self._not_valid_before is not None and + time < self._not_valid_before): + raise ValueError( + 'The not valid after date must be after the not valid before ' + 'date.' + ) + return CertificateBuilder( + self._issuer_name, self._subject_name, + self._public_key, self._serial_number, self._not_valid_before, + time, self._extensions + ) + + def add_extension(self, extension, critical): + """ + Adds an X.509 extension to the certificate. + """ + if not isinstance(extension, ExtensionType): + raise TypeError("extension must be an ExtensionType") + + extension = Extension(extension.oid, critical, extension) + _reject_duplicate_extension(extension, self._extensions) + + return CertificateBuilder( + self._issuer_name, self._subject_name, + self._public_key, self._serial_number, self._not_valid_before, + self._not_valid_after, self._extensions + [extension] + ) + + def sign(self, private_key, algorithm, backend): + """ + Signs the certificate using the CA's private key. + """ + if self._subject_name is None: + raise ValueError("A certificate must have a subject name") + + if self._issuer_name is None: + raise ValueError("A certificate must have an issuer name") + + if self._serial_number is None: + raise ValueError("A certificate must have a serial number") + + if self._not_valid_before is None: + raise ValueError("A certificate must have a not valid before time") + + if self._not_valid_after is None: + raise ValueError("A certificate must have a not valid after time") + + if self._public_key is None: + raise ValueError("A certificate must have a public key") + + return backend.create_x509_certificate(self, private_key, algorithm) + + +class CertificateRevocationListBuilder(object): + def __init__(self, issuer_name=None, last_update=None, next_update=None, + extensions=[], revoked_certificates=[]): + self._issuer_name = issuer_name + self._last_update = last_update + self._next_update = next_update + self._extensions = extensions + self._revoked_certificates = revoked_certificates + + def issuer_name(self, issuer_name): + if not isinstance(issuer_name, Name): + raise TypeError('Expecting x509.Name object.') + if self._issuer_name is not None: + raise ValueError('The issuer name may only be set once.') + return CertificateRevocationListBuilder( + issuer_name, self._last_update, self._next_update, + self._extensions, self._revoked_certificates + ) + + def last_update(self, last_update): + if not isinstance(last_update, datetime.datetime): + raise TypeError('Expecting datetime object.') + if self._last_update is not None: + raise ValueError('Last update may only be set once.') + last_update = _convert_to_naive_utc_time(last_update) + if last_update < _EARLIEST_UTC_TIME: + raise ValueError('The last update date must be on or after' + ' 1950 January 1.') + if self._next_update is not None and last_update > self._next_update: + raise ValueError( + 'The last update date must be before the next update date.' + ) + return CertificateRevocationListBuilder( + self._issuer_name, last_update, self._next_update, + self._extensions, self._revoked_certificates + ) + + def next_update(self, next_update): + if not isinstance(next_update, datetime.datetime): + raise TypeError('Expecting datetime object.') + if self._next_update is not None: + raise ValueError('Last update may only be set once.') + next_update = _convert_to_naive_utc_time(next_update) + if next_update < _EARLIEST_UTC_TIME: + raise ValueError('The last update date must be on or after' + ' 1950 January 1.') + if self._last_update is not None and next_update < self._last_update: + raise ValueError( + 'The next update date must be after the last update date.' + ) + return CertificateRevocationListBuilder( + self._issuer_name, self._last_update, next_update, + self._extensions, self._revoked_certificates + ) + + def add_extension(self, extension, critical): + """ + Adds an X.509 extension to the certificate revocation list. + """ + if not isinstance(extension, ExtensionType): + raise TypeError("extension must be an ExtensionType") + + extension = Extension(extension.oid, critical, extension) + _reject_duplicate_extension(extension, self._extensions) + return CertificateRevocationListBuilder( + self._issuer_name, self._last_update, self._next_update, + self._extensions + [extension], self._revoked_certificates + ) + + def add_revoked_certificate(self, revoked_certificate): + """ + Adds a revoked certificate to the CRL. + """ + if not isinstance(revoked_certificate, RevokedCertificate): + raise TypeError("Must be an instance of RevokedCertificate") + + return CertificateRevocationListBuilder( + self._issuer_name, self._last_update, + self._next_update, self._extensions, + self._revoked_certificates + [revoked_certificate] + ) + + def sign(self, private_key, algorithm, backend): + if self._issuer_name is None: + raise ValueError("A CRL must have an issuer name") + + if self._last_update is None: + raise ValueError("A CRL must have a last update time") + + if self._next_update is None: + raise ValueError("A CRL must have a next update time") + + return backend.create_x509_crl(self, private_key, algorithm) + + +class RevokedCertificateBuilder(object): + def __init__(self, serial_number=None, revocation_date=None, + extensions=[]): + self._serial_number = serial_number + self._revocation_date = revocation_date + self._extensions = extensions + + def serial_number(self, number): + if not isinstance(number, six.integer_types): + raise TypeError('Serial number must be of integral type.') + if self._serial_number is not None: + raise ValueError('The serial number may only be set once.') + if number <= 0: + raise ValueError('The serial number should be positive') + + # ASN.1 integers are always signed, so most significant bit must be + # zero. + if number.bit_length() >= 160: # As defined in RFC 5280 + raise ValueError('The serial number should not be more than 159 ' + 'bits.') + return RevokedCertificateBuilder( + number, self._revocation_date, self._extensions + ) + + def revocation_date(self, time): + if not isinstance(time, datetime.datetime): + raise TypeError('Expecting datetime object.') + if self._revocation_date is not None: + raise ValueError('The revocation date may only be set once.') + time = _convert_to_naive_utc_time(time) + if time < _EARLIEST_UTC_TIME: + raise ValueError('The revocation date must be on or after' + ' 1950 January 1.') + return RevokedCertificateBuilder( + self._serial_number, time, self._extensions + ) + + def add_extension(self, extension, critical): + if not isinstance(extension, ExtensionType): + raise TypeError("extension must be an ExtensionType") + + extension = Extension(extension.oid, critical, extension) + _reject_duplicate_extension(extension, self._extensions) + return RevokedCertificateBuilder( + self._serial_number, self._revocation_date, + self._extensions + [extension] + ) + + def build(self, backend): + if self._serial_number is None: + raise ValueError("A revoked certificate must have a serial number") + if self._revocation_date is None: + raise ValueError( + "A revoked certificate must have a revocation date" + ) + + return backend.create_x509_revoked_certificate(self) + + +def random_serial_number(): + return utils.int_from_bytes(os.urandom(20), "big") >> 1 diff --git a/src/cryptography/x509/certificate_transparency.py b/src/cryptography/x509/certificate_transparency.py new file mode 100644 index 00000000..d00fe812 --- /dev/null +++ b/src/cryptography/x509/certificate_transparency.py @@ -0,0 +1,46 @@ +# 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 +from enum import Enum + +import six + + +class LogEntryType(Enum): + X509_CERTIFICATE = 0 + PRE_CERTIFICATE = 1 + + +class Version(Enum): + v1 = 0 + + +@six.add_metaclass(abc.ABCMeta) +class SignedCertificateTimestamp(object): + @abc.abstractproperty + def version(self): + """ + Returns the SCT version. + """ + + @abc.abstractproperty + def log_id(self): + """ + Returns an identifier indicating which log this SCT is for. + """ + + @abc.abstractproperty + def timestamp(self): + """ + Returns the timestamp for this SCT. + """ + + @abc.abstractproperty + def entry_type(self): + """ + Returns whether this is an SCT for a certificate or pre-certificate. + """ diff --git a/src/cryptography/x509/extensions.py b/src/cryptography/x509/extensions.py new file mode 100644 index 00000000..1b96ffd7 --- /dev/null +++ b/src/cryptography/x509/extensions.py @@ -0,0 +1,1595 @@ +# 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 datetime +import hashlib +import ipaddress +from enum import Enum + +import six + +from cryptography import utils +from cryptography.hazmat._der import ( + BIT_STRING, DERReader, OBJECT_IDENTIFIER, SEQUENCE +) +from cryptography.hazmat.primitives import constant_time, serialization +from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurvePublicKey +from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey +from cryptography.x509.certificate_transparency import ( + SignedCertificateTimestamp +) +from cryptography.x509.general_name import GeneralName, IPAddress, OtherName +from cryptography.x509.name import RelativeDistinguishedName +from cryptography.x509.oid import ( + CRLEntryExtensionOID, ExtensionOID, OCSPExtensionOID, ObjectIdentifier, +) + + +def _key_identifier_from_public_key(public_key): + if isinstance(public_key, RSAPublicKey): + data = public_key.public_bytes( + serialization.Encoding.DER, + serialization.PublicFormat.PKCS1, + ) + elif isinstance(public_key, EllipticCurvePublicKey): + data = public_key.public_bytes( + serialization.Encoding.X962, + serialization.PublicFormat.UncompressedPoint + ) + else: + # This is a very slow way to do this. + serialized = public_key.public_bytes( + serialization.Encoding.DER, + serialization.PublicFormat.SubjectPublicKeyInfo + ) + + reader = DERReader(serialized) + with reader.read_single_element(SEQUENCE) as public_key_info: + algorithm = public_key_info.read_element(SEQUENCE) + public_key = public_key_info.read_element(BIT_STRING) + + # Double-check the algorithm structure. + with algorithm: + algorithm.read_element(OBJECT_IDENTIFIER) + if not algorithm.is_empty(): + # Skip the optional parameters field. + algorithm.read_any_element() + + # BIT STRING contents begin with the number of padding bytes added. It + # must be zero for SubjectPublicKeyInfo structures. + if public_key.read_byte() != 0: + raise ValueError('Invalid public key encoding') + + data = public_key.data + + return hashlib.sha1(data).digest() + + +def _make_sequence_methods(field_name): + def len_method(self): + return len(getattr(self, field_name)) + + def iter_method(self): + return iter(getattr(self, field_name)) + + def getitem_method(self, idx): + return getattr(self, field_name)[idx] + + return len_method, iter_method, getitem_method + + +class DuplicateExtension(Exception): + def __init__(self, msg, oid): + super(DuplicateExtension, self).__init__(msg) + self.oid = oid + + +class ExtensionNotFound(Exception): + def __init__(self, msg, oid): + super(ExtensionNotFound, self).__init__(msg) + self.oid = oid + + +@six.add_metaclass(abc.ABCMeta) +class ExtensionType(object): + @abc.abstractproperty + def oid(self): + """ + Returns the oid associated with the given extension type. + """ + + +class Extensions(object): + def __init__(self, extensions): + self._extensions = extensions + + def get_extension_for_oid(self, oid): + for ext in self: + if ext.oid == oid: + return ext + + raise ExtensionNotFound("No {} extension was found".format(oid), oid) + + def get_extension_for_class(self, extclass): + if extclass is UnrecognizedExtension: + raise TypeError( + "UnrecognizedExtension can't be used with " + "get_extension_for_class because more than one instance of the" + " class may be present." + ) + + for ext in self: + if isinstance(ext.value, extclass): + return ext + + raise ExtensionNotFound( + "No {} extension was found".format(extclass), extclass.oid + ) + + __len__, __iter__, __getitem__ = _make_sequence_methods("_extensions") + + def __repr__(self): + return ( + "<Extensions({})>".format(self._extensions) + ) + + +@utils.register_interface(ExtensionType) +class CRLNumber(object): + oid = ExtensionOID.CRL_NUMBER + + def __init__(self, crl_number): + if not isinstance(crl_number, six.integer_types): + raise TypeError("crl_number must be an integer") + + self._crl_number = crl_number + + def __eq__(self, other): + if not isinstance(other, CRLNumber): + return NotImplemented + + return self.crl_number == other.crl_number + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash(self.crl_number) + + def __repr__(self): + return "<CRLNumber({})>".format(self.crl_number) + + crl_number = utils.read_only_property("_crl_number") + + +@utils.register_interface(ExtensionType) +class AuthorityKeyIdentifier(object): + oid = ExtensionOID.AUTHORITY_KEY_IDENTIFIER + + def __init__(self, key_identifier, authority_cert_issuer, + authority_cert_serial_number): + if (authority_cert_issuer is None) != ( + authority_cert_serial_number is None + ): + raise ValueError( + "authority_cert_issuer and authority_cert_serial_number " + "must both be present or both None" + ) + + if authority_cert_issuer is not None: + authority_cert_issuer = list(authority_cert_issuer) + if not all( + isinstance(x, GeneralName) for x in authority_cert_issuer + ): + raise TypeError( + "authority_cert_issuer must be a list of GeneralName " + "objects" + ) + + if authority_cert_serial_number is not None and not isinstance( + authority_cert_serial_number, six.integer_types + ): + raise TypeError( + "authority_cert_serial_number must be an integer" + ) + + self._key_identifier = key_identifier + self._authority_cert_issuer = authority_cert_issuer + self._authority_cert_serial_number = authority_cert_serial_number + + @classmethod + def from_issuer_public_key(cls, public_key): + digest = _key_identifier_from_public_key(public_key) + return cls( + key_identifier=digest, + authority_cert_issuer=None, + authority_cert_serial_number=None + ) + + @classmethod + def from_issuer_subject_key_identifier(cls, ski): + return cls( + key_identifier=ski.digest, + authority_cert_issuer=None, + authority_cert_serial_number=None + ) + + def __repr__(self): + return ( + "<AuthorityKeyIdentifier(key_identifier={0.key_identifier!r}, " + "authority_cert_issuer={0.authority_cert_issuer}, " + "authority_cert_serial_number={0.authority_cert_serial_number}" + ")>".format(self) + ) + + def __eq__(self, other): + if not isinstance(other, AuthorityKeyIdentifier): + return NotImplemented + + return ( + self.key_identifier == other.key_identifier and + self.authority_cert_issuer == other.authority_cert_issuer and + self.authority_cert_serial_number == + other.authority_cert_serial_number + ) + + def __ne__(self, other): + return not self == other + + def __hash__(self): + if self.authority_cert_issuer is None: + aci = None + else: + aci = tuple(self.authority_cert_issuer) + return hash(( + self.key_identifier, aci, self.authority_cert_serial_number + )) + + key_identifier = utils.read_only_property("_key_identifier") + authority_cert_issuer = utils.read_only_property("_authority_cert_issuer") + authority_cert_serial_number = utils.read_only_property( + "_authority_cert_serial_number" + ) + + +@utils.register_interface(ExtensionType) +class SubjectKeyIdentifier(object): + oid = ExtensionOID.SUBJECT_KEY_IDENTIFIER + + def __init__(self, digest): + self._digest = digest + + @classmethod + def from_public_key(cls, public_key): + return cls(_key_identifier_from_public_key(public_key)) + + digest = utils.read_only_property("_digest") + + def __repr__(self): + return "<SubjectKeyIdentifier(digest={0!r})>".format(self.digest) + + def __eq__(self, other): + if not isinstance(other, SubjectKeyIdentifier): + return NotImplemented + + return constant_time.bytes_eq(self.digest, other.digest) + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash(self.digest) + + +@utils.register_interface(ExtensionType) +class AuthorityInformationAccess(object): + oid = ExtensionOID.AUTHORITY_INFORMATION_ACCESS + + def __init__(self, descriptions): + descriptions = list(descriptions) + if not all(isinstance(x, AccessDescription) for x in descriptions): + raise TypeError( + "Every item in the descriptions list must be an " + "AccessDescription" + ) + + self._descriptions = descriptions + + __len__, __iter__, __getitem__ = _make_sequence_methods("_descriptions") + + def __repr__(self): + return "<AuthorityInformationAccess({})>".format(self._descriptions) + + def __eq__(self, other): + if not isinstance(other, AuthorityInformationAccess): + return NotImplemented + + return self._descriptions == other._descriptions + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash(tuple(self._descriptions)) + + +class AccessDescription(object): + def __init__(self, access_method, access_location): + if not isinstance(access_method, ObjectIdentifier): + raise TypeError("access_method must be an ObjectIdentifier") + + if not isinstance(access_location, GeneralName): + raise TypeError("access_location must be a GeneralName") + + self._access_method = access_method + self._access_location = access_location + + def __repr__(self): + return ( + "<AccessDescription(access_method={0.access_method}, access_locati" + "on={0.access_location})>".format(self) + ) + + def __eq__(self, other): + if not isinstance(other, AccessDescription): + return NotImplemented + + return ( + self.access_method == other.access_method and + self.access_location == other.access_location + ) + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash((self.access_method, self.access_location)) + + access_method = utils.read_only_property("_access_method") + access_location = utils.read_only_property("_access_location") + + +@utils.register_interface(ExtensionType) +class BasicConstraints(object): + oid = ExtensionOID.BASIC_CONSTRAINTS + + def __init__(self, ca, path_length): + if not isinstance(ca, bool): + raise TypeError("ca must be a boolean value") + + if path_length is not None and not ca: + raise ValueError("path_length must be None when ca is False") + + if ( + path_length is not None and + (not isinstance(path_length, six.integer_types) or path_length < 0) + ): + raise TypeError( + "path_length must be a non-negative integer or None" + ) + + self._ca = ca + self._path_length = path_length + + ca = utils.read_only_property("_ca") + path_length = utils.read_only_property("_path_length") + + def __repr__(self): + return ("<BasicConstraints(ca={0.ca}, " + "path_length={0.path_length})>").format(self) + + def __eq__(self, other): + if not isinstance(other, BasicConstraints): + return NotImplemented + + return self.ca == other.ca and self.path_length == other.path_length + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash((self.ca, self.path_length)) + + +@utils.register_interface(ExtensionType) +class DeltaCRLIndicator(object): + oid = ExtensionOID.DELTA_CRL_INDICATOR + + def __init__(self, crl_number): + if not isinstance(crl_number, six.integer_types): + raise TypeError("crl_number must be an integer") + + self._crl_number = crl_number + + crl_number = utils.read_only_property("_crl_number") + + def __eq__(self, other): + if not isinstance(other, DeltaCRLIndicator): + return NotImplemented + + return self.crl_number == other.crl_number + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash(self.crl_number) + + def __repr__(self): + return "<DeltaCRLIndicator(crl_number={0.crl_number})>".format(self) + + +@utils.register_interface(ExtensionType) +class CRLDistributionPoints(object): + oid = ExtensionOID.CRL_DISTRIBUTION_POINTS + + def __init__(self, distribution_points): + distribution_points = list(distribution_points) + if not all( + isinstance(x, DistributionPoint) for x in distribution_points + ): + raise TypeError( + "distribution_points must be a list of DistributionPoint " + "objects" + ) + + self._distribution_points = distribution_points + + __len__, __iter__, __getitem__ = _make_sequence_methods( + "_distribution_points" + ) + + def __repr__(self): + return "<CRLDistributionPoints({})>".format(self._distribution_points) + + def __eq__(self, other): + if not isinstance(other, CRLDistributionPoints): + return NotImplemented + + return self._distribution_points == other._distribution_points + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash(tuple(self._distribution_points)) + + +@utils.register_interface(ExtensionType) +class FreshestCRL(object): + oid = ExtensionOID.FRESHEST_CRL + + def __init__(self, distribution_points): + distribution_points = list(distribution_points) + if not all( + isinstance(x, DistributionPoint) for x in distribution_points + ): + raise TypeError( + "distribution_points must be a list of DistributionPoint " + "objects" + ) + + self._distribution_points = distribution_points + + __len__, __iter__, __getitem__ = _make_sequence_methods( + "_distribution_points" + ) + + def __repr__(self): + return "<FreshestCRL({})>".format(self._distribution_points) + + def __eq__(self, other): + if not isinstance(other, FreshestCRL): + return NotImplemented + + return self._distribution_points == other._distribution_points + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash(tuple(self._distribution_points)) + + +class DistributionPoint(object): + def __init__(self, full_name, relative_name, reasons, crl_issuer): + if full_name and relative_name: + raise ValueError( + "You cannot provide both full_name and relative_name, at " + "least one must be None." + ) + + if full_name: + full_name = list(full_name) + if not all(isinstance(x, GeneralName) for x in full_name): + raise TypeError( + "full_name must be a list of GeneralName objects" + ) + + if relative_name: + if not isinstance(relative_name, RelativeDistinguishedName): + raise TypeError( + "relative_name must be a RelativeDistinguishedName" + ) + + if crl_issuer: + crl_issuer = list(crl_issuer) + if not all(isinstance(x, GeneralName) for x in crl_issuer): + raise TypeError( + "crl_issuer must be None or a list of general names" + ) + + if reasons and (not isinstance(reasons, frozenset) or not all( + isinstance(x, ReasonFlags) for x in reasons + )): + raise TypeError("reasons must be None or frozenset of ReasonFlags") + + if reasons and ( + ReasonFlags.unspecified in reasons or + ReasonFlags.remove_from_crl in reasons + ): + raise ValueError( + "unspecified and remove_from_crl are not valid reasons in a " + "DistributionPoint" + ) + + if reasons and not crl_issuer and not (full_name or relative_name): + raise ValueError( + "You must supply crl_issuer, full_name, or relative_name when " + "reasons is not None" + ) + + self._full_name = full_name + self._relative_name = relative_name + self._reasons = reasons + self._crl_issuer = crl_issuer + + def __repr__(self): + return ( + "<DistributionPoint(full_name={0.full_name}, relative_name={0.rela" + "tive_name}, reasons={0.reasons}, crl_issuer={0.crl_issuer})>" + .format(self) + ) + + def __eq__(self, other): + if not isinstance(other, DistributionPoint): + return NotImplemented + + return ( + self.full_name == other.full_name and + self.relative_name == other.relative_name and + self.reasons == other.reasons and + self.crl_issuer == other.crl_issuer + ) + + def __ne__(self, other): + return not self == other + + def __hash__(self): + if self.full_name is not None: + fn = tuple(self.full_name) + else: + fn = None + + if self.crl_issuer is not None: + crl_issuer = tuple(self.crl_issuer) + else: + crl_issuer = None + + return hash((fn, self.relative_name, self.reasons, crl_issuer)) + + full_name = utils.read_only_property("_full_name") + relative_name = utils.read_only_property("_relative_name") + reasons = utils.read_only_property("_reasons") + crl_issuer = utils.read_only_property("_crl_issuer") + + +class ReasonFlags(Enum): + unspecified = "unspecified" + key_compromise = "keyCompromise" + ca_compromise = "cACompromise" + affiliation_changed = "affiliationChanged" + superseded = "superseded" + cessation_of_operation = "cessationOfOperation" + certificate_hold = "certificateHold" + privilege_withdrawn = "privilegeWithdrawn" + aa_compromise = "aACompromise" + remove_from_crl = "removeFromCRL" + + +@utils.register_interface(ExtensionType) +class PolicyConstraints(object): + oid = ExtensionOID.POLICY_CONSTRAINTS + + def __init__(self, require_explicit_policy, inhibit_policy_mapping): + if require_explicit_policy is not None and not isinstance( + require_explicit_policy, six.integer_types + ): + raise TypeError( + "require_explicit_policy must be a non-negative integer or " + "None" + ) + + if inhibit_policy_mapping is not None and not isinstance( + inhibit_policy_mapping, six.integer_types + ): + raise TypeError( + "inhibit_policy_mapping must be a non-negative integer or None" + ) + + if inhibit_policy_mapping is None and require_explicit_policy is None: + raise ValueError( + "At least one of require_explicit_policy and " + "inhibit_policy_mapping must not be None" + ) + + self._require_explicit_policy = require_explicit_policy + self._inhibit_policy_mapping = inhibit_policy_mapping + + def __repr__(self): + return ( + u"<PolicyConstraints(require_explicit_policy={0.require_explicit" + u"_policy}, inhibit_policy_mapping={0.inhibit_policy_" + u"mapping})>".format(self) + ) + + def __eq__(self, other): + if not isinstance(other, PolicyConstraints): + return NotImplemented + + return ( + self.require_explicit_policy == other.require_explicit_policy and + self.inhibit_policy_mapping == other.inhibit_policy_mapping + ) + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash( + (self.require_explicit_policy, self.inhibit_policy_mapping) + ) + + require_explicit_policy = utils.read_only_property( + "_require_explicit_policy" + ) + inhibit_policy_mapping = utils.read_only_property( + "_inhibit_policy_mapping" + ) + + +@utils.register_interface(ExtensionType) +class CertificatePolicies(object): + oid = ExtensionOID.CERTIFICATE_POLICIES + + def __init__(self, policies): + policies = list(policies) + if not all(isinstance(x, PolicyInformation) for x in policies): + raise TypeError( + "Every item in the policies list must be a " + "PolicyInformation" + ) + + self._policies = policies + + __len__, __iter__, __getitem__ = _make_sequence_methods("_policies") + + def __repr__(self): + return "<CertificatePolicies({})>".format(self._policies) + + def __eq__(self, other): + if not isinstance(other, CertificatePolicies): + return NotImplemented + + return self._policies == other._policies + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash(tuple(self._policies)) + + +class PolicyInformation(object): + def __init__(self, policy_identifier, policy_qualifiers): + if not isinstance(policy_identifier, ObjectIdentifier): + raise TypeError("policy_identifier must be an ObjectIdentifier") + + self._policy_identifier = policy_identifier + + if policy_qualifiers: + policy_qualifiers = list(policy_qualifiers) + if not all( + isinstance(x, (six.text_type, UserNotice)) + for x in policy_qualifiers + ): + raise TypeError( + "policy_qualifiers must be a list of strings and/or " + "UserNotice objects or None" + ) + + self._policy_qualifiers = policy_qualifiers + + def __repr__(self): + return ( + "<PolicyInformation(policy_identifier={0.policy_identifier}, polic" + "y_qualifiers={0.policy_qualifiers})>".format(self) + ) + + def __eq__(self, other): + if not isinstance(other, PolicyInformation): + return NotImplemented + + return ( + self.policy_identifier == other.policy_identifier and + self.policy_qualifiers == other.policy_qualifiers + ) + + def __ne__(self, other): + return not self == other + + def __hash__(self): + if self.policy_qualifiers is not None: + pq = tuple(self.policy_qualifiers) + else: + pq = None + + return hash((self.policy_identifier, pq)) + + policy_identifier = utils.read_only_property("_policy_identifier") + policy_qualifiers = utils.read_only_property("_policy_qualifiers") + + +class UserNotice(object): + def __init__(self, notice_reference, explicit_text): + if notice_reference and not isinstance( + notice_reference, NoticeReference + ): + raise TypeError( + "notice_reference must be None or a NoticeReference" + ) + + self._notice_reference = notice_reference + self._explicit_text = explicit_text + + def __repr__(self): + return ( + "<UserNotice(notice_reference={0.notice_reference}, explicit_text=" + "{0.explicit_text!r})>".format(self) + ) + + def __eq__(self, other): + if not isinstance(other, UserNotice): + return NotImplemented + + return ( + self.notice_reference == other.notice_reference and + self.explicit_text == other.explicit_text + ) + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash((self.notice_reference, self.explicit_text)) + + notice_reference = utils.read_only_property("_notice_reference") + explicit_text = utils.read_only_property("_explicit_text") + + +class NoticeReference(object): + def __init__(self, organization, notice_numbers): + self._organization = organization + notice_numbers = list(notice_numbers) + if not all(isinstance(x, int) for x in notice_numbers): + raise TypeError( + "notice_numbers must be a list of integers" + ) + + self._notice_numbers = notice_numbers + + def __repr__(self): + return ( + "<NoticeReference(organization={0.organization!r}, notice_numbers=" + "{0.notice_numbers})>".format(self) + ) + + def __eq__(self, other): + if not isinstance(other, NoticeReference): + return NotImplemented + + return ( + self.organization == other.organization and + self.notice_numbers == other.notice_numbers + ) + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash((self.organization, tuple(self.notice_numbers))) + + organization = utils.read_only_property("_organization") + notice_numbers = utils.read_only_property("_notice_numbers") + + +@utils.register_interface(ExtensionType) +class ExtendedKeyUsage(object): + oid = ExtensionOID.EXTENDED_KEY_USAGE + + def __init__(self, usages): + usages = list(usages) + if not all(isinstance(x, ObjectIdentifier) for x in usages): + raise TypeError( + "Every item in the usages list must be an ObjectIdentifier" + ) + + self._usages = usages + + __len__, __iter__, __getitem__ = _make_sequence_methods("_usages") + + def __repr__(self): + return "<ExtendedKeyUsage({})>".format(self._usages) + + def __eq__(self, other): + if not isinstance(other, ExtendedKeyUsage): + return NotImplemented + + return self._usages == other._usages + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash(tuple(self._usages)) + + +@utils.register_interface(ExtensionType) +class OCSPNoCheck(object): + oid = ExtensionOID.OCSP_NO_CHECK + + def __eq__(self, other): + if not isinstance(other, OCSPNoCheck): + return NotImplemented + + return True + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash(OCSPNoCheck) + + def __repr__(self): + return "<OCSPNoCheck()>" + + +@utils.register_interface(ExtensionType) +class PrecertPoison(object): + oid = ExtensionOID.PRECERT_POISON + + def __eq__(self, other): + if not isinstance(other, PrecertPoison): + return NotImplemented + + return True + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash(PrecertPoison) + + def __repr__(self): + return "<PrecertPoison()>" + + +@utils.register_interface(ExtensionType) +class TLSFeature(object): + oid = ExtensionOID.TLS_FEATURE + + def __init__(self, features): + features = list(features) + if ( + not all(isinstance(x, TLSFeatureType) for x in features) or + len(features) == 0 + ): + raise TypeError( + "features must be a list of elements from the TLSFeatureType " + "enum" + ) + + self._features = features + + __len__, __iter__, __getitem__ = _make_sequence_methods("_features") + + def __repr__(self): + return "<TLSFeature(features={0._features})>".format(self) + + def __eq__(self, other): + if not isinstance(other, TLSFeature): + return NotImplemented + + return self._features == other._features + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash(tuple(self._features)) + + +class TLSFeatureType(Enum): + # status_request is defined in RFC 6066 and is used for what is commonly + # called OCSP Must-Staple when present in the TLS Feature extension in an + # X.509 certificate. + status_request = 5 + # status_request_v2 is defined in RFC 6961 and allows multiple OCSP + # responses to be provided. It is not currently in use by clients or + # servers. + status_request_v2 = 17 + + +_TLS_FEATURE_TYPE_TO_ENUM = {x.value: x for x in TLSFeatureType} + + +@utils.register_interface(ExtensionType) +class InhibitAnyPolicy(object): + oid = ExtensionOID.INHIBIT_ANY_POLICY + + def __init__(self, skip_certs): + if not isinstance(skip_certs, six.integer_types): + raise TypeError("skip_certs must be an integer") + + if skip_certs < 0: + raise ValueError("skip_certs must be a non-negative integer") + + self._skip_certs = skip_certs + + def __repr__(self): + return "<InhibitAnyPolicy(skip_certs={0.skip_certs})>".format(self) + + def __eq__(self, other): + if not isinstance(other, InhibitAnyPolicy): + return NotImplemented + + return self.skip_certs == other.skip_certs + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash(self.skip_certs) + + skip_certs = utils.read_only_property("_skip_certs") + + +@utils.register_interface(ExtensionType) +class KeyUsage(object): + oid = ExtensionOID.KEY_USAGE + + def __init__(self, digital_signature, content_commitment, key_encipherment, + data_encipherment, key_agreement, key_cert_sign, crl_sign, + encipher_only, decipher_only): + if not key_agreement and (encipher_only or decipher_only): + raise ValueError( + "encipher_only and decipher_only can only be true when " + "key_agreement is true" + ) + + self._digital_signature = digital_signature + self._content_commitment = content_commitment + self._key_encipherment = key_encipherment + self._data_encipherment = data_encipherment + self._key_agreement = key_agreement + self._key_cert_sign = key_cert_sign + self._crl_sign = crl_sign + self._encipher_only = encipher_only + self._decipher_only = decipher_only + + digital_signature = utils.read_only_property("_digital_signature") + content_commitment = utils.read_only_property("_content_commitment") + key_encipherment = utils.read_only_property("_key_encipherment") + data_encipherment = utils.read_only_property("_data_encipherment") + key_agreement = utils.read_only_property("_key_agreement") + key_cert_sign = utils.read_only_property("_key_cert_sign") + crl_sign = utils.read_only_property("_crl_sign") + + @property + def encipher_only(self): + if not self.key_agreement: + raise ValueError( + "encipher_only is undefined unless key_agreement is true" + ) + else: + return self._encipher_only + + @property + def decipher_only(self): + if not self.key_agreement: + raise ValueError( + "decipher_only is undefined unless key_agreement is true" + ) + else: + return self._decipher_only + + def __repr__(self): + try: + encipher_only = self.encipher_only + decipher_only = self.decipher_only + except ValueError: + encipher_only = None + decipher_only = None + + return ("<KeyUsage(digital_signature={0.digital_signature}, " + "content_commitment={0.content_commitment}, " + "key_encipherment={0.key_encipherment}, " + "data_encipherment={0.data_encipherment}, " + "key_agreement={0.key_agreement}, " + "key_cert_sign={0.key_cert_sign}, crl_sign={0.crl_sign}, " + "encipher_only={1}, decipher_only={2})>").format( + self, encipher_only, decipher_only) + + def __eq__(self, other): + if not isinstance(other, KeyUsage): + return NotImplemented + + return ( + self.digital_signature == other.digital_signature and + self.content_commitment == other.content_commitment and + self.key_encipherment == other.key_encipherment and + self.data_encipherment == other.data_encipherment and + self.key_agreement == other.key_agreement and + self.key_cert_sign == other.key_cert_sign and + self.crl_sign == other.crl_sign and + self._encipher_only == other._encipher_only and + self._decipher_only == other._decipher_only + ) + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash(( + self.digital_signature, self.content_commitment, + self.key_encipherment, self.data_encipherment, + self.key_agreement, self.key_cert_sign, + self.crl_sign, self._encipher_only, + self._decipher_only + )) + + +@utils.register_interface(ExtensionType) +class NameConstraints(object): + oid = ExtensionOID.NAME_CONSTRAINTS + + def __init__(self, permitted_subtrees, excluded_subtrees): + if permitted_subtrees is not None: + permitted_subtrees = list(permitted_subtrees) + if not all( + isinstance(x, GeneralName) for x in permitted_subtrees + ): + raise TypeError( + "permitted_subtrees must be a list of GeneralName objects " + "or None" + ) + + self._validate_ip_name(permitted_subtrees) + + if excluded_subtrees is not None: + excluded_subtrees = list(excluded_subtrees) + if not all( + isinstance(x, GeneralName) for x in excluded_subtrees + ): + raise TypeError( + "excluded_subtrees must be a list of GeneralName objects " + "or None" + ) + + self._validate_ip_name(excluded_subtrees) + + if permitted_subtrees is None and excluded_subtrees is None: + raise ValueError( + "At least one of permitted_subtrees and excluded_subtrees " + "must not be None" + ) + + self._permitted_subtrees = permitted_subtrees + self._excluded_subtrees = excluded_subtrees + + def __eq__(self, other): + if not isinstance(other, NameConstraints): + return NotImplemented + + return ( + self.excluded_subtrees == other.excluded_subtrees and + self.permitted_subtrees == other.permitted_subtrees + ) + + def __ne__(self, other): + return not self == other + + def _validate_ip_name(self, tree): + if any(isinstance(name, IPAddress) and not isinstance( + name.value, (ipaddress.IPv4Network, ipaddress.IPv6Network) + ) for name in tree): + raise TypeError( + "IPAddress name constraints must be an IPv4Network or" + " IPv6Network object" + ) + + def __repr__(self): + return ( + u"<NameConstraints(permitted_subtrees={0.permitted_subtrees}, " + u"excluded_subtrees={0.excluded_subtrees})>".format(self) + ) + + def __hash__(self): + if self.permitted_subtrees is not None: + ps = tuple(self.permitted_subtrees) + else: + ps = None + + if self.excluded_subtrees is not None: + es = tuple(self.excluded_subtrees) + else: + es = None + + return hash((ps, es)) + + permitted_subtrees = utils.read_only_property("_permitted_subtrees") + excluded_subtrees = utils.read_only_property("_excluded_subtrees") + + +class Extension(object): + def __init__(self, oid, critical, value): + if not isinstance(oid, ObjectIdentifier): + raise TypeError( + "oid argument must be an ObjectIdentifier instance." + ) + + if not isinstance(critical, bool): + raise TypeError("critical must be a boolean value") + + self._oid = oid + self._critical = critical + self._value = value + + oid = utils.read_only_property("_oid") + critical = utils.read_only_property("_critical") + value = utils.read_only_property("_value") + + def __repr__(self): + return ("<Extension(oid={0.oid}, critical={0.critical}, " + "value={0.value})>").format(self) + + def __eq__(self, other): + if not isinstance(other, Extension): + return NotImplemented + + return ( + self.oid == other.oid and + self.critical == other.critical and + self.value == other.value + ) + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash((self.oid, self.critical, self.value)) + + +class GeneralNames(object): + def __init__(self, general_names): + general_names = list(general_names) + if not all(isinstance(x, GeneralName) for x in general_names): + raise TypeError( + "Every item in the general_names list must be an " + "object conforming to the GeneralName interface" + ) + + self._general_names = general_names + __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names") + + def get_values_for_type(self, type): + # Return the value of each GeneralName, except for OtherName instances + # which we return directly because it has two important properties not + # just one value. + objs = (i for i in self if isinstance(i, type)) + if type != OtherName: + objs = (i.value for i in objs) + return list(objs) + + def __repr__(self): + return "<GeneralNames({})>".format(self._general_names) + + def __eq__(self, other): + if not isinstance(other, GeneralNames): + return NotImplemented + + return self._general_names == other._general_names + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash(tuple(self._general_names)) + + +@utils.register_interface(ExtensionType) +class SubjectAlternativeName(object): + oid = ExtensionOID.SUBJECT_ALTERNATIVE_NAME + + def __init__(self, general_names): + self._general_names = GeneralNames(general_names) + + __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names") + + def get_values_for_type(self, type): + return self._general_names.get_values_for_type(type) + + def __repr__(self): + return "<SubjectAlternativeName({})>".format(self._general_names) + + def __eq__(self, other): + if not isinstance(other, SubjectAlternativeName): + return NotImplemented + + return self._general_names == other._general_names + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash(self._general_names) + + +@utils.register_interface(ExtensionType) +class IssuerAlternativeName(object): + oid = ExtensionOID.ISSUER_ALTERNATIVE_NAME + + def __init__(self, general_names): + self._general_names = GeneralNames(general_names) + + __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names") + + def get_values_for_type(self, type): + return self._general_names.get_values_for_type(type) + + def __repr__(self): + return "<IssuerAlternativeName({})>".format(self._general_names) + + def __eq__(self, other): + if not isinstance(other, IssuerAlternativeName): + return NotImplemented + + return self._general_names == other._general_names + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash(self._general_names) + + +@utils.register_interface(ExtensionType) +class CertificateIssuer(object): + oid = CRLEntryExtensionOID.CERTIFICATE_ISSUER + + def __init__(self, general_names): + self._general_names = GeneralNames(general_names) + + __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names") + + def get_values_for_type(self, type): + return self._general_names.get_values_for_type(type) + + def __repr__(self): + return "<CertificateIssuer({})>".format(self._general_names) + + def __eq__(self, other): + if not isinstance(other, CertificateIssuer): + return NotImplemented + + return self._general_names == other._general_names + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash(self._general_names) + + +@utils.register_interface(ExtensionType) +class CRLReason(object): + oid = CRLEntryExtensionOID.CRL_REASON + + def __init__(self, reason): + if not isinstance(reason, ReasonFlags): + raise TypeError("reason must be an element from ReasonFlags") + + self._reason = reason + + def __repr__(self): + return "<CRLReason(reason={})>".format(self._reason) + + def __eq__(self, other): + if not isinstance(other, CRLReason): + return NotImplemented + + return self.reason == other.reason + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash(self.reason) + + reason = utils.read_only_property("_reason") + + +@utils.register_interface(ExtensionType) +class InvalidityDate(object): + oid = CRLEntryExtensionOID.INVALIDITY_DATE + + def __init__(self, invalidity_date): + if not isinstance(invalidity_date, datetime.datetime): + raise TypeError("invalidity_date must be a datetime.datetime") + + self._invalidity_date = invalidity_date + + def __repr__(self): + return "<InvalidityDate(invalidity_date={})>".format( + self._invalidity_date + ) + + def __eq__(self, other): + if not isinstance(other, InvalidityDate): + return NotImplemented + + return self.invalidity_date == other.invalidity_date + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash(self.invalidity_date) + + invalidity_date = utils.read_only_property("_invalidity_date") + + +@utils.register_interface(ExtensionType) +class PrecertificateSignedCertificateTimestamps(object): + oid = ExtensionOID.PRECERT_SIGNED_CERTIFICATE_TIMESTAMPS + + def __init__(self, signed_certificate_timestamps): + signed_certificate_timestamps = list(signed_certificate_timestamps) + if not all( + isinstance(sct, SignedCertificateTimestamp) + for sct in signed_certificate_timestamps + ): + raise TypeError( + "Every item in the signed_certificate_timestamps list must be " + "a SignedCertificateTimestamp" + ) + self._signed_certificate_timestamps = signed_certificate_timestamps + + __len__, __iter__, __getitem__ = _make_sequence_methods( + "_signed_certificate_timestamps" + ) + + def __repr__(self): + return ( + "<PrecertificateSignedCertificateTimestamps({})>".format( + list(self) + ) + ) + + def __hash__(self): + return hash(tuple(self._signed_certificate_timestamps)) + + def __eq__(self, other): + if not isinstance(other, PrecertificateSignedCertificateTimestamps): + return NotImplemented + + return ( + self._signed_certificate_timestamps == + other._signed_certificate_timestamps + ) + + def __ne__(self, other): + return not self == other + + +@utils.register_interface(ExtensionType) +class OCSPNonce(object): + oid = OCSPExtensionOID.NONCE + + def __init__(self, nonce): + if not isinstance(nonce, bytes): + raise TypeError("nonce must be bytes") + + self._nonce = nonce + + def __eq__(self, other): + if not isinstance(other, OCSPNonce): + return NotImplemented + + return self.nonce == other.nonce + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash(self.nonce) + + def __repr__(self): + return "<OCSPNonce(nonce={0.nonce!r})>".format(self) + + nonce = utils.read_only_property("_nonce") + + +@utils.register_interface(ExtensionType) +class IssuingDistributionPoint(object): + oid = ExtensionOID.ISSUING_DISTRIBUTION_POINT + + def __init__(self, full_name, relative_name, only_contains_user_certs, + only_contains_ca_certs, only_some_reasons, indirect_crl, + only_contains_attribute_certs): + if ( + only_some_reasons and ( + not isinstance(only_some_reasons, frozenset) or not all( + isinstance(x, ReasonFlags) for x in only_some_reasons + ) + ) + ): + raise TypeError( + "only_some_reasons must be None or frozenset of ReasonFlags" + ) + + if only_some_reasons and ( + ReasonFlags.unspecified in only_some_reasons or + ReasonFlags.remove_from_crl in only_some_reasons + ): + raise ValueError( + "unspecified and remove_from_crl are not valid reasons in an " + "IssuingDistributionPoint" + ) + + if not ( + isinstance(only_contains_user_certs, bool) and + isinstance(only_contains_ca_certs, bool) and + isinstance(indirect_crl, bool) and + isinstance(only_contains_attribute_certs, bool) + ): + raise TypeError( + "only_contains_user_certs, only_contains_ca_certs, " + "indirect_crl and only_contains_attribute_certs " + "must all be boolean." + ) + + crl_constraints = [ + only_contains_user_certs, only_contains_ca_certs, + indirect_crl, only_contains_attribute_certs + ] + + if len([x for x in crl_constraints if x]) > 1: + raise ValueError( + "Only one of the following can be set to True: " + "only_contains_user_certs, only_contains_ca_certs, " + "indirect_crl, only_contains_attribute_certs" + ) + + if ( + not any([ + only_contains_user_certs, only_contains_ca_certs, + indirect_crl, only_contains_attribute_certs, full_name, + relative_name, only_some_reasons + ]) + ): + raise ValueError( + "Cannot create empty extension: " + "if only_contains_user_certs, only_contains_ca_certs, " + "indirect_crl, and only_contains_attribute_certs are all False" + ", then either full_name, relative_name, or only_some_reasons " + "must have a value." + ) + + self._only_contains_user_certs = only_contains_user_certs + self._only_contains_ca_certs = only_contains_ca_certs + self._indirect_crl = indirect_crl + self._only_contains_attribute_certs = only_contains_attribute_certs + self._only_some_reasons = only_some_reasons + self._full_name = full_name + self._relative_name = relative_name + + def __repr__(self): + return ( + "<IssuingDistributionPoint(full_name={0.full_name}, " + "relative_name={0.relative_name}, " + "only_contains_user_certs={0.only_contains_user_certs}, " + "only_contains_ca_certs={0.only_contains_ca_certs}, " + "only_some_reasons={0.only_some_reasons}, " + "indirect_crl={0.indirect_crl}, " + "only_contains_attribute_certs=" + "{0.only_contains_attribute_certs})>".format(self) + ) + + def __eq__(self, other): + if not isinstance(other, IssuingDistributionPoint): + return NotImplemented + + return ( + self.full_name == other.full_name and + self.relative_name == other.relative_name and + self.only_contains_user_certs == other.only_contains_user_certs and + self.only_contains_ca_certs == other.only_contains_ca_certs and + self.only_some_reasons == other.only_some_reasons and + self.indirect_crl == other.indirect_crl and + self.only_contains_attribute_certs == + other.only_contains_attribute_certs + ) + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash(( + self.full_name, + self.relative_name, + self.only_contains_user_certs, + self.only_contains_ca_certs, + self.only_some_reasons, + self.indirect_crl, + self.only_contains_attribute_certs, + )) + + full_name = utils.read_only_property("_full_name") + relative_name = utils.read_only_property("_relative_name") + only_contains_user_certs = utils.read_only_property( + "_only_contains_user_certs" + ) + only_contains_ca_certs = utils.read_only_property( + "_only_contains_ca_certs" + ) + only_some_reasons = utils.read_only_property("_only_some_reasons") + indirect_crl = utils.read_only_property("_indirect_crl") + only_contains_attribute_certs = utils.read_only_property( + "_only_contains_attribute_certs" + ) + + +@utils.register_interface(ExtensionType) +class UnrecognizedExtension(object): + def __init__(self, oid, value): + if not isinstance(oid, ObjectIdentifier): + raise TypeError("oid must be an ObjectIdentifier") + self._oid = oid + self._value = value + + oid = utils.read_only_property("_oid") + value = utils.read_only_property("_value") + + def __repr__(self): + return ( + "<UnrecognizedExtension(oid={0.oid}, value={0.value!r})>".format( + self + ) + ) + + def __eq__(self, other): + if not isinstance(other, UnrecognizedExtension): + return NotImplemented + + return self.oid == other.oid and self.value == other.value + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash((self.oid, self.value)) diff --git a/src/cryptography/x509/general_name.py b/src/cryptography/x509/general_name.py new file mode 100644 index 00000000..12338415 --- /dev/null +++ b/src/cryptography/x509/general_name.py @@ -0,0 +1,360 @@ +# 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 ipaddress +import warnings +from email.utils import parseaddr + +import six +from six.moves import urllib_parse + +from cryptography import utils +from cryptography.x509.name import Name +from cryptography.x509.oid import ObjectIdentifier + + +_GENERAL_NAMES = { + 0: "otherName", + 1: "rfc822Name", + 2: "dNSName", + 3: "x400Address", + 4: "directoryName", + 5: "ediPartyName", + 6: "uniformResourceIdentifier", + 7: "iPAddress", + 8: "registeredID", +} + + +def _lazy_import_idna(): + # Import idna lazily becase it allocates a decent amount of memory, and + # we're only using it in deprecated paths. + try: + import idna + return idna + except ImportError: + raise ImportError( + "idna is not installed, but a deprecated feature that requires it" + " was used. See: https://cryptography.io/en/latest/faq/#importe" + "rror-idna-is-not-installed" + ) + + +class UnsupportedGeneralNameType(Exception): + def __init__(self, msg, type): + super(UnsupportedGeneralNameType, self).__init__(msg) + self.type = type + + +@six.add_metaclass(abc.ABCMeta) +class GeneralName(object): + @abc.abstractproperty + def value(self): + """ + Return the value of the object + """ + + +@utils.register_interface(GeneralName) +class RFC822Name(object): + def __init__(self, value): + if isinstance(value, six.text_type): + try: + value.encode("ascii") + except UnicodeEncodeError: + value = self._idna_encode(value) + warnings.warn( + "RFC822Name values should be passed as an A-label string. " + "This means unicode characters should be encoded via " + "idna. Support for passing unicode strings (aka U-label) " + "will be removed in a future version.", + utils.PersistentlyDeprecated2017, + stacklevel=2, + ) + else: + raise TypeError("value must be string") + + name, address = parseaddr(value) + if name or not address: + # parseaddr has found a name (e.g. Name <email>) or the entire + # value is an empty string. + raise ValueError("Invalid rfc822name value") + + self._value = value + + value = utils.read_only_property("_value") + + @classmethod + def _init_without_validation(cls, value): + instance = cls.__new__(cls) + instance._value = value + return instance + + def _idna_encode(self, value): + idna = _lazy_import_idna() + _, address = parseaddr(value) + parts = address.split(u"@") + return parts[0] + "@" + idna.encode(parts[1]).decode("ascii") + + def __repr__(self): + return "<RFC822Name(value={0!r})>".format(self.value) + + def __eq__(self, other): + if not isinstance(other, RFC822Name): + return NotImplemented + + return self.value == other.value + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash(self.value) + + +def _idna_encode(value): + idna = _lazy_import_idna() + # Retain prefixes '*.' for common/alt names and '.' for name constraints + for prefix in ['*.', '.']: + if value.startswith(prefix): + value = value[len(prefix):] + return prefix + idna.encode(value).decode("ascii") + return idna.encode(value).decode("ascii") + + +@utils.register_interface(GeneralName) +class DNSName(object): + def __init__(self, value): + if isinstance(value, six.text_type): + try: + value.encode("ascii") + except UnicodeEncodeError: + value = _idna_encode(value) + warnings.warn( + "DNSName values should be passed as an A-label string. " + "This means unicode characters should be encoded via " + "idna. Support for passing unicode strings (aka U-label) " + "will be removed in a future version.", + utils.PersistentlyDeprecated2017, + stacklevel=2, + ) + else: + raise TypeError("value must be string") + + self._value = value + + value = utils.read_only_property("_value") + + @classmethod + def _init_without_validation(cls, value): + instance = cls.__new__(cls) + instance._value = value + return instance + + def __repr__(self): + return "<DNSName(value={0!r})>".format(self.value) + + def __eq__(self, other): + if not isinstance(other, DNSName): + return NotImplemented + + return self.value == other.value + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash(self.value) + + +@utils.register_interface(GeneralName) +class UniformResourceIdentifier(object): + def __init__(self, value): + if isinstance(value, six.text_type): + try: + value.encode("ascii") + except UnicodeEncodeError: + value = self._idna_encode(value) + warnings.warn( + "URI values should be passed as an A-label string. " + "This means unicode characters should be encoded via " + "idna. Support for passing unicode strings (aka U-label) " + " will be removed in a future version.", + utils.PersistentlyDeprecated2017, + stacklevel=2, + ) + else: + raise TypeError("value must be string") + + self._value = value + + value = utils.read_only_property("_value") + + @classmethod + def _init_without_validation(cls, value): + instance = cls.__new__(cls) + instance._value = value + return instance + + def _idna_encode(self, value): + idna = _lazy_import_idna() + parsed = urllib_parse.urlparse(value) + if parsed.port: + netloc = ( + idna.encode(parsed.hostname) + + ":{}".format(parsed.port).encode("ascii") + ).decode("ascii") + else: + netloc = idna.encode(parsed.hostname).decode("ascii") + + # Note that building a URL in this fashion means it should be + # semantically indistinguishable from the original but is not + # guaranteed to be exactly the same. + return urllib_parse.urlunparse(( + parsed.scheme, + netloc, + parsed.path, + parsed.params, + parsed.query, + parsed.fragment + )) + + def __repr__(self): + return "<UniformResourceIdentifier(value={0!r})>".format(self.value) + + def __eq__(self, other): + if not isinstance(other, UniformResourceIdentifier): + return NotImplemented + + return self.value == other.value + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash(self.value) + + +@utils.register_interface(GeneralName) +class DirectoryName(object): + def __init__(self, value): + if not isinstance(value, Name): + raise TypeError("value must be a Name") + + self._value = value + + value = utils.read_only_property("_value") + + def __repr__(self): + return "<DirectoryName(value={})>".format(self.value) + + def __eq__(self, other): + if not isinstance(other, DirectoryName): + return NotImplemented + + return self.value == other.value + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash(self.value) + + +@utils.register_interface(GeneralName) +class RegisteredID(object): + def __init__(self, value): + if not isinstance(value, ObjectIdentifier): + raise TypeError("value must be an ObjectIdentifier") + + self._value = value + + value = utils.read_only_property("_value") + + def __repr__(self): + return "<RegisteredID(value={})>".format(self.value) + + def __eq__(self, other): + if not isinstance(other, RegisteredID): + return NotImplemented + + return self.value == other.value + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash(self.value) + + +@utils.register_interface(GeneralName) +class IPAddress(object): + def __init__(self, value): + if not isinstance( + value, + ( + ipaddress.IPv4Address, + ipaddress.IPv6Address, + ipaddress.IPv4Network, + ipaddress.IPv6Network + ) + ): + raise TypeError( + "value must be an instance of ipaddress.IPv4Address, " + "ipaddress.IPv6Address, ipaddress.IPv4Network, or " + "ipaddress.IPv6Network" + ) + + self._value = value + + value = utils.read_only_property("_value") + + def __repr__(self): + return "<IPAddress(value={})>".format(self.value) + + def __eq__(self, other): + if not isinstance(other, IPAddress): + return NotImplemented + + return self.value == other.value + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash(self.value) + + +@utils.register_interface(GeneralName) +class OtherName(object): + def __init__(self, type_id, value): + if not isinstance(type_id, ObjectIdentifier): + raise TypeError("type_id must be an ObjectIdentifier") + if not isinstance(value, bytes): + raise TypeError("value must be a binary string") + + self._type_id = type_id + self._value = value + + type_id = utils.read_only_property("_type_id") + value = utils.read_only_property("_value") + + def __repr__(self): + return "<OtherName(type_id={}, value={!r})>".format( + self.type_id, self.value) + + def __eq__(self, other): + if not isinstance(other, OtherName): + return NotImplemented + + return self.type_id == other.type_id and self.value == other.value + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash((self.type_id, self.value)) diff --git a/src/cryptography/x509/name.py b/src/cryptography/x509/name.py new file mode 100644 index 00000000..6816e063 --- /dev/null +++ b/src/cryptography/x509/name.py @@ -0,0 +1,263 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import absolute_import, division, print_function + +from enum import Enum + +import six + +from cryptography import utils +from cryptography.x509.oid import NameOID, ObjectIdentifier + + +class _ASN1Type(Enum): + UTF8String = 12 + NumericString = 18 + PrintableString = 19 + T61String = 20 + IA5String = 22 + UTCTime = 23 + GeneralizedTime = 24 + VisibleString = 26 + UniversalString = 28 + BMPString = 30 + + +_ASN1_TYPE_TO_ENUM = {i.value: i for i in _ASN1Type} +_SENTINEL = object() +_NAMEOID_DEFAULT_TYPE = { + NameOID.COUNTRY_NAME: _ASN1Type.PrintableString, + NameOID.JURISDICTION_COUNTRY_NAME: _ASN1Type.PrintableString, + NameOID.SERIAL_NUMBER: _ASN1Type.PrintableString, + NameOID.DN_QUALIFIER: _ASN1Type.PrintableString, + NameOID.EMAIL_ADDRESS: _ASN1Type.IA5String, + NameOID.DOMAIN_COMPONENT: _ASN1Type.IA5String, +} + +#: Short attribute names from RFC 4514: +#: https://tools.ietf.org/html/rfc4514#page-7 +_NAMEOID_TO_NAME = { + NameOID.COMMON_NAME: 'CN', + NameOID.LOCALITY_NAME: 'L', + NameOID.STATE_OR_PROVINCE_NAME: 'ST', + NameOID.ORGANIZATION_NAME: 'O', + NameOID.ORGANIZATIONAL_UNIT_NAME: 'OU', + NameOID.COUNTRY_NAME: 'C', + NameOID.STREET_ADDRESS: 'STREET', + NameOID.DOMAIN_COMPONENT: 'DC', + NameOID.USER_ID: 'UID', +} + + +def _escape_dn_value(val): + """Escape special characters in RFC4514 Distinguished Name value.""" + + if not val: + return '' + + # See https://tools.ietf.org/html/rfc4514#section-2.4 + val = val.replace('\\', '\\\\') + val = val.replace('"', '\\"') + val = val.replace('+', '\\+') + val = val.replace(',', '\\,') + val = val.replace(';', '\\;') + val = val.replace('<', '\\<') + val = val.replace('>', '\\>') + val = val.replace('\0', '\\00') + + if val[0] in ('#', ' '): + val = '\\' + val + if val[-1] == ' ': + val = val[:-1] + '\\ ' + + return val + + +class NameAttribute(object): + def __init__(self, oid, value, _type=_SENTINEL): + if not isinstance(oid, ObjectIdentifier): + raise TypeError( + "oid argument must be an ObjectIdentifier instance." + ) + + if not isinstance(value, six.text_type): + raise TypeError( + "value argument must be a text type." + ) + + if ( + oid == NameOID.COUNTRY_NAME or + oid == NameOID.JURISDICTION_COUNTRY_NAME + ): + if len(value.encode("utf8")) != 2: + raise ValueError( + "Country name must be a 2 character country code" + ) + + # The appropriate ASN1 string type varies by OID and is defined across + # multiple RFCs including 2459, 3280, and 5280. In general UTF8String + # is preferred (2459), but 3280 and 5280 specify several OIDs with + # alternate types. This means when we see the sentinel value we need + # to look up whether the OID has a non-UTF8 type. If it does, set it + # to that. Otherwise, UTF8! + if _type == _SENTINEL: + _type = _NAMEOID_DEFAULT_TYPE.get(oid, _ASN1Type.UTF8String) + + if not isinstance(_type, _ASN1Type): + raise TypeError("_type must be from the _ASN1Type enum") + + self._oid = oid + self._value = value + self._type = _type + + oid = utils.read_only_property("_oid") + value = utils.read_only_property("_value") + + def rfc4514_string(self): + """ + Format as RFC4514 Distinguished Name string. + + Use short attribute name if available, otherwise fall back to OID + dotted string. + """ + key = _NAMEOID_TO_NAME.get(self.oid, self.oid.dotted_string) + return '%s=%s' % (key, _escape_dn_value(self.value)) + + def __eq__(self, other): + if not isinstance(other, NameAttribute): + return NotImplemented + + return ( + self.oid == other.oid and + self.value == other.value + ) + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash((self.oid, self.value)) + + def __repr__(self): + return "<NameAttribute(oid={0.oid}, value={0.value!r})>".format(self) + + +class RelativeDistinguishedName(object): + def __init__(self, attributes): + attributes = list(attributes) + if not attributes: + raise ValueError("a relative distinguished name cannot be empty") + if not all(isinstance(x, NameAttribute) for x in attributes): + raise TypeError("attributes must be an iterable of NameAttribute") + + # Keep list and frozenset to preserve attribute order where it matters + self._attributes = attributes + self._attribute_set = frozenset(attributes) + + if len(self._attribute_set) != len(attributes): + raise ValueError("duplicate attributes are not allowed") + + def get_attributes_for_oid(self, oid): + return [i for i in self if i.oid == oid] + + def rfc4514_string(self): + """ + Format as RFC4514 Distinguished Name string. + + Within each RDN, attributes are joined by '+', although that is rarely + used in certificates. + """ + return '+'.join(attr.rfc4514_string() for attr in self._attributes) + + def __eq__(self, other): + if not isinstance(other, RelativeDistinguishedName): + return NotImplemented + + return self._attribute_set == other._attribute_set + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash(self._attribute_set) + + def __iter__(self): + return iter(self._attributes) + + def __len__(self): + return len(self._attributes) + + def __repr__(self): + return "<RelativeDistinguishedName({})>".format(self.rfc4514_string()) + + +class Name(object): + def __init__(self, attributes): + attributes = list(attributes) + if all(isinstance(x, NameAttribute) for x in attributes): + self._attributes = [ + RelativeDistinguishedName([x]) for x in attributes + ] + elif all(isinstance(x, RelativeDistinguishedName) for x in attributes): + self._attributes = attributes + else: + raise TypeError( + "attributes must be a list of NameAttribute" + " or a list RelativeDistinguishedName" + ) + + def rfc4514_string(self): + """ + Format as RFC4514 Distinguished Name string. + For example 'CN=foobar.com,O=Foo Corp,C=US' + + An X.509 name is a two-level structure: a list of sets of attributes. + Each list element is separated by ',' and within each list element, set + elements are separated by '+'. The latter is almost never used in + real world certificates. According to RFC4514 section 2.1 the + RDNSequence must be reversed when converting to string representation. + """ + return ','.join( + attr.rfc4514_string() for attr in reversed(self._attributes)) + + def get_attributes_for_oid(self, oid): + return [i for i in self if i.oid == oid] + + @property + def rdns(self): + return self._attributes + + def public_bytes(self, backend): + return backend.x509_name_bytes(self) + + def __eq__(self, other): + if not isinstance(other, Name): + return NotImplemented + + return self._attributes == other._attributes + + def __ne__(self, other): + return not self == other + + def __hash__(self): + # TODO: this is relatively expensive, if this looks like a bottleneck + # for you, consider optimizing! + return hash(tuple(self._attributes)) + + def __iter__(self): + for rdn in self._attributes: + for ava in rdn: + yield ava + + def __len__(self): + return sum(len(rdn) for rdn in self._attributes) + + def __repr__(self): + rdns = ','.join(attr.rfc4514_string() for attr in self._attributes) + + if six.PY2: + return "<Name({})>".format(rdns.encode('utf8')) + else: + return "<Name({})>".format(rdns) diff --git a/src/cryptography/x509/ocsp.py b/src/cryptography/x509/ocsp.py new file mode 100644 index 00000000..7db92b90 --- /dev/null +++ b/src/cryptography/x509/ocsp.py @@ -0,0 +1,425 @@ +# 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 datetime +from enum import Enum + +import six + +from cryptography import x509 +from cryptography.hazmat.primitives import hashes +from cryptography.x509.base import ( + _EARLIEST_UTC_TIME, _convert_to_naive_utc_time, _reject_duplicate_extension +) + + +_OIDS_TO_HASH = { + "1.3.14.3.2.26": hashes.SHA1(), + "2.16.840.1.101.3.4.2.4": hashes.SHA224(), + "2.16.840.1.101.3.4.2.1": hashes.SHA256(), + "2.16.840.1.101.3.4.2.2": hashes.SHA384(), + "2.16.840.1.101.3.4.2.3": hashes.SHA512(), +} + + +class OCSPResponderEncoding(Enum): + HASH = "By Hash" + NAME = "By Name" + + +class OCSPResponseStatus(Enum): + SUCCESSFUL = 0 + MALFORMED_REQUEST = 1 + INTERNAL_ERROR = 2 + TRY_LATER = 3 + SIG_REQUIRED = 5 + UNAUTHORIZED = 6 + + +_RESPONSE_STATUS_TO_ENUM = {x.value: x for x in OCSPResponseStatus} +_ALLOWED_HASHES = ( + hashes.SHA1, hashes.SHA224, hashes.SHA256, + hashes.SHA384, hashes.SHA512 +) + + +def _verify_algorithm(algorithm): + if not isinstance(algorithm, _ALLOWED_HASHES): + raise ValueError( + "Algorithm must be SHA1, SHA224, SHA256, SHA384, or SHA512" + ) + + +class OCSPCertStatus(Enum): + GOOD = 0 + REVOKED = 1 + UNKNOWN = 2 + + +_CERT_STATUS_TO_ENUM = {x.value: x for x in OCSPCertStatus} + + +def load_der_ocsp_request(data): + from cryptography.hazmat.backends.openssl.backend import backend + return backend.load_der_ocsp_request(data) + + +def load_der_ocsp_response(data): + from cryptography.hazmat.backends.openssl.backend import backend + return backend.load_der_ocsp_response(data) + + +class OCSPRequestBuilder(object): + def __init__(self, request=None, extensions=[]): + self._request = request + self._extensions = extensions + + def add_certificate(self, cert, issuer, algorithm): + if self._request is not None: + raise ValueError("Only one certificate can be added to a request") + + _verify_algorithm(algorithm) + if ( + not isinstance(cert, x509.Certificate) or + not isinstance(issuer, x509.Certificate) + ): + raise TypeError("cert and issuer must be a Certificate") + + return OCSPRequestBuilder((cert, issuer, algorithm), self._extensions) + + def add_extension(self, extension, critical): + if not isinstance(extension, x509.ExtensionType): + raise TypeError("extension must be an ExtensionType") + + extension = x509.Extension(extension.oid, critical, extension) + _reject_duplicate_extension(extension, self._extensions) + + return OCSPRequestBuilder( + self._request, self._extensions + [extension] + ) + + def build(self): + from cryptography.hazmat.backends.openssl.backend import backend + if self._request is None: + raise ValueError("You must add a certificate before building") + + return backend.create_ocsp_request(self) + + +class _SingleResponse(object): + def __init__(self, cert, issuer, algorithm, cert_status, this_update, + next_update, revocation_time, revocation_reason): + if ( + not isinstance(cert, x509.Certificate) or + not isinstance(issuer, x509.Certificate) + ): + raise TypeError("cert and issuer must be a Certificate") + + _verify_algorithm(algorithm) + if not isinstance(this_update, datetime.datetime): + raise TypeError("this_update must be a datetime object") + if ( + next_update is not None and + not isinstance(next_update, datetime.datetime) + ): + raise TypeError("next_update must be a datetime object or None") + + self._cert = cert + self._issuer = issuer + self._algorithm = algorithm + self._this_update = this_update + self._next_update = next_update + + if not isinstance(cert_status, OCSPCertStatus): + raise TypeError( + "cert_status must be an item from the OCSPCertStatus enum" + ) + if cert_status is not OCSPCertStatus.REVOKED: + if revocation_time is not None: + raise ValueError( + "revocation_time can only be provided if the certificate " + "is revoked" + ) + if revocation_reason is not None: + raise ValueError( + "revocation_reason can only be provided if the certificate" + " is revoked" + ) + else: + if not isinstance(revocation_time, datetime.datetime): + raise TypeError("revocation_time must be a datetime object") + + revocation_time = _convert_to_naive_utc_time(revocation_time) + if revocation_time < _EARLIEST_UTC_TIME: + raise ValueError('The revocation_time must be on or after' + ' 1950 January 1.') + + if ( + revocation_reason is not None and + not isinstance(revocation_reason, x509.ReasonFlags) + ): + raise TypeError( + "revocation_reason must be an item from the ReasonFlags " + "enum or None" + ) + + self._cert_status = cert_status + self._revocation_time = revocation_time + self._revocation_reason = revocation_reason + + +class OCSPResponseBuilder(object): + def __init__(self, response=None, responder_id=None, certs=None, + extensions=[]): + self._response = response + self._responder_id = responder_id + self._certs = certs + self._extensions = extensions + + def add_response(self, cert, issuer, algorithm, cert_status, this_update, + next_update, revocation_time, revocation_reason): + if self._response is not None: + raise ValueError("Only one response per OCSPResponse.") + + singleresp = _SingleResponse( + cert, issuer, algorithm, cert_status, this_update, next_update, + revocation_time, revocation_reason + ) + return OCSPResponseBuilder( + singleresp, self._responder_id, + self._certs, self._extensions, + ) + + def responder_id(self, encoding, responder_cert): + if self._responder_id is not None: + raise ValueError("responder_id can only be set once") + if not isinstance(responder_cert, x509.Certificate): + raise TypeError("responder_cert must be a Certificate") + if not isinstance(encoding, OCSPResponderEncoding): + raise TypeError( + "encoding must be an element from OCSPResponderEncoding" + ) + + return OCSPResponseBuilder( + self._response, (responder_cert, encoding), + self._certs, self._extensions, + ) + + def certificates(self, certs): + if self._certs is not None: + raise ValueError("certificates may only be set once") + certs = list(certs) + if len(certs) == 0: + raise ValueError("certs must not be an empty list") + if not all(isinstance(x, x509.Certificate) for x in certs): + raise TypeError("certs must be a list of Certificates") + return OCSPResponseBuilder( + self._response, self._responder_id, + certs, self._extensions, + ) + + def add_extension(self, extension, critical): + if not isinstance(extension, x509.ExtensionType): + raise TypeError("extension must be an ExtensionType") + + extension = x509.Extension(extension.oid, critical, extension) + _reject_duplicate_extension(extension, self._extensions) + + return OCSPResponseBuilder( + self._response, self._responder_id, + self._certs, self._extensions + [extension], + ) + + def sign(self, private_key, algorithm): + from cryptography.hazmat.backends.openssl.backend import backend + if self._response is None: + raise ValueError("You must add a response before signing") + if self._responder_id is None: + raise ValueError("You must add a responder_id before signing") + + return backend.create_ocsp_response( + OCSPResponseStatus.SUCCESSFUL, self, private_key, algorithm + ) + + @classmethod + def build_unsuccessful(cls, response_status): + from cryptography.hazmat.backends.openssl.backend import backend + if not isinstance(response_status, OCSPResponseStatus): + raise TypeError( + "response_status must be an item from OCSPResponseStatus" + ) + if response_status is OCSPResponseStatus.SUCCESSFUL: + raise ValueError("response_status cannot be SUCCESSFUL") + + return backend.create_ocsp_response(response_status, None, None, None) + + +@six.add_metaclass(abc.ABCMeta) +class OCSPRequest(object): + @abc.abstractproperty + def issuer_key_hash(self): + """ + The hash of the issuer public key + """ + + @abc.abstractproperty + def issuer_name_hash(self): + """ + The hash of the issuer name + """ + + @abc.abstractproperty + def hash_algorithm(self): + """ + The hash algorithm used in the issuer name and key hashes + """ + + @abc.abstractproperty + def serial_number(self): + """ + The serial number of the cert whose status is being checked + """ + @abc.abstractmethod + def public_bytes(self, encoding): + """ + Serializes the request to DER + """ + + @abc.abstractproperty + def extensions(self): + """ + The list of request extensions. Not single request extensions. + """ + + +@six.add_metaclass(abc.ABCMeta) +class OCSPResponse(object): + @abc.abstractproperty + def response_status(self): + """ + The status of the response. This is a value from the OCSPResponseStatus + enumeration + """ + + @abc.abstractproperty + def signature_algorithm_oid(self): + """ + The ObjectIdentifier of the signature algorithm + """ + + @abc.abstractproperty + def signature_hash_algorithm(self): + """ + Returns a HashAlgorithm corresponding to the type of the digest signed + """ + + @abc.abstractproperty + def signature(self): + """ + The signature bytes + """ + + @abc.abstractproperty + def tbs_response_bytes(self): + """ + The tbsResponseData bytes + """ + + @abc.abstractproperty + def certificates(self): + """ + A list of certificates used to help build a chain to verify the OCSP + response. This situation occurs when the OCSP responder uses a delegate + certificate. + """ + + @abc.abstractproperty + def responder_key_hash(self): + """ + The responder's key hash or None + """ + + @abc.abstractproperty + def responder_name(self): + """ + The responder's Name or None + """ + + @abc.abstractproperty + def produced_at(self): + """ + The time the response was produced + """ + + @abc.abstractproperty + def certificate_status(self): + """ + The status of the certificate (an element from the OCSPCertStatus enum) + """ + + @abc.abstractproperty + def revocation_time(self): + """ + The date of when the certificate was revoked or None if not + revoked. + """ + + @abc.abstractproperty + def revocation_reason(self): + """ + The reason the certificate was revoked or None if not specified or + not revoked. + """ + + @abc.abstractproperty + def this_update(self): + """ + The most recent time at which the status being indicated is known by + the responder to have been correct + """ + + @abc.abstractproperty + def next_update(self): + """ + The time when newer information will be available + """ + + @abc.abstractproperty + def issuer_key_hash(self): + """ + The hash of the issuer public key + """ + + @abc.abstractproperty + def issuer_name_hash(self): + """ + The hash of the issuer name + """ + + @abc.abstractproperty + def hash_algorithm(self): + """ + The hash algorithm used in the issuer name and key hashes + """ + + @abc.abstractproperty + def serial_number(self): + """ + The serial number of the cert whose status is being checked + """ + + @abc.abstractproperty + def extensions(self): + """ + The list of response extensions. Not single response extensions. + """ + + @abc.abstractproperty + def single_extensions(self): + """ + The list of single response extensions. Not response extensions. + """ diff --git a/src/cryptography/x509/oid.py b/src/cryptography/x509/oid.py new file mode 100644 index 00000000..284db54f --- /dev/null +++ b/src/cryptography/x509/oid.py @@ -0,0 +1,257 @@ +# 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._oid import ObjectIdentifier +from cryptography.hazmat.primitives import hashes + + +class ExtensionOID(object): + SUBJECT_DIRECTORY_ATTRIBUTES = ObjectIdentifier("2.5.29.9") + SUBJECT_KEY_IDENTIFIER = ObjectIdentifier("2.5.29.14") + KEY_USAGE = ObjectIdentifier("2.5.29.15") + SUBJECT_ALTERNATIVE_NAME = ObjectIdentifier("2.5.29.17") + ISSUER_ALTERNATIVE_NAME = ObjectIdentifier("2.5.29.18") + BASIC_CONSTRAINTS = ObjectIdentifier("2.5.29.19") + NAME_CONSTRAINTS = ObjectIdentifier("2.5.29.30") + CRL_DISTRIBUTION_POINTS = ObjectIdentifier("2.5.29.31") + CERTIFICATE_POLICIES = ObjectIdentifier("2.5.29.32") + POLICY_MAPPINGS = ObjectIdentifier("2.5.29.33") + AUTHORITY_KEY_IDENTIFIER = ObjectIdentifier("2.5.29.35") + POLICY_CONSTRAINTS = ObjectIdentifier("2.5.29.36") + EXTENDED_KEY_USAGE = ObjectIdentifier("2.5.29.37") + FRESHEST_CRL = ObjectIdentifier("2.5.29.46") + INHIBIT_ANY_POLICY = ObjectIdentifier("2.5.29.54") + ISSUING_DISTRIBUTION_POINT = ObjectIdentifier("2.5.29.28") + AUTHORITY_INFORMATION_ACCESS = ObjectIdentifier("1.3.6.1.5.5.7.1.1") + SUBJECT_INFORMATION_ACCESS = ObjectIdentifier("1.3.6.1.5.5.7.1.11") + OCSP_NO_CHECK = ObjectIdentifier("1.3.6.1.5.5.7.48.1.5") + TLS_FEATURE = ObjectIdentifier("1.3.6.1.5.5.7.1.24") + CRL_NUMBER = ObjectIdentifier("2.5.29.20") + DELTA_CRL_INDICATOR = ObjectIdentifier("2.5.29.27") + PRECERT_SIGNED_CERTIFICATE_TIMESTAMPS = ( + ObjectIdentifier("1.3.6.1.4.1.11129.2.4.2") + ) + PRECERT_POISON = ( + ObjectIdentifier("1.3.6.1.4.1.11129.2.4.3") + ) + + +class OCSPExtensionOID(object): + NONCE = ObjectIdentifier("1.3.6.1.5.5.7.48.1.2") + + +class CRLEntryExtensionOID(object): + CERTIFICATE_ISSUER = ObjectIdentifier("2.5.29.29") + CRL_REASON = ObjectIdentifier("2.5.29.21") + INVALIDITY_DATE = ObjectIdentifier("2.5.29.24") + + +class NameOID(object): + COMMON_NAME = ObjectIdentifier("2.5.4.3") + COUNTRY_NAME = ObjectIdentifier("2.5.4.6") + LOCALITY_NAME = ObjectIdentifier("2.5.4.7") + STATE_OR_PROVINCE_NAME = ObjectIdentifier("2.5.4.8") + STREET_ADDRESS = ObjectIdentifier("2.5.4.9") + ORGANIZATION_NAME = ObjectIdentifier("2.5.4.10") + ORGANIZATIONAL_UNIT_NAME = ObjectIdentifier("2.5.4.11") + SERIAL_NUMBER = ObjectIdentifier("2.5.4.5") + SURNAME = ObjectIdentifier("2.5.4.4") + GIVEN_NAME = ObjectIdentifier("2.5.4.42") + TITLE = ObjectIdentifier("2.5.4.12") + GENERATION_QUALIFIER = ObjectIdentifier("2.5.4.44") + X500_UNIQUE_IDENTIFIER = ObjectIdentifier("2.5.4.45") + DN_QUALIFIER = ObjectIdentifier("2.5.4.46") + PSEUDONYM = ObjectIdentifier("2.5.4.65") + USER_ID = ObjectIdentifier("0.9.2342.19200300.100.1.1") + DOMAIN_COMPONENT = ObjectIdentifier("0.9.2342.19200300.100.1.25") + EMAIL_ADDRESS = ObjectIdentifier("1.2.840.113549.1.9.1") + JURISDICTION_COUNTRY_NAME = ObjectIdentifier("1.3.6.1.4.1.311.60.2.1.3") + JURISDICTION_LOCALITY_NAME = ObjectIdentifier("1.3.6.1.4.1.311.60.2.1.1") + JURISDICTION_STATE_OR_PROVINCE_NAME = ObjectIdentifier( + "1.3.6.1.4.1.311.60.2.1.2" + ) + BUSINESS_CATEGORY = ObjectIdentifier("2.5.4.15") + POSTAL_ADDRESS = ObjectIdentifier("2.5.4.16") + POSTAL_CODE = ObjectIdentifier("2.5.4.17") + INN = ObjectIdentifier("1.2.643.3.131.1.1") + OGRN = ObjectIdentifier("1.2.643.100.1") + SNILS = ObjectIdentifier("1.2.643.100.3") + + +class SignatureAlgorithmOID(object): + RSA_WITH_MD5 = ObjectIdentifier("1.2.840.113549.1.1.4") + RSA_WITH_SHA1 = ObjectIdentifier("1.2.840.113549.1.1.5") + # This is an alternate OID for RSA with SHA1 that is occasionally seen + _RSA_WITH_SHA1 = ObjectIdentifier("1.3.14.3.2.29") + RSA_WITH_SHA224 = ObjectIdentifier("1.2.840.113549.1.1.14") + RSA_WITH_SHA256 = ObjectIdentifier("1.2.840.113549.1.1.11") + RSA_WITH_SHA384 = ObjectIdentifier("1.2.840.113549.1.1.12") + RSA_WITH_SHA512 = ObjectIdentifier("1.2.840.113549.1.1.13") + RSASSA_PSS = ObjectIdentifier("1.2.840.113549.1.1.10") + ECDSA_WITH_SHA1 = ObjectIdentifier("1.2.840.10045.4.1") + ECDSA_WITH_SHA224 = ObjectIdentifier("1.2.840.10045.4.3.1") + ECDSA_WITH_SHA256 = ObjectIdentifier("1.2.840.10045.4.3.2") + ECDSA_WITH_SHA384 = ObjectIdentifier("1.2.840.10045.4.3.3") + ECDSA_WITH_SHA512 = ObjectIdentifier("1.2.840.10045.4.3.4") + DSA_WITH_SHA1 = ObjectIdentifier("1.2.840.10040.4.3") + DSA_WITH_SHA224 = ObjectIdentifier("2.16.840.1.101.3.4.3.1") + DSA_WITH_SHA256 = ObjectIdentifier("2.16.840.1.101.3.4.3.2") + ED25519 = ObjectIdentifier("1.3.101.112") + ED448 = ObjectIdentifier("1.3.101.113") + GOSTR3411_94_WITH_3410_2001 = ObjectIdentifier("1.2.643.2.2.3") + GOSTR3410_2012_WITH_3411_2012_256 = ObjectIdentifier( + "1.2.643.7.1.1.3.2" + ) + GOSTR3410_2012_WITH_3411_2012_512 = ObjectIdentifier( + "1.2.643.7.1.1.3.3" + ) + + +_SIG_OIDS_TO_HASH = { + SignatureAlgorithmOID.RSA_WITH_MD5: hashes.MD5(), + SignatureAlgorithmOID.RSA_WITH_SHA1: hashes.SHA1(), + SignatureAlgorithmOID._RSA_WITH_SHA1: hashes.SHA1(), + SignatureAlgorithmOID.RSA_WITH_SHA224: hashes.SHA224(), + SignatureAlgorithmOID.RSA_WITH_SHA256: hashes.SHA256(), + SignatureAlgorithmOID.RSA_WITH_SHA384: hashes.SHA384(), + SignatureAlgorithmOID.RSA_WITH_SHA512: hashes.SHA512(), + SignatureAlgorithmOID.ECDSA_WITH_SHA1: hashes.SHA1(), + SignatureAlgorithmOID.ECDSA_WITH_SHA224: hashes.SHA224(), + SignatureAlgorithmOID.ECDSA_WITH_SHA256: hashes.SHA256(), + SignatureAlgorithmOID.ECDSA_WITH_SHA384: hashes.SHA384(), + SignatureAlgorithmOID.ECDSA_WITH_SHA512: hashes.SHA512(), + SignatureAlgorithmOID.DSA_WITH_SHA1: hashes.SHA1(), + SignatureAlgorithmOID.DSA_WITH_SHA224: hashes.SHA224(), + SignatureAlgorithmOID.DSA_WITH_SHA256: hashes.SHA256(), + SignatureAlgorithmOID.ED25519: None, + SignatureAlgorithmOID.ED448: None, + SignatureAlgorithmOID.GOSTR3411_94_WITH_3410_2001: None, + SignatureAlgorithmOID.GOSTR3410_2012_WITH_3411_2012_256: None, + SignatureAlgorithmOID.GOSTR3410_2012_WITH_3411_2012_512: None, +} + + +class ExtendedKeyUsageOID(object): + SERVER_AUTH = ObjectIdentifier("1.3.6.1.5.5.7.3.1") + CLIENT_AUTH = ObjectIdentifier("1.3.6.1.5.5.7.3.2") + CODE_SIGNING = ObjectIdentifier("1.3.6.1.5.5.7.3.3") + EMAIL_PROTECTION = ObjectIdentifier("1.3.6.1.5.5.7.3.4") + TIME_STAMPING = ObjectIdentifier("1.3.6.1.5.5.7.3.8") + OCSP_SIGNING = ObjectIdentifier("1.3.6.1.5.5.7.3.9") + ANY_EXTENDED_KEY_USAGE = ObjectIdentifier("2.5.29.37.0") + + +class AuthorityInformationAccessOID(object): + CA_ISSUERS = ObjectIdentifier("1.3.6.1.5.5.7.48.2") + OCSP = ObjectIdentifier("1.3.6.1.5.5.7.48.1") + + +class CertificatePoliciesOID(object): + CPS_QUALIFIER = ObjectIdentifier("1.3.6.1.5.5.7.2.1") + CPS_USER_NOTICE = ObjectIdentifier("1.3.6.1.5.5.7.2.2") + ANY_POLICY = ObjectIdentifier("2.5.29.32.0") + + +_OID_NAMES = { + NameOID.COMMON_NAME: "commonName", + NameOID.COUNTRY_NAME: "countryName", + NameOID.LOCALITY_NAME: "localityName", + NameOID.STATE_OR_PROVINCE_NAME: "stateOrProvinceName", + NameOID.STREET_ADDRESS: "streetAddress", + NameOID.ORGANIZATION_NAME: "organizationName", + NameOID.ORGANIZATIONAL_UNIT_NAME: "organizationalUnitName", + NameOID.SERIAL_NUMBER: "serialNumber", + NameOID.SURNAME: "surname", + NameOID.GIVEN_NAME: "givenName", + NameOID.TITLE: "title", + NameOID.GENERATION_QUALIFIER: "generationQualifier", + NameOID.X500_UNIQUE_IDENTIFIER: "x500UniqueIdentifier", + NameOID.DN_QUALIFIER: "dnQualifier", + NameOID.PSEUDONYM: "pseudonym", + NameOID.USER_ID: "userID", + NameOID.DOMAIN_COMPONENT: "domainComponent", + NameOID.EMAIL_ADDRESS: "emailAddress", + NameOID.JURISDICTION_COUNTRY_NAME: "jurisdictionCountryName", + NameOID.JURISDICTION_LOCALITY_NAME: "jurisdictionLocalityName", + NameOID.JURISDICTION_STATE_OR_PROVINCE_NAME: ( + "jurisdictionStateOrProvinceName" + ), + NameOID.BUSINESS_CATEGORY: "businessCategory", + NameOID.POSTAL_ADDRESS: "postalAddress", + NameOID.POSTAL_CODE: "postalCode", + NameOID.INN: "INN", + NameOID.OGRN: "OGRN", + NameOID.SNILS: "SNILS", + + SignatureAlgorithmOID.RSA_WITH_MD5: "md5WithRSAEncryption", + SignatureAlgorithmOID.RSA_WITH_SHA1: "sha1WithRSAEncryption", + SignatureAlgorithmOID.RSA_WITH_SHA224: "sha224WithRSAEncryption", + SignatureAlgorithmOID.RSA_WITH_SHA256: "sha256WithRSAEncryption", + SignatureAlgorithmOID.RSA_WITH_SHA384: "sha384WithRSAEncryption", + SignatureAlgorithmOID.RSA_WITH_SHA512: "sha512WithRSAEncryption", + SignatureAlgorithmOID.RSASSA_PSS: "RSASSA-PSS", + SignatureAlgorithmOID.ECDSA_WITH_SHA1: "ecdsa-with-SHA1", + SignatureAlgorithmOID.ECDSA_WITH_SHA224: "ecdsa-with-SHA224", + SignatureAlgorithmOID.ECDSA_WITH_SHA256: "ecdsa-with-SHA256", + SignatureAlgorithmOID.ECDSA_WITH_SHA384: "ecdsa-with-SHA384", + SignatureAlgorithmOID.ECDSA_WITH_SHA512: "ecdsa-with-SHA512", + SignatureAlgorithmOID.DSA_WITH_SHA1: "dsa-with-sha1", + SignatureAlgorithmOID.DSA_WITH_SHA224: "dsa-with-sha224", + SignatureAlgorithmOID.DSA_WITH_SHA256: "dsa-with-sha256", + SignatureAlgorithmOID.ED25519: "ed25519", + SignatureAlgorithmOID.ED448: "ed448", + SignatureAlgorithmOID.GOSTR3411_94_WITH_3410_2001: ( + "GOST R 34.11-94 with GOST R 34.10-2001" + ), + SignatureAlgorithmOID.GOSTR3410_2012_WITH_3411_2012_256: ( + "GOST R 34.10-2012 with GOST R 34.11-2012 (256 bit)" + ), + SignatureAlgorithmOID.GOSTR3410_2012_WITH_3411_2012_512: ( + "GOST R 34.10-2012 with GOST R 34.11-2012 (512 bit)" + ), + ExtendedKeyUsageOID.SERVER_AUTH: "serverAuth", + ExtendedKeyUsageOID.CLIENT_AUTH: "clientAuth", + ExtendedKeyUsageOID.CODE_SIGNING: "codeSigning", + ExtendedKeyUsageOID.EMAIL_PROTECTION: "emailProtection", + ExtendedKeyUsageOID.TIME_STAMPING: "timeStamping", + ExtendedKeyUsageOID.OCSP_SIGNING: "OCSPSigning", + ExtensionOID.SUBJECT_DIRECTORY_ATTRIBUTES: "subjectDirectoryAttributes", + ExtensionOID.SUBJECT_KEY_IDENTIFIER: "subjectKeyIdentifier", + ExtensionOID.KEY_USAGE: "keyUsage", + ExtensionOID.SUBJECT_ALTERNATIVE_NAME: "subjectAltName", + ExtensionOID.ISSUER_ALTERNATIVE_NAME: "issuerAltName", + ExtensionOID.BASIC_CONSTRAINTS: "basicConstraints", + ExtensionOID.PRECERT_SIGNED_CERTIFICATE_TIMESTAMPS: ( + "signedCertificateTimestampList" + ), + ExtensionOID.PRECERT_POISON: "ctPoison", + CRLEntryExtensionOID.CRL_REASON: "cRLReason", + CRLEntryExtensionOID.INVALIDITY_DATE: "invalidityDate", + CRLEntryExtensionOID.CERTIFICATE_ISSUER: "certificateIssuer", + ExtensionOID.NAME_CONSTRAINTS: "nameConstraints", + ExtensionOID.CRL_DISTRIBUTION_POINTS: "cRLDistributionPoints", + ExtensionOID.CERTIFICATE_POLICIES: "certificatePolicies", + ExtensionOID.POLICY_MAPPINGS: "policyMappings", + ExtensionOID.AUTHORITY_KEY_IDENTIFIER: "authorityKeyIdentifier", + ExtensionOID.POLICY_CONSTRAINTS: "policyConstraints", + ExtensionOID.EXTENDED_KEY_USAGE: "extendedKeyUsage", + ExtensionOID.FRESHEST_CRL: "freshestCRL", + ExtensionOID.INHIBIT_ANY_POLICY: "inhibitAnyPolicy", + ExtensionOID.ISSUING_DISTRIBUTION_POINT: ( + "issuingDistributionPoint" + ), + ExtensionOID.AUTHORITY_INFORMATION_ACCESS: "authorityInfoAccess", + ExtensionOID.SUBJECT_INFORMATION_ACCESS: "subjectInfoAccess", + ExtensionOID.OCSP_NO_CHECK: "OCSPNoCheck", + ExtensionOID.CRL_NUMBER: "cRLNumber", + ExtensionOID.DELTA_CRL_INDICATOR: "deltaCRLIndicator", + ExtensionOID.TLS_FEATURE: "TLSFeature", + AuthorityInformationAccessOID.OCSP: "OCSP", + AuthorityInformationAccessOID.CA_ISSUERS: "caIssuers", + CertificatePoliciesOID.CPS_QUALIFIER: "id-qt-cps", + CertificatePoliciesOID.CPS_USER_NOTICE: "id-qt-unotice", + OCSPExtensionOID.NONCE: "OCSPNonce", +} |
