diff options
-rwxr-xr-x | .travis/install.sh | 4 | ||||
-rw-r--r-- | docs/development/custom-vectors/secp256k1/verify_secp256k1.py | 4 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/backend.py | 81 | ||||
-rw-r--r-- | src/cryptography/x509/oid.py | 29 | ||||
-rw-r--r-- | tests/hazmat/backends/test_openssl.py | 17 | ||||
-rw-r--r-- | tests/hazmat/primitives/test_ec.py | 6 | ||||
-rw-r--r-- | tests/test_x509.py | 77 | ||||
-rw-r--r-- | tests/test_x509_ext.py | 31 |
8 files changed, 171 insertions, 78 deletions
diff --git a/.travis/install.sh b/.travis/install.sh index 112add24..142b4988 100755 --- a/.travis/install.sh +++ b/.travis/install.sh @@ -34,8 +34,8 @@ if [[ "$(uname -s)" == 'Darwin' ]]; then pyenv global 3.4.2 ;; py35) - pyenv install 3.5.0 - pyenv global 3.5.0 + pyenv install 3.5.1 + pyenv global 3.5.1 ;; pypy) pyenv install pypy-4.0.1 diff --git a/docs/development/custom-vectors/secp256k1/verify_secp256k1.py b/docs/development/custom-vectors/secp256k1/verify_secp256k1.py index 3d2c25b9..b236d77f 100644 --- a/docs/development/custom-vectors/secp256k1/verify_secp256k1.py +++ b/docs/development/custom-vectors/secp256k1/verify_secp256k1.py @@ -6,7 +6,7 @@ from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.asymmetric import ec from cryptography.hazmat.primitives.asymmetric.utils import ( - encode_rfc6979_signature + encode_dss_signature ) from tests.utils import ( @@ -27,7 +27,7 @@ def verify_one_vector(vector): message = vector['message'] x = vector['x'] y = vector['y'] - signature = encode_rfc6979_signature(vector['r'], vector['s']) + signature = encode_dss_signature(vector['r'], vector['s']) numbers = ec.EllipticCurvePublicNumbers( x, y, diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index 768559cf..e69554f9 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -40,6 +40,7 @@ from cryptography.hazmat.backends.openssl.x509 import ( _Certificate, _CertificateRevocationList, _CertificateSigningRequest, _DISTPOINT_TYPE_FULLNAME, _DISTPOINT_TYPE_RELATIVENAME ) +from cryptography.hazmat.bindings._openssl import ffi as _ffi from cryptography.hazmat.bindings.openssl import binding from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import dsa, ec, rsa @@ -625,6 +626,34 @@ _EXTENSION_ENCODE_HANDLERS = { } +class _PasswordUserdata(object): + def __init__(self, password): + self.password = password + self.called = 0 + self.exception = None + + +def _pem_password_cb(buf, size, writing, userdata_handle): + ud = _ffi.from_handle(userdata_handle) + ud.called += 1 + + if not ud.password: + ud.exception = TypeError( + "Password was not given but private key is encrypted." + ) + return -1 + elif len(ud.password) < size: + pw_buf = _ffi.buffer(buf, size) + pw_buf[:len(ud.password)] = ud.password + return len(ud.password) + else: + ud.exception = ValueError( + "Passwords longer than {0} bytes are not supported " + "by this backend.".format(size - 1) + ) + return 0 + + @utils.register_interface(CipherBackend) @utils.register_interface(CMACBackend) @utils.register_interface(DERSerializationBackend) @@ -1090,37 +1119,22 @@ class Backend(object): Useful for decrypting PKCS8 files and so on. - Returns a tuple of (cdata function pointer, callback function). + Returns a tuple of (cdata function pointer, userdata). """ + # Forward compatibility for new static callbacks: + # _pem_password_cb is not a nested function because closures don't + # work well with static callbacks. Static callbacks are registered + # globally. The backend is passed in as userdata argument. - def pem_password_cb(buf, size, writing, userdata): - pem_password_cb.called += 1 + userdata = _PasswordUserdata(password=password) - 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) - ) - 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 + pem_password_cb = self._ffi.callback( + "int (char *, int, int, void *)", + _pem_password_cb, ) + return pem_password_cb, userdata + def _mgf1_hash_supported(self, algorithm): if self._lib.Cryptography_HAS_MGF1_MD: return self.hash_supported(algorithm) @@ -1626,31 +1640,32 @@ 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) + password_cb, userdata = self._pem_password_cb(password) + userdata_handle = self._ffi.new_handle(userdata) evp_pkey = openssl_read_func( mem_bio.bio, self._ffi.NULL, - password_callback, - self._ffi.NULL + password_cb, + userdata_handle, ) if evp_pkey == self._ffi.NULL: - if password_func.exception is not None: + if userdata.exception is not None: errors = self._consume_errors() self.openssl_assert(errors) - raise password_func.exception + raise userdata.exception 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 ) diff --git a/src/cryptography/x509/oid.py b/src/cryptography/x509/oid.py index 98a61e0b..7b4df1c9 100644 --- a/src/cryptography/x509/oid.py +++ b/src/cryptography/x509/oid.py @@ -12,6 +12,35 @@ class ObjectIdentifier(object): def __init__(self, dotted_string): self._dotted_string = dotted_string + nodes = self._dotted_string.split(".") + intnodes = [] + + # There must be at least 2 nodes, the first node must be 0..2, and + # if less than 2, the second node cannot have a value outside the + # range 0..39. All nodes must be integers. + for node in nodes: + try: + intnodes.append(int(node, 0)) + except ValueError: + raise ValueError( + "Malformed OID: %s (non-integer nodes)" % ( + self._dotted_string)) + + if len(nodes) < 2: + raise ValueError( + "Malformed OID: %s (insufficient number of nodes)" % ( + self._dotted_string)) + + if intnodes[0] > 2: + raise ValueError( + "Malformed OID: %s (first node outside valid range)" % ( + self._dotted_string)) + + if intnodes[0] < 2 and intnodes[1] >= 40: + raise ValueError( + "Malformed OID: %s (second node outside valid range)" % ( + self._dotted_string)) + def __eq__(self, other): if not isinstance(other, ObjectIdentifier): return NotImplemented diff --git a/tests/hazmat/backends/test_openssl.py b/tests/hazmat/backends/test_openssl.py index 85331595..d048fe68 100644 --- a/tests/hazmat/backends/test_openssl.py +++ b/tests/hazmat/backends/test_openssl.py @@ -503,8 +503,21 @@ class TestOpenSSLSignX509Certificate(object): class TestOpenSSLSerialisationWithOpenSSL(object): def test_pem_password_cb_buffer_too_small(self): - ffi_cb, cb = backend._pem_password_cb(b"aa") - assert cb(None, 1, False, None) == 0 + ffi_cb, userdata = backend._pem_password_cb(b"aa") + handle = backend._ffi.new_handle(userdata) + buf = backend._ffi.new('char *') + assert ffi_cb(buf, 1, False, handle) == 0 + assert userdata.called == 1 + assert isinstance(userdata.exception, ValueError) + + def test_pem_password_cb(self): + password = b'abcdefg' + ffi_cb, userdata = backend._pem_password_cb(password) + handle = backend._ffi.new_handle(userdata) + buf = backend._ffi.new('char *') + assert ffi_cb(buf, len(password) + 1, False, handle) == len(password) + assert userdata.called == 1 + assert backend._ffi.string(buf, len(password)) == password def test_unsupported_evp_pkey_type(self): key = pretend.stub(type="unsupported") diff --git a/tests/hazmat/primitives/test_ec.py b/tests/hazmat/primitives/test_ec.py index a0417fbd..86132678 100644 --- a/tests/hazmat/primitives/test_ec.py +++ b/tests/hazmat/primitives/test_ec.py @@ -19,7 +19,7 @@ from cryptography.hazmat.backends.interfaces import ( from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import ec from cryptography.hazmat.primitives.asymmetric.utils import ( - encode_rfc6979_signature + encode_dss_signature ) from .fixtures_ec import EC_KEY_SECP384R1 @@ -434,7 +434,7 @@ class TestECDSAVectors(object): curve_type() ).public_key(backend) - signature = encode_rfc6979_signature(vector['r'], vector['s']) + signature = encode_dss_signature(vector['r'], vector['s']) verifier = key.verifier( signature, @@ -463,7 +463,7 @@ class TestECDSAVectors(object): curve_type() ).public_key(backend) - signature = encode_rfc6979_signature(vector['r'], vector['s']) + signature = encode_dss_signature(vector['r'], vector['s']) verifier = key.verifier( signature, diff --git a/tests/test_x509.py b/tests/test_x509.py index 86f771b3..67066f04 100644 --- a/tests/test_x509.py +++ b/tests/test_x509.py @@ -3210,15 +3210,15 @@ class TestNameAttribute(object): def test_init_bad_value(self): with pytest.raises(TypeError): x509.NameAttribute( - x509.ObjectIdentifier('oid'), + x509.ObjectIdentifier('2.999.1'), b'bytes' ) def test_eq(self): assert x509.NameAttribute( - x509.ObjectIdentifier('oid'), u'value' + x509.ObjectIdentifier('2.999.1'), u'value' ) == x509.NameAttribute( - x509.ObjectIdentifier('oid'), u'value' + x509.ObjectIdentifier('2.999.1'), u'value' ) def test_ne(self): @@ -3228,12 +3228,12 @@ class TestNameAttribute(object): x509.ObjectIdentifier('2.5.4.5'), u'value' ) assert x509.NameAttribute( - x509.ObjectIdentifier('oid'), u'value' + x509.ObjectIdentifier('2.999.1'), u'value' ) != x509.NameAttribute( - x509.ObjectIdentifier('oid'), u'value2' + x509.ObjectIdentifier('2.999.1'), u'value2' ) assert x509.NameAttribute( - x509.ObjectIdentifier('oid'), u'value' + x509.ObjectIdentifier('2.999.2'), u'value' ) != object() def test_repr(self): @@ -3252,64 +3252,87 @@ class TestNameAttribute(object): class TestObjectIdentifier(object): def test_eq(self): - oid1 = x509.ObjectIdentifier('oid') - oid2 = x509.ObjectIdentifier('oid') + oid1 = x509.ObjectIdentifier('2.999.1') + oid2 = x509.ObjectIdentifier('2.999.1') assert oid1 == oid2 def test_ne(self): - oid1 = x509.ObjectIdentifier('oid') - assert oid1 != x509.ObjectIdentifier('oid1') + oid1 = x509.ObjectIdentifier('2.999.1') + assert oid1 != x509.ObjectIdentifier('2.999.2') assert oid1 != object() def test_repr(self): oid = x509.ObjectIdentifier("2.5.4.3") assert repr(oid) == "<ObjectIdentifier(oid=2.5.4.3, name=commonName)>" - oid = x509.ObjectIdentifier("oid1") - assert repr(oid) == "<ObjectIdentifier(oid=oid1, name=Unknown OID)>" + oid = x509.ObjectIdentifier("2.999.1") + assert repr(oid) == "<ObjectIdentifier(oid=2.999.1, name=Unknown OID)>" def test_name_property(self): oid = x509.ObjectIdentifier("2.5.4.3") assert oid._name == 'commonName' - oid = x509.ObjectIdentifier("oid1") + oid = x509.ObjectIdentifier("2.999.1") assert oid._name == 'Unknown OID' + def test_too_short(self): + with pytest.raises(ValueError): + x509.ObjectIdentifier("1") + + def test_invalid_input(self): + with pytest.raises(ValueError): + x509.ObjectIdentifier("notavalidform") + + def test_invalid_node1(self): + with pytest.raises(ValueError): + x509.ObjectIdentifier("7.1.37") + + def test_invalid_node2(self): + with pytest.raises(ValueError): + x509.ObjectIdentifier("1.50.200") + + def test_valid(self): + x509.ObjectIdentifier("0.35.200") + x509.ObjectIdentifier("1.39.999") + x509.ObjectIdentifier("2.5.29.3") + x509.ObjectIdentifier("2.999.37.5.22.8") + x509.ObjectIdentifier("2.25.305821105408246119474742976030998643995") + class TestName(object): def test_eq(self): name1 = x509.Name([ - x509.NameAttribute(x509.ObjectIdentifier('oid'), u'value1'), - x509.NameAttribute(x509.ObjectIdentifier('oid2'), u'value2'), + x509.NameAttribute(x509.ObjectIdentifier('2.999.1'), u'value1'), + x509.NameAttribute(x509.ObjectIdentifier('2.999.2'), u'value2'), ]) name2 = x509.Name([ - x509.NameAttribute(x509.ObjectIdentifier('oid'), u'value1'), - x509.NameAttribute(x509.ObjectIdentifier('oid2'), u'value2'), + x509.NameAttribute(x509.ObjectIdentifier('2.999.1'), u'value1'), + x509.NameAttribute(x509.ObjectIdentifier('2.999.2'), u'value2'), ]) assert name1 == name2 def test_ne(self): name1 = x509.Name([ - x509.NameAttribute(x509.ObjectIdentifier('oid'), u'value1'), - x509.NameAttribute(x509.ObjectIdentifier('oid2'), u'value2'), + x509.NameAttribute(x509.ObjectIdentifier('2.999.1'), u'value1'), + x509.NameAttribute(x509.ObjectIdentifier('2.999.2'), u'value2'), ]) name2 = x509.Name([ - x509.NameAttribute(x509.ObjectIdentifier('oid2'), u'value2'), - x509.NameAttribute(x509.ObjectIdentifier('oid'), u'value1'), + x509.NameAttribute(x509.ObjectIdentifier('2.999.2'), u'value2'), + x509.NameAttribute(x509.ObjectIdentifier('2.999.1'), u'value1'), ]) assert name1 != name2 assert name1 != object() def test_hash(self): name1 = x509.Name([ - x509.NameAttribute(x509.ObjectIdentifier('oid'), u'value1'), - x509.NameAttribute(x509.ObjectIdentifier('oid2'), u'value2'), + x509.NameAttribute(x509.ObjectIdentifier('2.999.1'), u'value1'), + x509.NameAttribute(x509.ObjectIdentifier('2.999.2'), u'value2'), ]) name2 = x509.Name([ - x509.NameAttribute(x509.ObjectIdentifier('oid'), u'value1'), - x509.NameAttribute(x509.ObjectIdentifier('oid2'), u'value2'), + x509.NameAttribute(x509.ObjectIdentifier('2.999.1'), u'value1'), + x509.NameAttribute(x509.ObjectIdentifier('2.999.2'), u'value2'), ]) name3 = x509.Name([ - x509.NameAttribute(x509.ObjectIdentifier('oid2'), u'value2'), - x509.NameAttribute(x509.ObjectIdentifier('oid'), u'value1'), + x509.NameAttribute(x509.ObjectIdentifier('2.999.2'), u'value2'), + x509.NameAttribute(x509.ObjectIdentifier('2.999.1'), u'value1'), ]) assert hash(name1) == hash(name2) diff --git a/tests/test_x509_ext.py b/tests/test_x509_ext.py index 8f469366..751de08d 100644 --- a/tests/test_x509_ext.py +++ b/tests/test_x509_ext.py @@ -18,7 +18,8 @@ from cryptography.hazmat.backends.interfaces import ( ) from cryptography.hazmat.primitives.asymmetric import ec from cryptography.x509.oid import ( - AuthorityInformationAccessOID, ExtendedKeyUsageOID, ExtensionOID, NameOID + AuthorityInformationAccessOID, ExtendedKeyUsageOID, + ExtensionOID, NameOID ) from .hazmat.primitives.test_ec import _skip_curve_unsupported @@ -603,8 +604,14 @@ class TestAuthorityKeyIdentifier(object): def test_authority_cert_serial_number_not_integer(self): dirname = x509.DirectoryName( x509.Name([ - x509.NameAttribute(x509.ObjectIdentifier('oid'), u'value1'), - x509.NameAttribute(x509.ObjectIdentifier('oid2'), u'value2'), + x509.NameAttribute( + x509.ObjectIdentifier('2.999.1'), + u'value1' + ), + x509.NameAttribute( + x509.ObjectIdentifier('2.999.2'), + u'value2' + ), ]) ) with pytest.raises(TypeError): @@ -617,8 +624,14 @@ class TestAuthorityKeyIdentifier(object): def test_authority_issuer_not_none_serial_none(self): dirname = x509.DirectoryName( x509.Name([ - x509.NameAttribute(x509.ObjectIdentifier('oid'), u'value1'), - x509.NameAttribute(x509.ObjectIdentifier('oid2'), u'value2'), + x509.NameAttribute( + x509.ObjectIdentifier('2.999.1'), + u'value1' + ), + x509.NameAttribute( + x509.ObjectIdentifier('2.999.2'), + u'value2' + ), ]) ) with pytest.raises(ValueError): @@ -1166,10 +1179,10 @@ class TestDirectoryName(object): def test_eq(self): name = x509.Name([ - x509.NameAttribute(x509.ObjectIdentifier('oid'), u'value1') + x509.NameAttribute(x509.ObjectIdentifier('2.999.1'), u'value1') ]) name2 = x509.Name([ - x509.NameAttribute(x509.ObjectIdentifier('oid'), u'value1') + x509.NameAttribute(x509.ObjectIdentifier('2.999.1'), u'value1') ]) gn = x509.DirectoryName(x509.Name([name])) gn2 = x509.DirectoryName(x509.Name([name2])) @@ -1177,10 +1190,10 @@ class TestDirectoryName(object): def test_ne(self): name = x509.Name([ - x509.NameAttribute(x509.ObjectIdentifier('oid'), u'value1') + x509.NameAttribute(x509.ObjectIdentifier('2.999.1'), u'value1') ]) name2 = x509.Name([ - x509.NameAttribute(x509.ObjectIdentifier('oid'), u'value2') + x509.NameAttribute(x509.ObjectIdentifier('2.999.2'), u'value2') ]) gn = x509.DirectoryName(x509.Name([name])) gn2 = x509.DirectoryName(x509.Name([name2])) |