rustls_ffi/certificate.rs
1use std::ffi::CStr;
2use std::marker::PhantomData;
3use std::ptr::null;
4use std::slice;
5
6use libc::{c_char, size_t};
7use rustls::RootCertStore;
8use rustls::pki_types::pem::PemObject;
9use rustls::pki_types::{CertificateDer, PrivateKeyDer};
10use rustls::sign::CertifiedKey;
11
12use crate::crypto_provider::{self, rustls_signing_key};
13use crate::error::{map_error, rustls_result};
14use crate::ffi::{
15 arc_castable, box_castable, free_arc, free_box, ref_castable, set_arc_mut_ptr,
16 to_arc_const_ptr, to_boxed_mut_ptr, try_box_from_ptr, try_mut_from_ptr, try_ref_from_ptr,
17 try_ref_from_ptr_ptr, try_slice, try_take,
18};
19use crate::panic::ffi_panic_boundary;
20use crate::rslice::rustls_slice_bytes;
21
22ref_castable! {
23 /// An X.509 certificate, as used in rustls.
24 /// Corresponds to `CertificateDer` in the Rust pki-types API.
25 /// <https://docs.rs/rustls-pki-types/latest/rustls_pki_types/struct.CertificateDer.html>
26 pub struct rustls_certificate(CertificateDer<'a>);
27}
28
29/// Get the DER data of the certificate itself.
30/// The data is owned by the certificate and has the same lifetime.
31#[no_mangle]
32pub extern "C" fn rustls_certificate_get_der(
33 cert: *const rustls_certificate,
34 out_der_data: *mut *const u8,
35 out_der_len: *mut size_t,
36) -> rustls_result {
37 ffi_panic_boundary! {
38 let cert = try_ref_from_ptr!(cert);
39 if out_der_data.is_null() || out_der_len.is_null() {
40 return rustls_result::NullParameter;
41 }
42 let der = cert.as_ref();
43 unsafe {
44 *out_der_data = der.as_ptr();
45 *out_der_len = der.len();
46 }
47 rustls_result::Ok
48 }
49}
50
51arc_castable! {
52 /// The complete chain of certificates to send during a TLS handshake,
53 /// plus a private key that matches the end-entity (leaf) certificate.
54 ///
55 /// Corresponds to `CertifiedKey` in the Rust API.
56 /// <https://docs.rs/rustls/latest/rustls/sign/struct.CertifiedKey.html>
57 pub struct rustls_certified_key(CertifiedKey);
58}
59
60impl rustls_certified_key {
61 /// Build a `rustls_certified_key` from a certificate chain and a private key
62 /// and the default process-wide crypto provider.
63 ///
64 /// `cert_chain` must point to a buffer of `cert_chain_len` bytes, containing
65 /// a series of PEM-encoded certificates, with the end-entity (leaf)
66 /// certificate first.
67 ///
68 /// `private_key` must point to a buffer of `private_key_len` bytes, containing
69 /// a PEM-encoded private key in either PKCS#1, PKCS#8 or SEC#1 format when
70 /// using `aws-lc-rs` as the crypto provider. Supported formats may vary by
71 /// provider.
72 ///
73 /// On success, this writes a pointer to the newly created
74 /// `rustls_certified_key` in `certified_key_out`. That pointer must later
75 /// be freed with `rustls_certified_key_free` to avoid memory leaks. Note that
76 /// internally, this is an atomically reference-counted pointer, so even after
77 /// the original caller has called `rustls_certified_key_free`, other objects
78 /// may retain a pointer to the object. The memory will be freed when all
79 /// references are gone.
80 ///
81 /// This function does not take ownership of any of its input pointers. It
82 /// parses the pointed-to data and makes a copy of the result. You may
83 /// free the cert_chain and private_key pointers after calling it.
84 ///
85 /// Typically, you will build a `rustls_certified_key`, use it to create a
86 /// `rustls_server_config` (which increments the reference count), and then
87 /// immediately call `rustls_certified_key_free`. That leaves the
88 /// `rustls_server_config` in possession of the sole reference, so the
89 /// `rustls_certified_key`'s memory will automatically be released when
90 /// the `rustls_server_config` is freed.
91 #[no_mangle]
92 pub extern "C" fn rustls_certified_key_build(
93 cert_chain: *const u8,
94 cert_chain_len: size_t,
95 private_key: *const u8,
96 private_key_len: size_t,
97 certified_key_out: *mut *const rustls_certified_key,
98 ) -> rustls_result {
99 ffi_panic_boundary! {
100 let default_provider =
101 match crypto_provider::get_default_or_install_from_crate_features() {
102 Some(default_provider) => default_provider,
103 None => return rustls_result::NoDefaultCryptoProvider,
104 };
105
106 let private_key_der =
107 match PrivateKeyDer::from_pem_slice(try_slice!(private_key, private_key_len)) {
108 Ok(der) => der,
109 Err(_) => return rustls_result::PrivateKeyParseError,
110 };
111
112 let private_key = match default_provider
113 .key_provider
114 .load_private_key(private_key_der)
115 {
116 Ok(key) => key,
117 Err(e) => return map_error(e),
118 };
119
120 Self::rustls_certified_key_build_with_signing_key(
121 cert_chain,
122 cert_chain_len,
123 to_boxed_mut_ptr(private_key),
124 certified_key_out,
125 )
126 }
127 }
128
129 /// Build a `rustls_certified_key` from a certificate chain and a
130 /// `rustls_signing_key`.
131 ///
132 /// `cert_chain` must point to a buffer of `cert_chain_len` bytes, containing
133 /// a series of PEM-encoded certificates, with the end-entity (leaf)
134 /// certificate first.
135 ///
136 /// `signing_key` must point to a `rustls_signing_key` loaded using a
137 /// `rustls_crypto_provider` and `rustls_crypto_provider_load_key()`.
138 ///
139 /// On success, this writes a pointer to the newly created
140 /// `rustls_certified_key` in `certified_key_out`. That pointer must later
141 /// be freed with `rustls_certified_key_free` to avoid memory leaks. Note that
142 /// internally, this is an atomically reference-counted pointer, so even after
143 /// the original caller has called `rustls_certified_key_free`, other objects
144 /// may retain a pointer to the object. The memory will be freed when all
145 /// references are gone.
146 ///
147 /// This function does not take ownership of any of its input pointers. It
148 /// parses the pointed-to data and makes a copy of the result. You may
149 /// free the cert_chain and private_key pointers after calling it.
150 ///
151 /// Typically, you will build a `rustls_certified_key`, use it to create a
152 /// `rustls_server_config` (which increments the reference count), and then
153 /// immediately call `rustls_certified_key_free`. That leaves the
154 /// `rustls_server_config` in possession of the sole reference, so the
155 /// `rustls_certified_key`'s memory will automatically be released when
156 /// the `rustls_server_config` is freed.
157 #[no_mangle]
158 pub extern "C" fn rustls_certified_key_build_with_signing_key(
159 cert_chain: *const u8,
160 cert_chain_len: size_t,
161 signing_key: *mut rustls_signing_key,
162 certified_key_out: *mut *const rustls_certified_key,
163 ) -> rustls_result {
164 ffi_panic_boundary! {
165 let cert_chain = try_slice!(cert_chain, cert_chain_len);
166 let signing_key = try_box_from_ptr!(signing_key);
167 let certified_key_out = try_ref_from_ptr_ptr!(certified_key_out);
168
169 let parsed_chain =
170 match CertificateDer::pem_slice_iter(cert_chain).collect::<Result<Vec<_>, _>>() {
171 Ok(parsed_chain) => parsed_chain,
172 Err(_) => return rustls_result::CertificateParseError,
173 };
174
175 set_arc_mut_ptr(
176 certified_key_out,
177 CertifiedKey::new(parsed_chain, *signing_key),
178 );
179 rustls_result::Ok
180 }
181 }
182
183 /// Return the i-th rustls_certificate in the rustls_certified_key.
184 ///
185 /// 0 gives the end-entity certificate. 1 and higher give certificates from the chain.
186 ///
187 /// Indexes higher than the last available certificate return NULL.
188 ///
189 /// The returned certificate is valid until the rustls_certified_key is freed.
190 #[no_mangle]
191 pub extern "C" fn rustls_certified_key_get_certificate<'a>(
192 certified_key: *const rustls_certified_key,
193 i: size_t,
194 ) -> *const rustls_certificate<'a> {
195 ffi_panic_boundary! {
196 let certified_key = try_ref_from_ptr!(certified_key);
197 match certified_key.cert.get(i) {
198 Some(cert) => cert as *const CertificateDer as *const _,
199 None => null(),
200 }
201 }
202 }
203
204 /// Create a copy of the rustls_certified_key with the given OCSP response data
205 /// as DER encoded bytes.
206 ///
207 /// The OCSP response may be given as NULL to clear any possibly present OCSP
208 /// data from the cloned key.
209 ///
210 /// The cloned key is independent from its original and needs to be freed
211 /// by the application.
212 #[no_mangle]
213 pub extern "C" fn rustls_certified_key_clone_with_ocsp(
214 certified_key: *const rustls_certified_key,
215 ocsp_response: *const rustls_slice_bytes,
216 cloned_key_out: *mut *const rustls_certified_key,
217 ) -> rustls_result {
218 ffi_panic_boundary! {
219 let cloned_key_out = unsafe {
220 match cloned_key_out.as_mut() {
221 Some(c) => c,
222 None => return rustls_result::NullParameter,
223 }
224 };
225 let certified_key = try_ref_from_ptr!(certified_key);
226 let mut new_key = certified_key.clone();
227 if !ocsp_response.is_null() {
228 let ocsp_slice = unsafe { &*ocsp_response };
229 new_key.ocsp = Some(Vec::from(try_slice!(ocsp_slice.data, ocsp_slice.len)));
230 } else {
231 new_key.ocsp = None;
232 }
233 *cloned_key_out = to_arc_const_ptr(new_key);
234 rustls_result::Ok
235 }
236 }
237
238 /// Verify the consistency of this `rustls_certified_key`'s public and private keys.
239 ///
240 /// This is done by performing a comparison of subject public key information (SPKI) bytes
241 /// between the certificate and private key.
242 ///
243 /// If the private key matches the certificate this function returns `RUSTLS_RESULT_OK`,
244 /// otherwise an error `rustls_result` is returned.
245 #[no_mangle]
246 pub extern "C" fn rustls_certified_key_keys_match(
247 key: *const rustls_certified_key,
248 ) -> rustls_result {
249 ffi_panic_boundary! {
250 match try_ref_from_ptr!(key).keys_match() {
251 Ok(_) => rustls_result::Ok,
252 Err(e) => map_error(e),
253 }
254 }
255 }
256
257 /// "Free" a certified_key previously returned from `rustls_certified_key_build`.
258 ///
259 /// Since certified_key is actually an atomically reference-counted pointer,
260 /// extant certified_key may still hold an internal reference to the Rust object.
261 ///
262 /// However, C code must consider this pointer unusable after "free"ing it.
263 ///
264 /// Calling with NULL is fine. Must not be called twice with the same value.
265 #[no_mangle]
266 pub extern "C" fn rustls_certified_key_free(key: *const rustls_certified_key) {
267 ffi_panic_boundary! {
268 free_arc(key);
269 }
270 }
271}
272
273box_castable! {
274 /// A `rustls_root_cert_store` being constructed.
275 ///
276 /// A builder can be modified by adding trust anchor root certificates with
277 /// `rustls_root_cert_store_builder_add_pem`. Once you're done adding root certificates,
278 /// call `rustls_root_cert_store_builder_build` to turn it into a `rustls_root_cert_store`.
279 /// This object is not safe for concurrent mutation.
280 pub struct rustls_root_cert_store_builder(Option<RootCertStoreBuilder>);
281}
282
283pub(crate) struct RootCertStoreBuilder {
284 roots: RootCertStore,
285}
286
287impl rustls_root_cert_store_builder {
288 /// Create a `rustls_root_cert_store_builder`.
289 ///
290 /// Caller owns the memory and may free it with `rustls_root_cert_store_free`, regardless of
291 /// whether `rustls_root_cert_store_builder_build` was called.
292 ///
293 /// If you wish to abandon the builder without calling `rustls_root_cert_store_builder_build`,
294 /// it must be freed with `rustls_root_cert_store_builder_free`.
295 #[no_mangle]
296 pub extern "C" fn rustls_root_cert_store_builder_new() -> *mut rustls_root_cert_store_builder {
297 ffi_panic_boundary! {
298 let store = RootCertStore::empty();
299 to_boxed_mut_ptr(Some(RootCertStoreBuilder { roots: store }))
300 }
301 }
302
303 /// Add one or more certificates to the root cert store builder using PEM
304 /// encoded data.
305 ///
306 /// When `strict` is true an error will return a `CertificateParseError`
307 /// result. So will an attempt to parse data that has zero certificates.
308 ///
309 /// When `strict` is false, unparseable root certificates will be ignored.
310 /// This may be useful on systems that have syntactically invalid root
311 /// certificates.
312 #[no_mangle]
313 pub extern "C" fn rustls_root_cert_store_builder_add_pem(
314 builder: *mut rustls_root_cert_store_builder,
315 pem: *const u8,
316 pem_len: size_t,
317 strict: bool,
318 ) -> rustls_result {
319 ffi_panic_boundary! {
320 let certs_pem = try_slice!(pem, pem_len);
321 let builder = try_mut_from_ptr!(builder);
322 let builder = match builder {
323 None => return rustls_result::AlreadyUsed,
324 Some(b) => b,
325 };
326
327 let certs =
328 match CertificateDer::pem_slice_iter(certs_pem).collect::<Result<Vec<_>, _>>() {
329 Ok(certs) => certs,
330 Err(_) => return rustls_result::CertificateParseError,
331 };
332
333 // We first copy into a temporary root store so we can uphold our
334 // API guideline that there are no partial failures or partial
335 // successes.
336 let mut new_store = RootCertStore::empty();
337 let (parsed, rejected) = new_store.add_parsable_certificates(certs);
338 if strict && (rejected > 0 || parsed == 0) {
339 return rustls_result::CertificateParseError;
340 }
341
342 builder.roots.roots.append(&mut new_store.roots);
343
344 rustls_result::Ok
345 }
346 }
347
348 /// Add one or more certificates to the root cert store builder using PEM
349 /// encoded data read from the named file.
350 ///
351 /// When `strict` is true an error will return a `CertificateParseError`
352 /// result. So will an attempt to parse data that has zero certificates.
353 ///
354 /// When `strict` is false, unparseable root certificates will be ignored.
355 /// This may be useful on systems that have syntactically invalid root
356 /// certificates.
357 #[no_mangle]
358 pub extern "C" fn rustls_root_cert_store_builder_load_roots_from_file(
359 builder: *mut rustls_root_cert_store_builder,
360 filename: *const c_char,
361 strict: bool,
362 ) -> rustls_result {
363 ffi_panic_boundary! {
364 let builder = try_mut_from_ptr!(builder);
365 let builder = match builder {
366 None => return rustls_result::AlreadyUsed,
367 Some(b) => b,
368 };
369
370 let filename = unsafe {
371 if filename.is_null() {
372 return rustls_result::NullParameter;
373 }
374 CStr::from_ptr(filename)
375 };
376
377 let filename = filename.to_bytes();
378 let filename = match std::str::from_utf8(filename) {
379 Ok(s) => s,
380 Err(_) => return rustls_result::Io,
381 };
382
383 let certs = match CertificateDer::pem_file_iter(filename) {
384 Ok(certs) => certs,
385 Err(_) => return rustls_result::Io,
386 };
387
388 let certs = match certs.collect::<Result<Vec<_>, _>>() {
389 Ok(certs) => certs,
390 Err(_) => return rustls_result::CertificateParseError,
391 };
392
393 // We first copy into a temporary root store so we can uphold our
394 // API guideline that there are no partial failures or partial
395 // successes.
396 let mut roots = RootCertStore::empty();
397 let (parsed, rejected) = roots.add_parsable_certificates(certs);
398 if strict && (rejected > 0 || parsed == 0) {
399 return rustls_result::CertificateParseError;
400 }
401
402 builder.roots.roots.append(&mut roots.roots);
403 rustls_result::Ok
404 }
405 }
406
407 /// Create a new `rustls_root_cert_store` from the builder.
408 ///
409 /// The builder is consumed and cannot be used again, but must still be freed.
410 ///
411 /// The root cert store can be used in several `rustls_web_pki_client_cert_verifier_builder_new`
412 /// instances and must be freed by the application when no longer needed. See the documentation of
413 /// `rustls_root_cert_store_free` for details about lifetime.
414 #[no_mangle]
415 pub extern "C" fn rustls_root_cert_store_builder_build(
416 builder: *mut rustls_root_cert_store_builder,
417 root_cert_store_out: *mut *const rustls_root_cert_store,
418 ) -> rustls_result {
419 ffi_panic_boundary! {
420 let builder = try_mut_from_ptr!(builder);
421 let builder = try_take!(builder);
422 let root_cert_store_out = try_ref_from_ptr_ptr!(root_cert_store_out);
423 set_arc_mut_ptr(root_cert_store_out, builder.roots);
424
425 rustls_result::Ok
426 }
427 }
428
429 /// Free a `rustls_root_cert_store_builder` previously returned from
430 /// `rustls_root_cert_store_builder_new`.
431 ///
432 /// Calling with NULL is fine. Must not be called twice with the same value.
433 #[no_mangle]
434 pub extern "C" fn rustls_root_cert_store_builder_free(
435 builder: *mut rustls_root_cert_store_builder,
436 ) {
437 ffi_panic_boundary! {
438 free_box(builder);
439 }
440 }
441}
442
443arc_castable! {
444 /// A root certificate store.
445 /// <https://docs.rs/rustls/latest/rustls/struct.RootCertStore.html>
446 pub struct rustls_root_cert_store(RootCertStore);
447}
448
449impl rustls_root_cert_store {
450 /// Free a rustls_root_cert_store previously returned from rustls_root_cert_store_builder_build.
451 ///
452 /// Calling with NULL is fine. Must not be called twice with the same value.
453 #[no_mangle]
454 pub extern "C" fn rustls_root_cert_store_free(store: *const rustls_root_cert_store) {
455 ffi_panic_boundary! {
456 free_arc(store);
457 }
458 }
459}