|
1 | 1 | use std::{cmp::min, slice}; |
2 | 2 |
|
3 | | -use crate::{ffi_panic_boundary_generic, ffi_panic_boundary_unit}; |
| 3 | +use crate::error::rustls_result; |
| 4 | +use crate::{ |
| 5 | + ffi_panic_boundary, ffi_panic_boundary_generic, ffi_panic_boundary_unit, try_ref_from_ptr, |
| 6 | +}; |
4 | 7 | use libc::{c_char, size_t}; |
| 8 | +use std::io::Cursor; |
5 | 9 | use std::os::raw::c_ushort; |
| 10 | +use std::sync::Arc; |
6 | 11 |
|
7 | | -/// All SignatureScheme currently defines in rustls. |
| 12 | +use rustls::{Certificate, PrivateKey}; |
| 13 | +use rustls_pemfile::{certs, pkcs8_private_keys, rsa_private_keys}; |
| 14 | + |
| 15 | +use rustls::sign::CertifiedKey; |
| 16 | +use rustls_result::NullParameter; |
| 17 | + |
| 18 | +/// All SignatureScheme currently defined in rustls. |
8 | 19 | /// At the moment not exposed by rustls itself. |
9 | 20 | #[no_mangle] |
10 | 21 | pub(crate) static ALL_SIGNATURE_SCHEMES: &[rustls::SignatureScheme] = &[ |
@@ -81,3 +92,110 @@ pub extern "C" fn rustls_cipher_get_signature_scheme_name( |
81 | 92 | } |
82 | 93 | } |
83 | 94 | } |
| 95 | + |
| 96 | +/// The complete chain of certificates plus private key for |
| 97 | +/// being certified against someones list of trust anchors (commonly |
| 98 | +/// called root store). Corresponds to `CertifiedKey` in the Rust API. |
| 99 | +pub struct rustls_cipher_certified_key { |
| 100 | + // We use the opaque struct pattern to tell C about our types without |
| 101 | + // telling them what's inside. |
| 102 | + // https://doc.rust-lang.org/nomicon/ffi.html#representing-opaque-structs |
| 103 | + _private: [u8; 0], |
| 104 | +} |
| 105 | + |
| 106 | +#[no_mangle] |
| 107 | +pub extern "C" fn rustls_cipher_certified_key_build( |
| 108 | + cert_chain: *const u8, |
| 109 | + cert_chain_len: size_t, |
| 110 | + private_key: *const u8, |
| 111 | + private_key_len: size_t, |
| 112 | + certified_key_out: *mut *const rustls_cipher_certified_key, |
| 113 | +) -> rustls_result { |
| 114 | + ffi_panic_boundary! { |
| 115 | + let certified_key = match certified_key_build( |
| 116 | + cert_chain, cert_chain_len, private_key, private_key_len) { |
| 117 | + Ok(key) => Box::new(key), |
| 118 | + Err(rr) => return rr, |
| 119 | + }; |
| 120 | + unsafe { |
| 121 | + *certified_key_out = Arc::into_raw(Arc::new(*certified_key)) as *const _; |
| 122 | + } |
| 123 | + return rustls_result::Ok |
| 124 | + } |
| 125 | +} |
| 126 | + |
| 127 | +/// "Free" a certified_key previously returned from |
| 128 | +/// rustls_cipher_certified_key_build. Since certified_key is actually an |
| 129 | +/// atomically reference-counted pointer, extant certified_key may still |
| 130 | +/// hold an internal reference to the Rust object. However, C code must |
| 131 | +/// consider this pointer unusable after "free"ing it. |
| 132 | +/// Calling with NULL is fine. Must not be called twice with the same value. |
| 133 | +#[no_mangle] |
| 134 | +pub extern "C" fn rustls_cipher_certified_key_free(config: *const rustls_cipher_certified_key) { |
| 135 | + ffi_panic_boundary_unit! { |
| 136 | + let key: &CertifiedKey = try_ref_from_ptr!(config, &mut CertifiedKey, ()); |
| 137 | + // To free the certified_key, we reconstruct the Arc. It should have a refcount of 1, |
| 138 | + // representing the C code's copy. When it drops, that refcount will go down to 0 |
| 139 | + // and the inner ServerConfig will be dropped. |
| 140 | + let arc: Arc<CertifiedKey> = unsafe { Arc::from_raw(key) }; |
| 141 | + let strong_count = Arc::strong_count(&arc); |
| 142 | + if strong_count < 1 { |
| 143 | + eprintln!( |
| 144 | + "rustls_cipher_certified_key_free: invariant failed: arc.strong_count was < 1: {}. \ |
| 145 | + You must not free the same certified_key multiple times.", |
| 146 | + strong_count |
| 147 | + ); |
| 148 | + } |
| 149 | + } |
| 150 | +} |
| 151 | + |
| 152 | +pub(crate) fn certified_key_build( |
| 153 | + cert_chain: *const u8, |
| 154 | + cert_chain_len: size_t, |
| 155 | + private_key: *const u8, |
| 156 | + private_key_len: size_t, |
| 157 | +) -> Result<CertifiedKey, rustls_result> { |
| 158 | + let mut cert_chain: &[u8] = unsafe { |
| 159 | + if cert_chain.is_null() { |
| 160 | + return Err(NullParameter); |
| 161 | + } |
| 162 | + slice::from_raw_parts(cert_chain, cert_chain_len as usize) |
| 163 | + }; |
| 164 | + let private_key: &[u8] = unsafe { |
| 165 | + if private_key.is_null() { |
| 166 | + return Err(NullParameter); |
| 167 | + } |
| 168 | + slice::from_raw_parts(private_key, private_key_len as usize) |
| 169 | + }; |
| 170 | + let mut private_keys: Vec<Vec<u8>> = match pkcs8_private_keys(&mut Cursor::new(private_key)) { |
| 171 | + Ok(v) => v, |
| 172 | + Err(_) => return Err(rustls_result::PrivateKeyParseError), |
| 173 | + }; |
| 174 | + let private_key: PrivateKey = match private_keys.pop() { |
| 175 | + Some(p) => PrivateKey(p), |
| 176 | + None => { |
| 177 | + private_keys = match rsa_private_keys(&mut Cursor::new(private_key)) { |
| 178 | + Ok(v) => v, |
| 179 | + Err(_) => return Err(rustls_result::PrivateKeyParseError), |
| 180 | + }; |
| 181 | + let rsa_private_key: PrivateKey = match private_keys.pop() { |
| 182 | + Some(p) => PrivateKey(p), |
| 183 | + None => return Err(rustls_result::PrivateKeyParseError), |
| 184 | + }; |
| 185 | + rsa_private_key |
| 186 | + } |
| 187 | + }; |
| 188 | + let signing_key = match rustls::sign::any_supported_type(&private_key) { |
| 189 | + Ok(key) => key, |
| 190 | + Err(_) => return Err(rustls_result::PrivateKeyParseError), |
| 191 | + }; |
| 192 | + let parsed_chain: Vec<Certificate> = match certs(&mut cert_chain) { |
| 193 | + Ok(v) => v.into_iter().map(Certificate).collect(), |
| 194 | + Err(_) => return Err(rustls_result::CertificateParseError), |
| 195 | + }; |
| 196 | + |
| 197 | + Ok(rustls::sign::CertifiedKey::new( |
| 198 | + parsed_chain, |
| 199 | + Arc::new(signing_key), |
| 200 | + )) |
| 201 | +} |
0 commit comments