aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/cryptography/hazmat/backends/openssl/backend.py139
-rw-r--r--src/cryptography/hazmat/backends/openssl/dsa.py26
-rw-r--r--src/cryptography/hazmat/backends/openssl/ec.py42
-rw-r--r--src/cryptography/hazmat/backends/openssl/rsa.py27
-rw-r--r--src/cryptography/hazmat/bindings/openssl/dh.py4
-rw-r--r--src/cryptography/hazmat/bindings/openssl/err.py5
-rw-r--r--src/cryptography/hazmat/bindings/openssl/pem.py16
-rw-r--r--src/cryptography/hazmat/primitives/asymmetric/dh.py101
-rw-r--r--src/cryptography/hazmat/primitives/asymmetric/dsa.py19
-rw-r--r--src/cryptography/hazmat/primitives/asymmetric/ec.py38
-rw-r--r--src/cryptography/hazmat/primitives/asymmetric/rsa.py38
-rw-r--r--src/cryptography/hazmat/primitives/interfaces/__init__.py13
-rw-r--r--src/cryptography/hazmat/primitives/padding.py48
-rw-r--r--src/cryptography/hazmat/primitives/serialization.py37
-rw-r--r--src/cryptography/hazmat/primitives/src/padding.c39
-rw-r--r--src/cryptography/hazmat/primitives/src/padding.h5
-rw-r--r--src/cryptography/utils.py4
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