-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Minimal PoC for putting pyOpenSSL functionality in Rust
- Loading branch information
Showing
10 changed files
with
260 additions
and
8 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
# 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. | ||
|
||
from __future__ import annotations | ||
|
||
import typing | ||
|
||
SSLv23_METHOD: int | ||
TLSv1_METHOD: int | ||
TLSv1_1_METHOD: int | ||
TLSv1_2_METHOD: int | ||
TLS_METHOD: int | ||
TLS_SERVER_METHOD: int | ||
TLS_CLIENT_METHOD: int | ||
DTLS_METHOD: int | ||
DTLS_SERVER_METHOD: int | ||
DTLS_CLIENT_METHOD: int | ||
|
||
class Context: | ||
def __new__(cls, method: int) -> Context: ... | ||
@property | ||
def _context(self) -> typing.Any: ... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
// 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 pyo3::types::PyListMethods; | ||
|
||
pyo3::create_exception!( | ||
OpenSSL.SSL, | ||
Error, | ||
pyo3::exceptions::PyException, | ||
"An error occurred in an `OpenSSL.SSL` API." | ||
); | ||
|
||
pub(crate) enum PyOpenSslError { | ||
Py(pyo3::PyErr), | ||
OpenSSL(openssl::error::ErrorStack), | ||
} | ||
|
||
impl From<pyo3::PyErr> for PyOpenSslError { | ||
fn from(e: pyo3::PyErr) -> PyOpenSslError { | ||
PyOpenSslError::Py(e) | ||
} | ||
} | ||
|
||
impl From<openssl::error::ErrorStack> for PyOpenSslError { | ||
fn from(e: openssl::error::ErrorStack) -> PyOpenSslError { | ||
PyOpenSslError::OpenSSL(e) | ||
} | ||
} | ||
|
||
impl From<PyOpenSslError> for pyo3::PyErr { | ||
fn from(e: PyOpenSslError) -> pyo3::PyErr { | ||
match e { | ||
PyOpenSslError::Py(e) => e, | ||
PyOpenSslError::OpenSSL(e) => pyo3::Python::with_gil(|py| { | ||
let errs = pyo3::types::PyList::empty(py); | ||
for err in e.errors() { | ||
errs.append(( | ||
err.library().unwrap_or(""), | ||
err.function().unwrap_or(""), | ||
err.reason().unwrap_or(""), | ||
))?; | ||
} | ||
Ok(Error::new_err(errs.unbind())) | ||
}) | ||
.unwrap_or_else(|e| e), | ||
} | ||
} | ||
} | ||
|
||
pub(crate) type PyOpenSslResult<T> = Result<T, PyOpenSslError>; | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::{Error, PyOpenSslError}; | ||
|
||
#[test] | ||
fn test_pyopenssl_error_from_openssl_error() { | ||
pyo3::Python::with_gil(|py| { | ||
// Literally anything that returns a non-empty error stack | ||
let err = openssl::x509::X509::from_der(b"").unwrap_err(); | ||
|
||
let py_err: pyo3::PyErr = PyOpenSslError::from(err).into(); | ||
assert!(py_err.is_instance_of::<Error>(py)); | ||
assert!(py_err.to_string().starts_with("Error: [("),); | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
// 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. | ||
|
||
pub(crate) mod error; | ||
pub(crate) mod ssl; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
// 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 pyo3::types::PyAnyMethods; | ||
|
||
use crate::pyopenssl::error::{PyOpenSslError, PyOpenSslResult}; | ||
use crate::types; | ||
|
||
pub(crate) const SSLV23_METHOD: u32 = 3; | ||
pub(crate) const TLSV1_METHOD: u32 = 4; | ||
pub(crate) const TLSV1_1_METHOD: u32 = 5; | ||
pub(crate) const TLSV1_2_METHOD: u32 = 6; | ||
pub(crate) const TLS_METHOD: u32 = 7; | ||
pub(crate) const TLS_SERVER_METHOD: u32 = 8; | ||
pub(crate) const TLS_CLIENT_METHOD: u32 = 9; | ||
pub(crate) const DTLS_METHOD: u32 = 10; | ||
pub(crate) const DTLS_SERVER_METHOD: u32 = 11; | ||
pub(crate) const DTLS_CLIENT_METHOD: u32 = 12; | ||
|
||
#[pyo3::pyclass(subclass, module = "OpenSSL.SSL")] | ||
pub(crate) struct Context { | ||
ssl_ctx: openssl::ssl::SslContextBuilder, | ||
} | ||
|
||
#[pyo3::pymethods] | ||
impl Context { | ||
#[new] | ||
fn new(method: u32) -> PyOpenSslResult<Self> { | ||
let (ssl_method, version) = match method { | ||
SSLV23_METHOD => (openssl::ssl::SslMethod::tls(), None), | ||
TLSV1_METHOD => ( | ||
openssl::ssl::SslMethod::tls(), | ||
Some(openssl::ssl::SslVersion::TLS1), | ||
), | ||
TLSV1_1_METHOD => ( | ||
openssl::ssl::SslMethod::tls(), | ||
Some(openssl::ssl::SslVersion::TLS1_1), | ||
), | ||
TLSV1_2_METHOD => ( | ||
openssl::ssl::SslMethod::tls(), | ||
Some(openssl::ssl::SslVersion::TLS1_2), | ||
), | ||
TLS_METHOD => (openssl::ssl::SslMethod::tls(), None), | ||
TLS_SERVER_METHOD => (openssl::ssl::SslMethod::tls_server(), None), | ||
TLS_CLIENT_METHOD => (openssl::ssl::SslMethod::tls_client(), None), | ||
DTLS_METHOD => (openssl::ssl::SslMethod::dtls(), None), | ||
DTLS_SERVER_METHOD => (openssl::ssl::SslMethod::dtls_server(), None), | ||
DTLS_CLIENT_METHOD => (openssl::ssl::SslMethod::dtls_client(), None), | ||
_ => { | ||
return Err(PyOpenSslError::from( | ||
pyo3::exceptions::PyValueError::new_err("No such protocol"), | ||
)) | ||
} | ||
}; | ||
let mut ssl_ctx = openssl::ssl::SslContext::builder(ssl_method)?; | ||
if let Some(version) = version { | ||
ssl_ctx.set_min_proto_version(Some(version))?; | ||
ssl_ctx.set_max_proto_version(Some(version))?; | ||
} | ||
|
||
Ok(Context { ssl_ctx }) | ||
} | ||
|
||
#[getter] | ||
fn _context<'p>(&self, py: pyo3::Python<'p>) -> PyOpenSslResult<pyo3::Bound<'p, pyo3::PyAny>> { | ||
Ok(types::FFI.get(py)?.call_method1( | ||
pyo3::intern!(py, "cast"), | ||
( | ||
pyo3::intern!(py, "SSL_CTX *"), | ||
self.ssl_ctx.as_ptr() as usize, | ||
), | ||
)?) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# 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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
# 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. | ||
|
||
import pytest | ||
|
||
from cryptography.hazmat.bindings._rust import _openssl, pyopenssl | ||
|
||
|
||
class TestContext: | ||
def test_create(self): | ||
for method in [ | ||
pyopenssl.SSLv23_METHOD, | ||
pyopenssl.TLSv1_METHOD, | ||
pyopenssl.TLSv1_1_METHOD, | ||
pyopenssl.TLSv1_2_METHOD, | ||
pyopenssl.TLS_METHOD, | ||
pyopenssl.TLS_SERVER_METHOD, | ||
pyopenssl.TLS_CLIENT_METHOD, | ||
pyopenssl.DTLS_METHOD, | ||
pyopenssl.DTLS_SERVER_METHOD, | ||
pyopenssl.DTLS_CLIENT_METHOD, | ||
]: | ||
ctx = pyopenssl.Context(method) | ||
assert ctx | ||
|
||
with pytest.raises(TypeError): | ||
pyopenssl.Context(object()) # type: ignore[arg-type] | ||
|
||
with pytest.raises(ValueError): | ||
pyopenssl.Context(12324213) | ||
|
||
def test__context(self): | ||
ctx = pyopenssl.Context(pyopenssl.TLS_METHOD) | ||
assert ctx._context | ||
assert _openssl.ffi.typeof(ctx._context).cname == "SSL_CTX *" | ||
assert _openssl.ffi.cast("uintptr_t", ctx._context) > 0 |