1use 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#[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 pub struct Sct;
109 pub struct SctRef;
111}
112
113impl Stackable for Sct {
114 type StackType = foreign::SCT_LIST;
115}
116
117pub 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
134unsafe impl Send for WrappedObjPointer {}
140
141impl Drop for WrappedObjPointer {
142 fn drop(&mut self) {
143 let ptr = self.0;
144 unsafe {
145 ASN1_OBJECT_free(ptr);
146 }
147 }
148}
149
150unsafe fn oid_to_obj(zero_terminated_oid: &'static str) -> WrappedObjPointer {
151 unsafe {
152 let ptr = OBJ_txt2obj(zero_terminated_oid.as_ptr() as *const _, 1);
153 if ptr.is_null() {
154 panic!("OBJ_txt2obj failed.");
155 }
156 WrappedObjPointer(ptr)
157 }
158}
159
160lazy_static! {
161 static ref POISON_ASN1_OBJECT: WrappedObjPointer =
162 unsafe { oid_to_obj("1.3.6.1.4.1.11129.2.4.3\0") };
163 static ref SCT_LIST_ASN1_OBJECT: WrappedObjPointer =
164 unsafe { oid_to_obj("1.3.6.1.4.1.11129.2.4.2\0") };
165 static ref AUTHORITY_KEY_IDENTIFIER: WrappedObjPointer = unsafe { oid_to_obj("2.5.29.35\0") };
166 static ref SUBJECT_KEY_IDENTIFIER: WrappedObjPointer = unsafe { oid_to_obj("2.5.29.14\0") };
167}
168
169unsafe fn x509_remove_extension_by_obj(
170 cert: &mut openssl::x509::X509,
171 obj: *const ASN1_OBJECT,
172) -> Result<(), ErrorStack> {
173 unsafe {
174 let extpos = X509_get_ext_by_OBJ(cert.as_ptr(), obj, -1);
175 if extpos == -1 {
176 return Ok(());
177 }
178 let ext = X509_delete_ext(cert.as_ptr(), extpos);
179 if ext.is_null() {
180 Err(ErrorStack::get())
181 } else {
182 X509_EXTENSION_free(ext);
183 Ok(())
184 }
185 }
186}
187
188pub fn x509_remove_poison(cert: &mut openssl::x509::X509) -> Result<(), ErrorStack> {
189 unsafe { x509_remove_extension_by_obj(cert, POISON_ASN1_OBJECT.0) }
190}
191
192pub fn x509_remove_sct_list(
193 cert: &mut openssl::x509::X509,
194) -> Result<(), openssl::error::ErrorStack> {
195 unsafe { x509_remove_extension_by_obj(cert, SCT_LIST_ASN1_OBJECT.0) }
196}
197
198unsafe fn asn1_string_to_bytes<'a>(asn1_str: *mut openssl_sys::ASN1_STRING) -> &'a [u8] {
199 unsafe {
200 let data_len = usize::try_from(openssl_sys::ASN1_STRING_length(asn1_str)).unwrap();
201 let data_ptr = openssl_sys::ASN1_STRING_get0_data(asn1_str);
202 assert!(!data_ptr.is_null());
203 &*std::ptr::slice_from_raw_parts(data_ptr, data_len)
204 }
205}
206
207fn bytes_to_asn1_string(bytes: &[u8]) -> openssl::asn1::Asn1String {
208 unsafe {
209 let asn1 = openssl::asn1::Asn1String::from_ptr(ASN1_STRING_new());
210 ASN1_STRING_set(asn1.as_ptr(), bytes.as_ptr() as *const _, bytes.len() as _);
211 asn1
212 }
213}
214
215fn x509_get_ext_data<'a>(
216 cert: &'a X509Ref,
217 ext: &WrappedObjPointer,
218) -> Result<Option<&'a [u8]>, ErrorStack> {
219 unsafe {
220 let extpos = X509_get_ext_by_OBJ(cert.as_ptr(), ext.0, -1);
221 if extpos == -1 {
222 return Ok(None);
223 }
224 let ext = X509_get_ext(cert.as_ptr(), extpos);
225 if ext.is_null() {
226 return Err(ErrorStack::get());
227 }
228 Ok(Some(asn1_string_to_bytes(
230 X509_EXTENSION_get_data(ext) as *mut _
231 )))
232 }
233}
234
235fn x509_set_ext_data(
236 cert: &mut openssl::x509::X509,
237 ext: &WrappedObjPointer,
238 data: &[u8],
239) -> Result<(), crate::Error> {
240 unsafe {
241 use crate::Error;
242 let extpos = X509_get_ext_by_OBJ(cert.as_ptr(), ext.0, -1);
243 if extpos == -1 {
244 return Err(Error::Unknown(
245 "x509_set_ext_data: no such extension".to_owned(),
246 ));
247 }
248 let ext = X509_get_ext(cert.as_ptr(), extpos);
249 if ext.is_null() {
250 return Err(Error::Unknown(ErrorStack::get().to_string()));
251 }
252 X509_EXTENSION_set_data(ext, bytes_to_asn1_string(data).as_ptr() as *mut _);
253 Ok(())
254 }
255}
256
257pub fn x509_to_tbs<R: AsRef<X509Ref>>(cert: &R) -> Result<Vec<u8>, ErrorStack> {
258 unsafe {
259 let mut buf: *mut u8 = null_mut();
260 let ret = i2d_re_X509_tbs(cert.as_ref().as_ptr(), &mut buf as *mut _);
261 if ret < 0 {
262 return Err(ErrorStack::get());
263 }
264 if buf.is_null() {
265 return Ok(Vec::new());
266 }
267 let size = usize::try_from(ret).unwrap();
268 let mut owned_buf = Vec::with_capacity(size);
269 std::ptr::copy_nonoverlapping(buf, owned_buf.as_mut_ptr(), size);
270 owned_buf.set_len(size);
271 openssl_sys::CRYPTO_free(
272 buf as *mut _,
273 c"openssl_ffi.rs".as_ptr() as *const _,
274 line!() as i32,
275 );
276 Ok(owned_buf)
277 }
278}
279
280pub fn sct_list_from_x509<R: AsRef<X509Ref>>(cert: &R) -> Result<Option<SctList>, crate::Error> {
281 let data = x509_get_ext_data(cert.as_ref(), &SCT_LIST_ASN1_OBJECT)
282 .map_err(|e| crate::Error::BadCertificate(format!("{}", e)))?;
283 if data.is_none() {
284 return Ok(None);
285 }
286 let data = data.unwrap();
287 if data.is_empty() {
288 return Ok(None);
289 }
290 let mut pp = data.as_ptr();
291 unsafe {
292 let res = d2i_SCT_LIST(
293 std::ptr::null_mut(),
294 &mut pp as *mut _,
295 i64::try_from(data.len()).unwrap(),
296 );
297 if res.is_null() {
298 return Err(crate::Error::BadSct(format!("{}", ErrorStack::get())));
299 }
300 if pp != data.as_ptr().add(data.len()) {
301 return Err(crate::Error::BadSct(
302 "SCT extension data not fully consumed.".to_owned(),
303 ));
304 }
305 Ok(Some(SctList::from_ptr(res)))
306 }
307}
308
309#[derive(Debug, Eq, PartialEq, Copy, Clone)]
310pub enum SCTVersion {
311 V1,
312}
313#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Copy, Clone)]
314pub enum SignatureAlgorithm {
315 Sha256Rsa,
316 Sha256Ecdsa,
317}
318
319macro_rules! impl_get_data_fn {
320 ($fnname:ident, $fn:expr) => {
321 pub fn $fnname(&self) -> &[u8] {
322 unsafe {
323 let mut ptr: *mut u8 = std::ptr::null_mut();
324 let size = $fn(self.as_ptr(), &mut ptr as *mut _);
325 &*std::ptr::slice_from_raw_parts(ptr, size as usize)
326 }
327 }
328 };
329}
330
331impl SctRef {
332 pub fn version(&self) -> Option<SCTVersion> {
333 let raw_version = unsafe { SCT_get_version(self.as_ptr()) };
334 match raw_version {
335 SCT_VERSION_V1 => Some(SCTVersion::V1),
336 _ => None,
337 }
338 }
339
340 impl_get_data_fn!(log_id, SCT_get0_log_id);
341
342 pub fn timestamp(&self) -> u64 {
343 unsafe { SCT_get_timestamp(self.as_ptr()) }
344 }
345
346 impl_get_data_fn!(extensions, SCT_get0_extensions);
347
348 pub fn signature_algorithm(&self) -> Option<SignatureAlgorithm> {
349 let nid = unsafe { SCT_get_signature_nid(self.as_ptr()) };
350 match nid {
351 668 => Some(SignatureAlgorithm::Sha256Rsa),
352 794 => Some(SignatureAlgorithm::Sha256Ecdsa),
353 _ => None,
354 }
355 }
356
357 impl_get_data_fn!(raw_signature, SCT_get0_signature);
358}
359
360pub fn x509_make_a_looks_like_issued_by_b(
363 a: &mut openssl::x509::X509,
364 b: &openssl::x509::X509Ref,
365) -> Result<(), crate::Error> {
366 use crate::Error;
367 let subj_name = unsafe { X509_get_subject_name(b.as_ptr()) };
368 let ret = unsafe { X509_set_issuer_name(a.as_ptr(), subj_name) };
370 if ret != 1 {
372 Err(Error::Unknown(ErrorStack::get().to_string()))
373 } else {
374 let subj_auth_keyid = x509_get_ext_data(b, &SUBJECT_KEY_IDENTIFIER)
375 .map_err(|e| Error::Unknown(e.to_string()))?
376 .ok_or_else(|| Error::Unknown("x509_get_ext_data returned None".to_owned()))?;
377 if subj_auth_keyid.len() < 2 || subj_auth_keyid.len() > (1 << 8) - 1 {
378 return Err(Error::BadCertificate("Bad subjectKeyIdentifier".to_owned()));
379 }
380 let key = &subj_auth_keyid[2..];
381 if subj_auth_keyid[0..2] != [0x04, key.len() as u8] {
382 return Err(Error::BadCertificate("Bad subjectKeyIdentifier".to_owned()));
383 }
384 let mut auth_data = vec![0x30, (key.len() + 2) as u8, 0x80, key.len() as u8];
385 auth_data.extend_from_slice(key);
386 x509_set_ext_data(a, &AUTHORITY_KEY_IDENTIFIER, &auth_data)?;
387 Ok(())
388 }
389}