ctclient_async/internal/
openssl_ffi.rs

1//! Because `openssl` crate is incomplete.
2
3use std::convert::TryFrom;
4use std::ptr::null_mut;
5
6use foreign_types::{ForeignType, ForeignTypeRef};
7use openssl::error::ErrorStack;
8use openssl::stack::{Stack, Stackable};
9use openssl::x509::X509Ref;
10use openssl_sys::ASN1_OBJECT;
11
12use foreign::*;
13
14/// Because `openssl_sys` crate is incomplete.
15#[allow(non_camel_case_types)]
16pub mod foreign {
17    pub enum SCT_LIST {}
18    pub enum SCT {}
19
20    pub type sct_version_t = i32;
21    pub const SCT_VERSION_NOT_SET: sct_version_t = -1;
22    pub const SCT_VERSION_V1: sct_version_t = 0;
23
24    unsafe extern "C" {
25        pub fn i2d_re_X509_tbs(
26            x: *mut openssl_sys::X509,
27            pp: *mut *mut std::os::raw::c_uchar,
28        ) -> std::os::raw::c_int;
29
30        pub fn X509_get_ext_by_OBJ(
31            x: *const openssl_sys::X509,
32            obj: *const openssl_sys::ASN1_OBJECT,
33            lastpos: ::std::os::raw::c_int,
34        ) -> ::std::os::raw::c_int;
35
36        pub fn X509_get_ext(
37            x: *const openssl_sys::X509,
38            loc: ::std::os::raw::c_int,
39        ) -> *mut openssl_sys::X509_EXTENSION;
40        pub fn X509_delete_ext(
41            x: *mut openssl_sys::X509,
42            loc: ::std::os::raw::c_int,
43        ) -> *mut openssl_sys::X509_EXTENSION;
44
45        pub fn OBJ_txt2obj(
46            s: *const ::std::os::raw::c_char,
47            no_name: ::std::os::raw::c_int,
48        ) -> *mut openssl_sys::ASN1_OBJECT;
49
50        pub fn ASN1_OBJECT_free(a: *mut openssl_sys::ASN1_OBJECT);
51
52        pub fn X509_EXTENSION_free(a: *mut openssl_sys::X509_EXTENSION);
53
54        pub fn X509_dup(x509: *mut openssl_sys::X509) -> *mut openssl_sys::X509;
55
56        pub fn X509_EXTENSION_get_data(
57            ne: *mut openssl_sys::X509_EXTENSION,
58        ) -> *mut openssl_sys::ASN1_OCTET_STRING;
59        pub fn X509_EXTENSION_set_data(
60            ex: *mut openssl_sys::X509_EXTENSION,
61            data: *mut openssl_sys::ASN1_OCTET_STRING,
62        ) -> ::std::os::raw::c_int;
63
64        pub fn d2i_SCT_LIST(
65            a: *mut *mut SCT_LIST,
66            pp: *mut *const ::std::os::raw::c_uchar,
67            len: ::std::os::raw::c_long,
68        ) -> *mut SCT_LIST;
69
70        pub fn SCT_LIST_free(a: *mut SCT_LIST);
71
72        pub fn SCT_get_version(sct: *const SCT) -> sct_version_t;
73        pub fn SCT_get0_log_id(
74            sct: *const SCT,
75            log_id: *mut *mut ::std::os::raw::c_uchar,
76        ) -> ::std::os::raw::c_ulong;
77        pub fn SCT_get_timestamp(sct: *const SCT) -> u64;
78        pub fn SCT_get0_extensions(
79            sct: *const SCT,
80            ext: *mut *mut ::std::os::raw::c_uchar,
81        ) -> ::std::os::raw::c_ulong;
82        pub fn SCT_get_signature_nid(sct: *const SCT) -> ::std::os::raw::c_int;
83        pub fn SCT_get0_signature(
84            sct: *const SCT,
85            sig: *mut *mut ::std::os::raw::c_uchar,
86        ) -> ::std::os::raw::c_ulong;
87        pub fn SCT_free(sct: *mut SCT);
88
89        pub fn X509_set_issuer_name(
90            x: *mut openssl_sys::X509,
91            name: *mut openssl_sys::X509_NAME,
92        ) -> ::std::os::raw::c_int;
93        pub fn X509_get_subject_name(a: *const openssl_sys::X509) -> *mut openssl_sys::X509_NAME;
94
95        pub fn ASN1_STRING_new() -> *mut openssl_sys::ASN1_STRING;
96        pub fn ASN1_STRING_set(
97            str: *mut openssl_sys::ASN1_STRING,
98            data: *const ::std::os::raw::c_void,
99            len: ::std::os::raw::c_int,
100        ) -> ::std::os::raw::c_int;
101    }
102}
103
104foreign_types::foreign_type! {
105  type CType = foreign::SCT;
106  fn drop = foreign::SCT_free;
107  /// An owned reference to a openssl `SCT` struct.
108  pub struct Sct;
109  /// A reference to a openssl `SCT` struct.
110  pub struct SctRef;
111}
112
113impl Stackable for Sct {
114    type StackType = foreign::SCT_LIST;
115}
116
117/// An owned `STACK_OF(SCT)`.
118pub type SctList = Stack<Sct>;
119
120pub fn x509_clone<R: AsRef<X509Ref>>(src: &R) -> Result<openssl::x509::X509, ErrorStack> {
121    unsafe {
122        let cloned_ptr = X509_dup(src.as_ref().as_ptr());
123        if cloned_ptr.is_null() {
124            return Err(ErrorStack::get());
125        }
126        Ok(openssl::x509::X509::from_ptr(cloned_ptr))
127    }
128}
129
130struct WrappedObjPointer(*mut ASN1_OBJECT);
131
132unsafe impl Sync for WrappedObjPointer {}
133
134impl Drop for WrappedObjPointer {
135    fn drop(&mut self) {
136        let ptr = self.0;
137        unsafe {
138            ASN1_OBJECT_free(ptr);
139        }
140    }
141}
142
143unsafe fn oid_to_obj(zero_terminated_oid: &'static str) -> WrappedObjPointer {
144    unsafe {
145        let ptr = OBJ_txt2obj(zero_terminated_oid.as_ptr() as *const _, 1);
146        if ptr.is_null() {
147            panic!("OBJ_txt2obj failed.");
148        }
149        WrappedObjPointer(ptr)
150    }
151}
152
153lazy_static! {
154    static ref POISON_ASN1_OBJECT: WrappedObjPointer =
155        unsafe { oid_to_obj("1.3.6.1.4.1.11129.2.4.3\0") };
156    static ref SCT_LIST_ASN1_OBJECT: WrappedObjPointer =
157        unsafe { oid_to_obj("1.3.6.1.4.1.11129.2.4.2\0") };
158    static ref AUTHORITY_KEY_IDENTIFIER: WrappedObjPointer = unsafe { oid_to_obj("2.5.29.35\0") };
159    static ref SUBJECT_KEY_IDENTIFIER: WrappedObjPointer = unsafe { oid_to_obj("2.5.29.14\0") };
160}
161
162unsafe fn x509_remove_extension_by_obj(
163    cert: &mut openssl::x509::X509,
164    obj: *const ASN1_OBJECT,
165) -> Result<(), ErrorStack> {
166    unsafe {
167        let extpos = X509_get_ext_by_OBJ(cert.as_ptr(), obj, -1);
168        if extpos == -1 {
169            return Ok(());
170        }
171        let ext = X509_delete_ext(cert.as_ptr(), extpos);
172        if ext.is_null() {
173            Err(ErrorStack::get())
174        } else {
175            X509_EXTENSION_free(ext);
176            Ok(())
177        }
178    }
179}
180
181pub fn x509_remove_poison(cert: &mut openssl::x509::X509) -> Result<(), ErrorStack> {
182    unsafe { x509_remove_extension_by_obj(cert, POISON_ASN1_OBJECT.0) }
183}
184
185pub fn x509_remove_sct_list(
186    cert: &mut openssl::x509::X509,
187) -> Result<(), openssl::error::ErrorStack> {
188    unsafe { x509_remove_extension_by_obj(cert, SCT_LIST_ASN1_OBJECT.0) }
189}
190
191unsafe fn asn1_string_to_bytes<'a>(asn1_str: *mut openssl_sys::ASN1_STRING) -> &'a [u8] {
192    unsafe {
193        let data_len = usize::try_from(openssl_sys::ASN1_STRING_length(asn1_str)).unwrap();
194        let data_ptr = openssl_sys::ASN1_STRING_get0_data(asn1_str);
195        assert!(!data_ptr.is_null());
196        &*std::ptr::slice_from_raw_parts(data_ptr, data_len)
197    }
198}
199
200fn bytes_to_asn1_string(bytes: &[u8]) -> openssl::asn1::Asn1String {
201    unsafe {
202        let asn1 = openssl::asn1::Asn1String::from_ptr(ASN1_STRING_new());
203        ASN1_STRING_set(asn1.as_ptr(), bytes.as_ptr() as *const _, bytes.len() as _);
204        asn1
205    }
206}
207
208fn x509_get_ext_data<'a>(
209    cert: &'a X509Ref,
210    ext: &WrappedObjPointer,
211) -> Result<Option<&'a [u8]>, ErrorStack> {
212    unsafe {
213        let extpos = X509_get_ext_by_OBJ(cert.as_ptr(), ext.0, -1);
214        if extpos == -1 {
215            return Ok(None);
216        }
217        let ext = X509_get_ext(cert.as_ptr(), extpos);
218        if ext.is_null() {
219            return Err(ErrorStack::get());
220        }
221        // ASN1_OCTET_STRING is the same as ASN1_STRING: https://www.openssl.org/docs/man1.1.1/man3/ASN1_STRING_get0_data.html#NOTES
222        Ok(Some(asn1_string_to_bytes(
223            X509_EXTENSION_get_data(ext) as *mut _
224        )))
225    }
226}
227
228fn x509_set_ext_data(
229    cert: &mut openssl::x509::X509,
230    ext: &WrappedObjPointer,
231    data: &[u8],
232) -> Result<(), crate::Error> {
233    unsafe {
234        use crate::Error;
235        let extpos = X509_get_ext_by_OBJ(cert.as_ptr(), ext.0, -1);
236        if extpos == -1 {
237            return Err(Error::Unknown(
238                "x509_set_ext_data: no such extension".to_owned(),
239            ));
240        }
241        let ext = X509_get_ext(cert.as_ptr(), extpos);
242        if ext.is_null() {
243            return Err(Error::Unknown(ErrorStack::get().to_string()));
244        }
245        X509_EXTENSION_set_data(ext, bytes_to_asn1_string(data).as_ptr() as *mut _);
246        Ok(())
247    }
248}
249
250pub fn x509_to_tbs<R: AsRef<X509Ref>>(cert: &R) -> Result<Vec<u8>, ErrorStack> {
251    unsafe {
252        let mut buf: *mut u8 = null_mut();
253        let ret = i2d_re_X509_tbs(cert.as_ref().as_ptr(), &mut buf as *mut _);
254        if ret < 0 {
255            return Err(ErrorStack::get());
256        }
257        if buf.is_null() {
258            return Ok(Vec::new());
259        }
260        let size = usize::try_from(ret).unwrap();
261        let mut owned_buf = Vec::with_capacity(size);
262        std::ptr::copy_nonoverlapping(buf, owned_buf.as_mut_ptr(), size);
263        owned_buf.set_len(size);
264        openssl_sys::CRYPTO_free(
265            buf as *mut _,
266            c"openssl_ffi.rs".as_ptr() as *const _,
267            line!() as i32,
268        );
269        Ok(owned_buf)
270    }
271}
272
273pub fn sct_list_from_x509<R: AsRef<X509Ref>>(cert: &R) -> Result<Option<SctList>, crate::Error> {
274    let data = x509_get_ext_data(cert.as_ref(), &SCT_LIST_ASN1_OBJECT)
275        .map_err(|e| crate::Error::BadCertificate(format!("{}", e)))?;
276    if data.is_none() {
277        return Ok(None);
278    }
279    let data = data.unwrap();
280    if data.is_empty() {
281        return Ok(None);
282    }
283    let mut pp = data.as_ptr();
284    unsafe {
285        let res = d2i_SCT_LIST(
286            std::ptr::null_mut(),
287            &mut pp as *mut _,
288            i64::try_from(data.len()).unwrap(),
289        );
290        if res.is_null() {
291            return Err(crate::Error::BadSct(format!("{}", ErrorStack::get())));
292        }
293        if pp != data.as_ptr().add(data.len()) {
294            return Err(crate::Error::BadSct(
295                "SCT extension data not fully consumed.".to_owned(),
296            ));
297        }
298        Ok(Some(SctList::from_ptr(res)))
299    }
300}
301
302#[derive(Debug, Eq, PartialEq, Copy, Clone)]
303pub enum SCTVersion {
304    V1,
305}
306#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Copy, Clone)]
307pub enum SignatureAlgorithm {
308    Sha256Rsa,
309    Sha256Ecdsa,
310}
311
312macro_rules! impl_get_data_fn {
313    ($fnname:ident, $fn:expr) => {
314        pub fn $fnname(&self) -> &[u8] {
315            unsafe {
316                let mut ptr: *mut u8 = std::ptr::null_mut();
317                let size = $fn(self.as_ptr(), &mut ptr as *mut _);
318                &*std::ptr::slice_from_raw_parts(ptr, size as usize)
319            }
320        }
321    };
322}
323
324impl SctRef {
325    pub fn version(&self) -> Option<SCTVersion> {
326        let raw_version = unsafe { SCT_get_version(self.as_ptr()) };
327        match raw_version {
328            SCT_VERSION_V1 => Some(SCTVersion::V1),
329            _ => None,
330        }
331    }
332
333    impl_get_data_fn!(log_id, SCT_get0_log_id);
334
335    pub fn timestamp(&self) -> u64 {
336        unsafe { SCT_get_timestamp(self.as_ptr()) }
337    }
338
339    impl_get_data_fn!(extensions, SCT_get0_extensions);
340
341    pub fn signature_algorithm(&self) -> Option<SignatureAlgorithm> {
342        let nid = unsafe { SCT_get_signature_nid(self.as_ptr()) };
343        match nid {
344            668 => Some(SignatureAlgorithm::Sha256Rsa),
345            794 => Some(SignatureAlgorithm::Sha256Ecdsa),
346            _ => None,
347        }
348    }
349
350    impl_get_data_fn!(raw_signature, SCT_get0_signature);
351}
352
353/// Set the issuer name of `dst` to be the subject name of `src`, and also set the authorityKeyIdentifier of a to the
354/// subjectKeyIdentifier of b.
355pub fn x509_make_a_looks_like_issued_by_b(
356    a: &mut openssl::x509::X509,
357    b: &openssl::x509::X509Ref,
358) -> Result<(), crate::Error> {
359    use crate::Error;
360    let subj_name = unsafe { X509_get_subject_name(b.as_ptr()) };
361    // subj_name is an internal pointer to data in src.
362    let ret = unsafe { X509_set_issuer_name(a.as_ptr(), subj_name) };
363    // X509_set_issuer_name copies the data pointed to by subj_name.
364    if ret != 1 {
365        Err(Error::Unknown(ErrorStack::get().to_string()))
366    } else {
367        let subj_auth_keyid = x509_get_ext_data(b, &SUBJECT_KEY_IDENTIFIER)
368            .map_err(|e| Error::Unknown(e.to_string()))?
369            .ok_or_else(|| Error::Unknown("x509_get_ext_data returned None".to_owned()))?;
370        if subj_auth_keyid.len() < 2 || subj_auth_keyid.len() > (1 << 8) - 1 {
371            return Err(Error::BadCertificate("Bad subjectKeyIdentifier".to_owned()));
372        }
373        let key = &subj_auth_keyid[2..];
374        if subj_auth_keyid[0..2] != [0x04, key.len() as u8] {
375            return Err(Error::BadCertificate("Bad subjectKeyIdentifier".to_owned()));
376        }
377        let mut auth_data = vec![0x30, (key.len() + 2) as u8, 0x80, key.len() as u8];
378        auth_data.extend_from_slice(key);
379        x509_set_ext_data(a, &AUTHORITY_KEY_IDENTIFIER, &auth_data)?;
380        Ok(())
381    }
382}