diff options
Diffstat (limited to 'src')
17 files changed, 525 insertions, 76 deletions
diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index eb23ef00..f33aba95 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -36,7 +36,7 @@ from cryptography.hazmat.backends.openssl.rsa import ( ) from cryptography.hazmat.backends.openssl.x509 import _Certificate from cryptography.hazmat.bindings.openssl.binding import Binding -from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import dsa, ec, rsa from cryptography.hazmat.primitives.asymmetric.padding import ( MGF1, OAEP, PKCS1v15, PSS @@ -320,6 +320,7 @@ class Backend(object): ) def _bn_to_int(self, bn): + assert bn != self._ffi.NULL if six.PY3: # Python 3 has constant time from_bytes, so use that. @@ -346,6 +347,7 @@ class Backend(object): ownership of the object). Be sure to register it for GC if it will be discarded after use. """ + assert bn is None or bn != self._ffi.NULL if bn is None: bn = self._ffi.NULL @@ -690,12 +692,28 @@ class Backend(object): ) def load_pem_public_key(self, data): - return self._load_key( - self._lib.PEM_read_bio_PUBKEY, - self._evp_pkey_to_public_key, - data, - None, + mem_bio = self._bytes_to_bio(data) + evp_pkey = self._lib.PEM_read_bio_PUBKEY( + mem_bio.bio, self._ffi.NULL, self._ffi.NULL, self._ffi.NULL ) + if evp_pkey != self._ffi.NULL: + evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free) + return self._evp_pkey_to_public_key(evp_pkey) + else: + # It's not a (RSA/DSA/ECDSA) subjectPublicKeyInfo, but we still + # need to check to see if it is a pure PKCS1 RSA public key (not + # embedded in a subjectPublicKeyInfo) + self._consume_errors() + res = self._lib.BIO_reset(mem_bio.bio) + assert res == 1 + rsa_cdata = self._lib.PEM_read_bio_RSAPublicKey( + mem_bio.bio, self._ffi.NULL, self._ffi.NULL, self._ffi.NULL + ) + if rsa_cdata != self._ffi.NULL: + rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free) + return _RSAPublicKey(self, rsa_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 @@ -760,12 +778,24 @@ class Backend(object): 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) - if evp_pkey == self._ffi.NULL: + if evp_pkey != self._ffi.NULL: + evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free) + return self._evp_pkey_to_public_key(evp_pkey) + else: + # It's not a (RSA/DSA/ECDSA) subjectPublicKeyInfo, but we still + # need to check to see if it is a pure PKCS1 RSA public key (not + # embedded in a subjectPublicKeyInfo) self._consume_errors() - raise ValueError("Could not unserialize key data.") - - evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free) - return self._evp_pkey_to_public_key(evp_pkey) + res = self._lib.BIO_reset(mem_bio.bio) + assert res == 1 + rsa_cdata = self._lib.d2i_RSAPublicKey_bio( + mem_bio.bio, self._ffi.NULL + ) + if rsa_cdata != self._ffi.NULL: + rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free) + return _RSAPublicKey(self, rsa_cdata) + else: + self._handle_key_loading_error() def load_pem_x509_certificate(self, data): mem_bio = self._bytes_to_bio(data) @@ -1093,6 +1123,93 @@ class Backend(object): return ctx + def _private_key_bytes(self, encoding, format, encryption_algorithm, + traditional_write_func, 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.PrivateFormat): + raise TypeError( + "format must be an item from the PrivateFormat enum" + ) + + # This is a temporary check until we land DER serialization. + if encoding is not serialization.Encoding.PEM: + raise ValueError("Only PEM encoding is supported by this backend") + + if format is serialization.PrivateFormat.PKCS8: + write_bio = self._lib.PEM_write_bio_PKCS8PrivateKey + key = evp_pkey + elif format is serialization.PrivateFormat.TraditionalOpenSSL: + write_bio = traditional_write_func + key = cdata + + if not isinstance(encryption_algorithm, + serialization.KeySerializationEncryption): + raise TypeError( + "Encryption algorithm must be a KeySerializationEncryption " + "instance" + ) + + 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: + raise ValueError( + "Passwords longer than 1023 bytes are not supported by " + "this backend" + ) + else: + raise ValueError("Unsupported encryption type") + + bio = self._create_mem_bio() + res = write_bio( + bio, + key, + evp_cipher, + password, + passlen, + self._ffi.NULL, + self._ffi.NULL + ) + assert res == 1 + return self._read_mem_bio(bio) + + def _public_key_bytes(self, encoding, format, pkcs1_write_func, 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" + ) + + # This is a temporary check until we land DER serialization. + if encoding is not serialization.Encoding.PEM: + raise ValueError("Only PEM encoding is supported by this backend") + + if format is serialization.PublicFormat.SubjectPublicKeyInfo: + write_bio = self._lib.PEM_write_bio_PUBKEY + key = evp_pkey + elif format is serialization.PublicFormat.PKCS1: + write_bio = pkcs1_write_func + key = cdata + + bio = self._create_mem_bio() + res = write_bio(bio, key) + assert res == 1 + return self._read_mem_bio(bio) + class GetCipherByName(object): def __init__(self, fmt): diff --git a/src/cryptography/hazmat/backends/openssl/dsa.py b/src/cryptography/hazmat/backends/openssl/dsa.py index d2972e4a..8d02e492 100644 --- a/src/cryptography/hazmat/backends/openssl/dsa.py +++ b/src/cryptography/hazmat/backends/openssl/dsa.py @@ -11,9 +11,6 @@ from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.asymmetric import ( AsymmetricSignatureContext, AsymmetricVerificationContext, dsa ) -from cryptography.hazmat.primitives.interfaces import ( - DSAParametersWithNumbers, DSAPrivateKeyWithNumbers, DSAPublicKeyWithNumbers -) def _truncate_digest_for_dsa(dsa_cdata, digest, backend): @@ -94,7 +91,7 @@ class _DSASignatureContext(object): return self._backend._ffi.buffer(sig_buf)[:buflen[0]] -@utils.register_interface(DSAParametersWithNumbers) +@utils.register_interface(dsa.DSAParametersWithNumbers) class _DSAParameters(object): def __init__(self, backend, dsa_cdata): self._backend = backend @@ -111,7 +108,7 @@ class _DSAParameters(object): return self._backend.generate_dsa_private_key(self) -@utils.register_interface(DSAPrivateKeyWithNumbers) +@utils.register_interface(dsa.DSAPrivateKeyWithSerialization) class _DSAPrivateKey(object): def __init__(self, backend, dsa_cdata): self._backend = backend @@ -159,8 +156,25 @@ class _DSAPrivateKey(object): 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): + evp_pkey = self._backend._lib.EVP_PKEY_new() + assert evp_pkey != self._backend._ffi.NULL + evp_pkey = self._backend._ffi.gc( + evp_pkey, self._backend._lib.EVP_PKEY_free + ) + res = self._backend._lib.EVP_PKEY_set1_DSA(evp_pkey, self._dsa_cdata) + assert res == 1 + return self._backend._private_key_bytes( + encoding, + format, + encryption_algorithm, + self._backend._lib.PEM_write_bio_DSAPrivateKey, + evp_pkey, + self._dsa_cdata + ) + -@utils.register_interface(DSAPublicKeyWithNumbers) +@utils.register_interface(dsa.DSAPublicKeyWithNumbers) class _DSAPublicKey(object): def __init__(self, backend, dsa_cdata): self._backend = backend diff --git a/src/cryptography/hazmat/backends/openssl/ec.py b/src/cryptography/hazmat/backends/openssl/ec.py index 52c93da9..39b0a555 100644 --- a/src/cryptography/hazmat/backends/openssl/ec.py +++ b/src/cryptography/hazmat/backends/openssl/ec.py @@ -9,7 +9,7 @@ from cryptography.exceptions import ( InvalidSignature, UnsupportedAlgorithm, _Reasons ) from cryptography.hazmat.backends.openssl.utils import _truncate_digest -from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import ( AsymmetricSignatureContext, AsymmetricVerificationContext, ec ) @@ -148,7 +148,7 @@ class _ECDSAVerificationContext(object): return True -@utils.register_interface(ec.EllipticCurvePrivateKeyWithNumbers) +@utils.register_interface(ec.EllipticCurvePrivateKeyWithSerialization) class _EllipticCurvePrivateKey(object): def __init__(self, backend, ec_key_cdata): self._backend = backend @@ -200,6 +200,23 @@ class _EllipticCurvePrivateKey(object): public_numbers=self.public_key().public_numbers() ) + def private_bytes(self, encoding, format, encryption_algorithm): + evp_pkey = self._backend._lib.EVP_PKEY_new() + assert evp_pkey != self._backend._ffi.NULL + evp_pkey = self._backend._ffi.gc( + evp_pkey, self._backend._lib.EVP_PKEY_free + ) + res = self._backend._lib.EVP_PKEY_set1_EC_KEY(evp_pkey, self._ec_key) + assert res == 1 + return self._backend._private_key_bytes( + encoding, + format, + encryption_algorithm, + self._backend._lib.PEM_write_bio_ECPrivateKey, + evp_pkey, + self._ec_key + ) + @utils.register_interface(ec.EllipticCurvePublicKeyWithNumbers) class _EllipticCurvePublicKey(object): @@ -245,3 +262,24 @@ class _EllipticCurvePublicKey(object): y=y, curve=self._curve ) + + def public_bytes(self, encoding, format): + if format is serialization.PublicFormat.PKCS1: + raise ValueError( + "EC public keys do not support PKCS1 serialization" + ) + + evp_pkey = self._backend._lib.EVP_PKEY_new() + assert evp_pkey != self._backend._ffi.NULL + evp_pkey = self._backend._ffi.gc( + evp_pkey, self._backend._lib.EVP_PKEY_free + ) + res = self._backend._lib.EVP_PKEY_set1_EC_KEY(evp_pkey, self._ec_key) + assert res == 1 + return self._backend._public_key_bytes( + encoding, + format, + None, + evp_pkey, + None + ) diff --git a/src/cryptography/hazmat/backends/openssl/rsa.py b/src/cryptography/hazmat/backends/openssl/rsa.py index 00ddcda3..25168c2f 100644 --- a/src/cryptography/hazmat/backends/openssl/rsa.py +++ b/src/cryptography/hazmat/backends/openssl/rsa.py @@ -17,8 +17,9 @@ from cryptography.hazmat.primitives.asymmetric import ( from cryptography.hazmat.primitives.asymmetric.padding import ( AsymmetricPadding, MGF1, OAEP, PKCS1v15, PSS ) -from cryptography.hazmat.primitives.interfaces import ( - RSAPrivateKeyWithNumbers, RSAPublicKeyWithNumbers +from cryptography.hazmat.primitives.asymmetric.rsa import ( + RSAPrivateKeyWithNumbers, RSAPrivateKeyWithSerialization, + RSAPublicKeyWithSerialization ) @@ -507,6 +508,7 @@ class _RSAVerificationContext(object): @utils.register_interface(RSAPrivateKeyWithNumbers) +@utils.register_interface(RSAPrivateKeyWithSerialization) class _RSAPrivateKey(object): def __init__(self, backend, rsa_cdata): self._backend = backend @@ -559,8 +561,18 @@ class _RSAPrivateKey(object): ) ) + def private_bytes(self, encoding, format, encryption_algorithm): + return self._backend._private_key_bytes( + encoding, + format, + encryption_algorithm, + self._backend._lib.PEM_write_bio_RSAPrivateKey, + self._evp_pkey, + self._rsa_cdata + ) + -@utils.register_interface(RSAPublicKeyWithNumbers) +@utils.register_interface(RSAPublicKeyWithSerialization) class _RSAPublicKey(object): def __init__(self, backend, rsa_cdata): self._backend = backend @@ -592,3 +604,12 @@ class _RSAPublicKey(object): e=self._backend._bn_to_int(self._rsa_cdata.e), n=self._backend._bn_to_int(self._rsa_cdata.n), ) + + def public_bytes(self, encoding, format): + return self._backend._public_key_bytes( + encoding, + format, + self._backend._lib.PEM_write_bio_RSAPublicKey, + self._evp_pkey, + self._rsa_cdata + ) diff --git a/src/cryptography/hazmat/bindings/openssl/dh.py b/src/cryptography/hazmat/bindings/openssl/dh.py index 06ac6f41..b66e7196 100644 --- a/src/cryptography/hazmat/bindings/openssl/dh.py +++ b/src/cryptography/hazmat/bindings/openssl/dh.py @@ -18,6 +18,9 @@ typedef struct dh_st { BIGNUM *priv_key; /* Public DH value g^x */ BIGNUM *pub_key; + /* X9.42/RFC 2631 */ + BIGNUM *q; + BIGNUM *j; ...; } DH; """ @@ -28,6 +31,7 @@ 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 *); diff --git a/src/cryptography/hazmat/bindings/openssl/err.py b/src/cryptography/hazmat/bindings/openssl/err.py index ec393c1b..0ee19c9e 100644 --- a/src/cryptography/hazmat/bindings/openssl/err.py +++ b/src/cryptography/hazmat/bindings/openssl/err.py @@ -21,6 +21,7 @@ struct ERR_string_data_st { }; typedef struct ERR_string_data_st ERR_STRING_DATA; +static const int ERR_LIB_DH; static const int ERR_LIB_EVP; static const int ERR_LIB_EC; static const int ERR_LIB_PEM; @@ -95,6 +96,10 @@ 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 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; diff --git a/src/cryptography/hazmat/bindings/openssl/pem.py b/src/cryptography/hazmat/bindings/openssl/pem.py index d0c70f5d..98c7648f 100644 --- a/src/cryptography/hazmat/bindings/openssl/pem.py +++ b/src/cryptography/hazmat/bindings/openssl/pem.py @@ -72,9 +72,23 @@ 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 *); """ 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; +#endif + """ -CONDITIONAL_NAMES = {} +CONDITIONAL_NAMES = { + "Cryptography_HAS_EC": [ + "PEM_write_bio_ECPrivateKey" + ] +} diff --git a/src/cryptography/hazmat/primitives/asymmetric/dh.py b/src/cryptography/hazmat/primitives/asymmetric/dh.py new file mode 100644 index 00000000..61556efb --- /dev/null +++ b/src/cryptography/hazmat/primitives/asymmetric/dh.py @@ -0,0 +1,101 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import, division, print_function + +import six + +from cryptography import utils + + +class DHPrivateNumbers(object): + def __init__(self, x, public_numbers): + if not isinstance(x, six.integer_types): + raise TypeError("x must be an integer.") + + if not isinstance(public_numbers, DHPublicNumbers): + raise TypeError("public_numbers must be an instance of " + "DHPublicNumbers.") + + self._x = x + self._public_numbers = public_numbers + + def __eq__(self, other): + if not isinstance(other, DHPrivateNumbers): + return NotImplemented + + return ( + self._x == other._x and + self._public_numbers == other._public_numbers + ) + + def __ne__(self, other): + return not self == other + + public_numbers = utils.read_only_property("_public_numbers") + x = utils.read_only_property("_x") + + +class DHPublicNumbers(object): + def __init__(self, y, parameter_numbers): + if not isinstance(y, six.integer_types): + raise TypeError("y must be an integer.") + + if not isinstance(parameter_numbers, DHParameterNumbers): + raise TypeError( + "parameters must be an instance of DHParameterNumbers.") + + self._y = y + self._parameter_numbers = parameter_numbers + + def __eq__(self, other): + if not isinstance(other, DHPublicNumbers): + return NotImplemented + + return ( + self._y == other._y and + self._parameter_numbers == other._parameter_numbers + ) + + def __ne__(self, other): + return not self == other + + y = utils.read_only_property("_y") + parameter_numbers = utils.read_only_property("_parameter_numbers") + + +class DHParameterNumbers(object): + def __init__(self, p, g): + if ( + not isinstance(p, six.integer_types) or + not isinstance(g, six.integer_types) + ): + raise TypeError("p and g must be integers") + + self._p = p + self._g = g + + def __eq__(self, other): + if not isinstance(other, DHParameterNumbers): + return NotImplemented + + return ( + self._p == other._p and + self._g == other._g + ) + + def __ne__(self, other): + return not self == other + + p = utils.read_only_property("_p") + g = utils.read_only_property("_g") diff --git a/src/cryptography/hazmat/primitives/asymmetric/dsa.py b/src/cryptography/hazmat/primitives/asymmetric/dsa.py index 58058df9..084686e4 100644 --- a/src/cryptography/hazmat/primitives/asymmetric/dsa.py +++ b/src/cryptography/hazmat/primitives/asymmetric/dsa.py @@ -57,13 +57,30 @@ class DSAPrivateKey(object): @six.add_metaclass(abc.ABCMeta) -class DSAPrivateKeyWithNumbers(DSAPrivateKey): +class DSAPrivateKeyWithSerialization(DSAPrivateKey): @abc.abstractmethod def private_numbers(self): """ Returns a DSAPrivateNumbers. """ + @abc.abstractmethod + def private_bytes(self, encoding, format, encryption_algorithm): + """ + Returns the key serialized as bytes. + """ + + +DSAPrivateKeyWithNumbers = utils.deprecated( + DSAPrivateKeyWithSerialization, + __name__, + ( + "The DSAPrivateKeyWithNumbers interface has been renamed to " + "DSAPrivateKeyWithSerialization" + ), + utils.DeprecatedIn08 +) + @six.add_metaclass(abc.ABCMeta) class DSAPublicKey(object): diff --git a/src/cryptography/hazmat/primitives/asymmetric/ec.py b/src/cryptography/hazmat/primitives/asymmetric/ec.py index c7749ca5..bf1705db 100644 --- a/src/cryptography/hazmat/primitives/asymmetric/ec.py +++ b/src/cryptography/hazmat/primitives/asymmetric/ec.py @@ -57,13 +57,30 @@ class EllipticCurvePrivateKey(object): @six.add_metaclass(abc.ABCMeta) -class EllipticCurvePrivateKeyWithNumbers(EllipticCurvePrivateKey): +class EllipticCurvePrivateKeyWithSerialization(EllipticCurvePrivateKey): @abc.abstractmethod def private_numbers(self): """ Returns an EllipticCurvePrivateNumbers. """ + @abc.abstractmethod + def private_bytes(self, encoding, format, encryption_algorithm): + """ + Returns the key serialized as bytes. + """ + + +EllipticCurvePrivateKeyWithNumbers = utils.deprecated( + EllipticCurvePrivateKeyWithSerialization, + __name__, + ( + "The EllipticCurvePrivateKeyWithNumbers interface has been renamed to " + "EllipticCurvePrivateKeyWithSerialization" + ), + utils.DeprecatedIn08 +) + @six.add_metaclass(abc.ABCMeta) class EllipticCurvePublicKey(object): @@ -81,13 +98,30 @@ class EllipticCurvePublicKey(object): @six.add_metaclass(abc.ABCMeta) -class EllipticCurvePublicKeyWithNumbers(EllipticCurvePublicKey): +class EllipticCurvePublicKeyWithSerialization(EllipticCurvePublicKey): @abc.abstractmethod def public_numbers(self): """ Returns an EllipticCurvePublicNumbers. """ + @abc.abstractmethod + def public_bytes(self, encoding, format): + """ + Returns the key serialized as bytes. + """ + + +EllipticCurvePublicKeyWithNumbers = utils.deprecated( + EllipticCurvePublicKeyWithSerialization, + __name__, + ( + "The EllipticCurvePublicKeyWithNumbers interface has been renamed to " + "EllipticCurvePublicKeyWithSerialization" + ), + utils.DeprecatedIn08 +) + @utils.register_interface(EllipticCurve) class SECT571R1(object): diff --git a/src/cryptography/hazmat/primitives/asymmetric/rsa.py b/src/cryptography/hazmat/primitives/asymmetric/rsa.py index 332ad2c3..8adc7459 100644 --- a/src/cryptography/hazmat/primitives/asymmetric/rsa.py +++ b/src/cryptography/hazmat/primitives/asymmetric/rsa.py @@ -42,13 +42,30 @@ class RSAPrivateKey(object): @six.add_metaclass(abc.ABCMeta) -class RSAPrivateKeyWithNumbers(RSAPrivateKey): +class RSAPrivateKeyWithSerialization(RSAPrivateKey): @abc.abstractmethod def private_numbers(self): """ Returns an RSAPrivateNumbers. """ + @abc.abstractmethod + def private_bytes(self, encoding, format, encryption_algorithm): + """ + Returns the key serialized as bytes. + """ + + +RSAPrivateKeyWithNumbers = utils.deprecated( + RSAPrivateKeyWithSerialization, + __name__, + ( + "The RSAPrivateKeyWithNumbers interface has been renamed to " + "RSAPrivateKeyWithSerialization" + ), + utils.DeprecatedIn08 +) + @six.add_metaclass(abc.ABCMeta) class RSAPublicKey(object): @@ -72,13 +89,30 @@ class RSAPublicKey(object): @six.add_metaclass(abc.ABCMeta) -class RSAPublicKeyWithNumbers(RSAPublicKey): +class RSAPublicKeyWithSerialization(RSAPublicKey): @abc.abstractmethod def public_numbers(self): """ Returns an RSAPublicNumbers """ + @abc.abstractmethod + def public_bytes(self, encoding, format): + """ + Returns the key serialized as bytes. + """ + + +RSAPublicKeyWithNumbers = utils.deprecated( + RSAPublicKeyWithSerialization, + __name__, + ( + "The RSAPublicKeyWithNumbers interface has been renamed to " + "RSAPublicKeyWithSerialization" + ), + utils.DeprecatedIn08 +) + def generate_private_key(public_exponent, key_size, backend): if not isinstance(backend, RSABackend): diff --git a/src/cryptography/hazmat/primitives/interfaces/__init__.py b/src/cryptography/hazmat/primitives/interfaces/__init__.py index 6b4241bd..6913ace9 100644 --- a/src/cryptography/hazmat/primitives/interfaces/__init__.py +++ b/src/cryptography/hazmat/primitives/interfaces/__init__.py @@ -289,11 +289,12 @@ RSAPrivateKey = utils.deprecated( ) RSAPrivateKeyWithNumbers = utils.deprecated( - rsa.RSAPrivateKeyWithNumbers, + rsa.RSAPrivateKeyWithSerialization, __name__, ( "The RSAPrivateKeyWithNumbers interface has moved to the " - "cryptography.hazmat.primitives.asymmetric.rsa module" + "cryptography.hazmat.primitives.asymmetric.rsa module and has been " + "renamed RSAPrivateKeyWithSerialization" ), utils.DeprecatedIn08 ) @@ -386,5 +387,9 @@ class MACContext(object): signature. """ -# DeprecatedIn07 -CMACContext = MACContext +CMACContext = utils.deprecated( + MACContext, + __name__, + "The CMACContext interface has been renamed to MACContext", + utils.DeprecatedIn07 +) diff --git a/src/cryptography/hazmat/primitives/padding.py b/src/cryptography/hazmat/primitives/padding.py index 8ad64dec..6247f7b5 100644 --- a/src/cryptography/hazmat/primitives/padding.py +++ b/src/cryptography/hazmat/primitives/padding.py @@ -6,6 +6,8 @@ from __future__ import absolute_import, division, print_function import abc +import os + import six from cryptography import utils @@ -13,47 +15,11 @@ from cryptography.exceptions import AlreadyFinalized from cryptography.hazmat.bindings.utils import LazyLibrary, build_ffi -TYPES = """ -uint8_t Cryptography_check_pkcs7_padding(const uint8_t *, uint8_t); -""" - -FUNCTIONS = """ -/* 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; -} - -/* This returns 0xFF if a < b else 0x00, but does so in a constant time - fashion */ -static uint8_t Cryptography_constant_time_lt(uint8_t a, uint8_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; - 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]; - mismatch |= (mask & (pad_size ^ 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 >> 4; - mismatch |= mismatch >> 2; - mismatch |= mismatch >> 1; - /* Now check the low bit to see if it's set */ - return (mismatch & 1) == 0; -} -""" +with open(os.path.join(os.path.dirname(__file__), "src/padding.h")) as f: + TYPES = f.read() + +with open(os.path.join(os.path.dirname(__file__), "src/padding.c")) as f: + FUNCTIONS = f.read() _ffi = build_ffi(cdef_source=TYPES, verify_source=FUNCTIONS) diff --git a/src/cryptography/hazmat/primitives/serialization.py b/src/cryptography/hazmat/primitives/serialization.py index 0f9506e1..8699fa91 100644 --- a/src/cryptography/hazmat/primitives/serialization.py +++ b/src/cryptography/hazmat/primitives/serialization.py @@ -4,11 +4,14 @@ 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 @@ -164,3 +167,37 @@ else: 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/src/padding.c b/src/cryptography/hazmat/primitives/src/padding.c new file mode 100644 index 00000000..570bad9f --- /dev/null +++ b/src/cryptography/hazmat/primitives/src/padding.c @@ -0,0 +1,39 @@ +// 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. + +/* 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; +} + +/* This returns 0xFF if a < b else 0x00, but does so in a constant time + fashion */ +static uint8_t Cryptography_constant_time_lt(uint8_t a, uint8_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; + 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]; + mismatch |= (mask & (pad_size ^ 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 >> 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/cryptography/hazmat/primitives/src/padding.h b/src/cryptography/hazmat/primitives/src/padding.h new file mode 100644 index 00000000..4d218b1a --- /dev/null +++ b/src/cryptography/hazmat/primitives/src/padding.h @@ -0,0 +1,5 @@ +// 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_check_pkcs7_padding(const uint8_t *, uint8_t); diff --git a/src/cryptography/utils.py b/src/cryptography/utils.py index 78dcc1ca..253dea55 100644 --- a/src/cryptography/utils.py +++ b/src/cryptography/utils.py @@ -10,9 +10,7 @@ import sys import warnings -# DeprecatedIn07 objects exist. This comment exists to remind developers to -# look for them when it's time for the ninth release cycle deprecation dance. - +DeprecatedIn07 = DeprecationWarning DeprecatedIn08 = PendingDeprecationWarning |