|
1 | 1 | extern crate openssl; |
2 | 2 |
|
| 3 | +use std::io::{Error, ErrorKind}; |
| 4 | + |
| 5 | +use openssl::nid::Nid; |
| 6 | +use openssl::pkcs7::{Pkcs7, Pkcs7Flags}; |
3 | 7 | use openssl::pkey; |
4 | 8 | use openssl::rsa::Rsa; |
5 | | -use pyo3::prelude::*; |
6 | | -use openssl::pkcs7::{Pkcs7, Pkcs7Flags}; |
7 | | -use openssl::stack::Stack; |
8 | | -use openssl::x509::X509; |
| 9 | +use openssl::stack::{Stack, StackRef}; |
| 10 | +use openssl::x509::store::X509StoreBuilder; |
| 11 | +use openssl::x509::{X509Ref, X509}; |
9 | 12 | use pyo3::create_exception; |
10 | 13 | use pyo3::exceptions::PyException; |
| 14 | +use pyo3::prelude::*; |
| 15 | +use pyo3::types::PyBytes; |
11 | 16 |
|
12 | 17 | create_exception!(rsmime, ReadCertificateError, PyException); |
13 | 18 | create_exception!(rsmime, LoadCertificateError, PyException); |
14 | 19 | create_exception!(rsmime, SignError, PyException); |
| 20 | +create_exception!(rsmime, VerifyError, PyException); |
15 | 21 |
|
16 | | -pub fn _sign(cert_file: &str, key_file: &str, data_to_sign: &[u8]) -> PyResult<Vec<u8>> { |
| 22 | +fn _sign(cert_file: &str, key_file: &str, data_to_sign: &[u8]) -> PyResult<Vec<u8>> { |
17 | 23 | let certs = Stack::new().expect("Failed to create stack"); |
18 | 24 |
|
19 | 25 | if data_to_sign.is_empty() { |
20 | 26 | return Err(SignError::new_err("Cannot sign empty data")); |
21 | 27 | } |
22 | | - |
23 | | - let cert_data = std::fs::read(cert_file).map_err(|err| ReadCertificateError::new_err(err.to_string()))?; |
24 | | - let key_data = std::fs::read(key_file).map_err(|err| ReadCertificateError::new_err(err.to_string()))?; |
25 | 28 |
|
26 | | - let cert = X509::from_pem(&cert_data).map_err(|err| LoadCertificateError::new_err(err.to_string()))?; |
27 | | - let rsa = Rsa::private_key_from_pem(&key_data).map_err(|err| LoadCertificateError::new_err(err.to_string()))?; |
28 | | - let pkey = pkey::PKey::from_rsa(rsa).map_err(|err| LoadCertificateError::new_err(err.to_string()))?; |
| 29 | + let cert_data = |
| 30 | + std::fs::read(cert_file).map_err(|err| ReadCertificateError::new_err(err.to_string()))?; |
| 31 | + let key_data = |
| 32 | + std::fs::read(key_file).map_err(|err| ReadCertificateError::new_err(err.to_string()))?; |
| 33 | + |
| 34 | + let cert = |
| 35 | + X509::from_pem(&cert_data).map_err(|err| LoadCertificateError::new_err(err.to_string()))?; |
| 36 | + let rsa = Rsa::private_key_from_pem(&key_data) |
| 37 | + .map_err(|err| LoadCertificateError::new_err(err.to_string()))?; |
| 38 | + let pkey = |
| 39 | + pkey::PKey::from_rsa(rsa).map_err(|err| LoadCertificateError::new_err(err.to_string()))?; |
| 40 | + |
| 41 | + let pkcs7 = Pkcs7::sign( |
| 42 | + cert.as_ref(), |
| 43 | + pkey.as_ref(), |
| 44 | + certs.as_ref(), |
| 45 | + data_to_sign, |
| 46 | + Pkcs7Flags::empty(), |
| 47 | + ) |
| 48 | + .map_err(|err| SignError::new_err(err.to_string()))?; |
| 49 | + let out = pkcs7 |
| 50 | + .to_smime(data_to_sign, Pkcs7Flags::empty()) |
| 51 | + .map_err(|err| SignError::new_err(err.to_string()))?; |
| 52 | + |
| 53 | + Ok(out) |
| 54 | +} |
| 55 | + |
| 56 | +fn cert_subject_to_string(cert: &X509Ref, nid: Nid) -> String { |
| 57 | + let nid_entry = cert.subject_name().entries_by_nid(nid).next().unwrap(); |
| 58 | + nid_entry.data().as_utf8().unwrap().to_string() |
| 59 | +} |
| 60 | + |
| 61 | +fn validate_expiry(certs: &StackRef<X509>) -> Result<(), Error> { |
| 62 | + for cert in certs.iter() { |
| 63 | + let expire = cert.not_after(); |
| 64 | + if expire.le(&openssl::asn1::Asn1Time::days_from_now(0).unwrap()) { |
| 65 | + let expire_string = expire.to_string(); |
| 66 | + let subject_name = cert_subject_to_string(cert, Nid::COMMONNAME); |
| 67 | + return Err(Error::new( |
| 68 | + ErrorKind::Other, |
| 69 | + format!("Certificate {subject_name} expired {expire_string}"), |
| 70 | + )); |
| 71 | + } |
| 72 | + } |
| 73 | + Ok(()) |
| 74 | +} |
| 75 | + |
| 76 | +fn _verify(cert_file: &str, data_to_verify: &[u8], throw_on_expiry: bool) -> PyResult<Vec<u8>> { |
| 77 | + let cert_data = |
| 78 | + std::fs::read(cert_file).map_err(|err| ReadCertificateError::new_err(err.to_string()))?; |
| 79 | + let cert = |
| 80 | + X509::from_pem(&cert_data).map_err(|err| LoadCertificateError::new_err(err.to_string()))?; |
| 81 | + |
| 82 | + let mut certs = Stack::new().expect("Failed to create stack"); |
| 83 | + certs |
| 84 | + .push(cert) |
| 85 | + .map_err(|err| LoadCertificateError::new_err(err.to_string()))?; |
29 | 86 |
|
30 | | - let flags = Pkcs7Flags::STREAM; |
31 | | - let pkcs7 = Pkcs7::sign(cert.as_ref(), pkey.as_ref(), certs.as_ref(), data_to_sign, flags).map_err(|err| SignError::new_err(err.to_string()))?; |
32 | | - let encrypted = pkcs7.to_smime(data_to_sign, flags).map_err(|err| SignError::new_err(err.to_string()))?; |
| 87 | + let mut out: Vec<u8> = Vec::new(); |
| 88 | + let store = X509StoreBuilder::new().unwrap().build(); |
| 89 | + |
| 90 | + let x = Pkcs7::from_smime(data_to_verify); |
| 91 | + let x = x.map_err(|err| VerifyError::new_err(err.to_string()))?; |
| 92 | + let (pkcs7, _) = x; |
| 93 | + |
| 94 | + if throw_on_expiry { |
| 95 | + validate_expiry(certs.as_ref()).map_err(|err| VerifyError::new_err(err.to_string()))?; |
| 96 | + } |
33 | 97 |
|
34 | | - Ok(encrypted) |
| 98 | + pkcs7 |
| 99 | + .verify( |
| 100 | + certs.as_ref(), |
| 101 | + store.as_ref(), |
| 102 | + None, |
| 103 | + Some(out.as_mut()), |
| 104 | + Pkcs7Flags::NOVERIFY, |
| 105 | + ) |
| 106 | + .map_err(|err| VerifyError::new_err(err.to_string()))?; |
| 107 | + |
| 108 | + Ok(out) |
35 | 109 | } |
36 | 110 |
|
37 | 111 | #[pyfunction] |
38 | | -fn sign(cert_file: &str, key_file: &str, data_to_sign: Vec<u8>) -> PyResult<String> { |
| 112 | +fn sign(py: Python, cert_file: &str, key_file: &str, data_to_sign: Vec<u8>) -> PyResult<PyObject> { |
39 | 113 | match _sign(cert_file, key_file, &data_to_sign) { |
40 | | - Ok(signed_data) => Ok(String::from_utf8(signed_data).expect("Failed to convert to string")), |
| 114 | + Ok(data) => Ok(PyBytes::new(py, &data).into()), |
| 115 | + Err(err) => Err(err), |
| 116 | + } |
| 117 | +} |
| 118 | + |
| 119 | +#[pyfunction] |
| 120 | +#[pyo3(signature = (cert_file, data_to_verify, *, throw_on_expiry = false))] |
| 121 | +fn verify( |
| 122 | + py: Python, |
| 123 | + cert_file: &str, |
| 124 | + data_to_verify: Vec<u8>, |
| 125 | + throw_on_expiry: bool, |
| 126 | +) -> PyResult<PyObject> { |
| 127 | + match _verify(cert_file, &data_to_verify, throw_on_expiry) { |
| 128 | + Ok(data) => Ok(PyBytes::new(py, &data).into()), |
41 | 129 | Err(err) => Err(err), |
42 | 130 | } |
43 | 131 | } |
44 | 132 |
|
45 | 133 | #[pymodule] |
46 | 134 | fn rsmime(py: Python, m: &PyModule) -> PyResult<()> { |
47 | | - m.add("ReadCertificateError", py.get_type::<ReadCertificateError>())?; |
48 | | - m.add("LoadCertificateError", py.get_type::<LoadCertificateError>())?; |
| 135 | + m.add( |
| 136 | + "ReadCertificateError", |
| 137 | + py.get_type::<ReadCertificateError>(), |
| 138 | + )?; |
| 139 | + m.add( |
| 140 | + "LoadCertificateError", |
| 141 | + py.get_type::<LoadCertificateError>(), |
| 142 | + )?; |
49 | 143 | m.add("SignError", py.get_type::<SignError>())?; |
| 144 | + m.add("VerifyError", py.get_type::<VerifyError>())?; |
50 | 145 | m.add_function(wrap_pyfunction!(sign, m)?)?; |
| 146 | + m.add_function(wrap_pyfunction!(verify, m)?)?; |
51 | 147 | Ok(()) |
52 | 148 | } |
0 commit comments