diff options
| author | Alex Gaynor <alex.gaynor@gmail.com> | 2023-03-20 20:16:53 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-03-21 08:16:53 +0800 |
| commit | f371af837a6785959e52ac4c84e80f0453c542f1 (patch) | |
| tree | 087b340b6ffe83aceb5e56c78af85ed8c53513a5 /src | |
| parent | 328f04dd8a575540ef493613c08f3a521365ce8f (diff) | |
| download | cryptography-f371af837a6785959e52ac4c84e80f0453c542f1.tar.gz | |
Added support for handling python buffers in Rust code (#8556)
This is extra mega cursed, and strictly speaking unsound. It does, however, match the status quo ante, where someone mutating a buffer while its being used in cffi code will basically always be UB.
Diffstat (limited to 'src')
| -rw-r--r-- | src/cryptography/hazmat/primitives/serialization/pkcs7.py | 2 | ||||
| -rw-r--r-- | src/cryptography/utils.py | 7 | ||||
| -rw-r--r-- | src/rust/src/buf.rs | 45 | ||||
| -rw-r--r-- | src/rust/src/lib.rs | 1 | ||||
| -rw-r--r-- | src/rust/src/pkcs7.rs | 10 |
5 files changed, 61 insertions, 4 deletions
diff --git a/src/cryptography/hazmat/primitives/serialization/pkcs7.py b/src/cryptography/hazmat/primitives/serialization/pkcs7.py index 59b3ab99d..0a72e0df8 100644 --- a/src/cryptography/hazmat/primitives/serialization/pkcs7.py +++ b/src/cryptography/hazmat/primitives/serialization/pkcs7.py @@ -78,7 +78,7 @@ class PKCS7SignatureBuilder: if self._data is not None: raise ValueError("data may only be set once") - return PKCS7SignatureBuilder(bytes(data), self._signers) + return PKCS7SignatureBuilder(data, self._signers) def add_signer( self, diff --git a/src/cryptography/utils.py b/src/cryptography/utils.py index b8da26bdd..a84069f1c 100644 --- a/src/cryptography/utils.py +++ b/src/cryptography/utils.py @@ -43,6 +43,13 @@ def int_to_bytes(integer: int, length: typing.Optional[int] = None) -> bytes: ) +def _extract_buffer_length(obj: typing.Any) -> typing.Tuple[int, int]: + from cryptography.hazmat.bindings._rust import _openssl + + buf = _openssl.ffi.from_buffer(obj) + return int(_openssl.ffi.cast("intptr_t", buf)), len(buf) + + class InterfaceNotImplemented(Exception): pass diff --git a/src/rust/src/buf.rs b/src/rust/src/buf.rs new file mode 100644 index 000000000..23dddfd26 --- /dev/null +++ b/src/rust/src/buf.rs @@ -0,0 +1,45 @@ +// 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. + +use std::{ptr, slice}; + +pub(crate) struct CffiBuf<'p> { + _pyobj: &'p pyo3::PyAny, + buf: &'p [u8], +} + +impl CffiBuf<'_> { + pub(crate) fn as_bytes(&self) -> &[u8] { + self.buf + } +} + +impl<'a> pyo3::conversion::FromPyObject<'a> for CffiBuf<'a> { + fn extract(pyobj: &'a pyo3::PyAny) -> pyo3::PyResult<Self> { + let py = pyobj.py(); + + let (ptrval, len): (usize, usize) = py + .import("cryptography.utils")? + .call_method1("_extract_buffer_length", (pyobj,))? + .extract()?; + let ptr = if len == 0 { + ptr::NonNull::dangling().as_ptr() + } else { + ptrval as *const u8 + }; + + Ok(CffiBuf { + _pyobj: pyobj, + // SAFETY: _extract_buffer_length ensures that we have a valid ptr + // and length (and we ensure we meet slice's requirements for + // 0-length slices above), we're keeping pyobj alive which ensures + // the buffer is valid. But! There is no actually guarantee + // against concurrent mutation. See + // https://alexgaynor.net/2022/oct/23/buffers-on-the-edge/ + // for details. This is the same as our cffi status quo ante, so + // we're doing an unsound thing and living with it. + buf: unsafe { slice::from_raw_parts(ptr, len) }, + }) + } +} diff --git a/src/rust/src/lib.rs b/src/rust/src/lib.rs index 90ff46096..cec552621 100644 --- a/src/rust/src/lib.rs +++ b/src/rust/src/lib.rs @@ -10,6 +10,7 @@ #![allow(unknown_lints, clippy::borrow_deref_ref)] mod asn1; +mod buf; mod error; mod intern; pub(crate) mod oid; diff --git a/src/rust/src/pkcs7.rs b/src/rust/src/pkcs7.rs index 93d9a11e4..da2a6561b 100644 --- a/src/rust/src/pkcs7.rs +++ b/src/rust/src/pkcs7.rs @@ -3,6 +3,7 @@ // for complete details. use crate::asn1::encode_der_data; +use crate::buf::CffiBuf; use crate::error::CryptographyResult; use crate::x509; @@ -135,13 +136,16 @@ fn sign_and_serialize<'p>( .import("cryptography.hazmat.primitives.serialization.pkcs7")? .getattr(crate::intern!(py, "PKCS7Options"))?; - let raw_data = builder.getattr(crate::intern!(py, "_data"))?.extract()?; + let raw_data: CffiBuf<'p> = builder.getattr(crate::intern!(py, "_data"))?.extract()?; let text_mode = options.contains(pkcs7_options.getattr(crate::intern!(py, "Text"))?)?; let (data_with_header, data_without_header) = if options.contains(pkcs7_options.getattr(crate::intern!(py, "Binary"))?)? { - (Cow::Borrowed(raw_data), Cow::Borrowed(raw_data)) + ( + Cow::Borrowed(raw_data.as_bytes()), + Cow::Borrowed(raw_data.as_bytes()), + ) } else { - smime_canonicalize(raw_data, text_mode) + smime_canonicalize(raw_data.as_bytes(), text_mode) }; let content_type_bytes = asn1::write_single(&PKCS7_DATA_OID)?; |
