summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAlex Gaynor <alex.gaynor@gmail.com>2023-03-20 20:16:53 -0400
committerGitHub <noreply@github.com>2023-03-21 08:16:53 +0800
commitf371af837a6785959e52ac4c84e80f0453c542f1 (patch)
tree087b340b6ffe83aceb5e56c78af85ed8c53513a5 /src
parent328f04dd8a575540ef493613c08f3a521365ce8f (diff)
downloadcryptography-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.py2
-rw-r--r--src/cryptography/utils.py7
-rw-r--r--src/rust/src/buf.rs45
-rw-r--r--src/rust/src/lib.rs1
-rw-r--r--src/rust/src/pkcs7.rs10
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)?;