Skip to content

Commit 05ad7eb

Browse files
author
Stefan Eissing
committed
New opaque rustls_cipher_certified_key.
A rustls `CertifiedKey` can be constructed, given chain and key PEM data. Such a key can be returned by the client_hello callback or configured at the builder for use in the created session. The builder can be provided with a list of certified_keys and the session will chose the first one from the list that is compatible with the signatures the client provided in its hello.
1 parent 75aaa1e commit 05ad7eb

File tree

3 files changed

+282
-49
lines changed

3 files changed

+282
-49
lines changed

src/cipher.rs

Lines changed: 119 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,19 @@
11
use std::{cmp::min, slice};
22

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+
};
47
use libc::{c_char, size_t};
8+
use std::io::Cursor;
59
use std::os::raw::c_ushort;
10+
use std::sync::Arc;
11+
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;
617

718
/// All SignatureScheme currently defined in rustls.
819
/// At the moment not exposed by rustls itself.
@@ -81,3 +92,110 @@ pub extern "C" fn rustls_cipher_get_signature_scheme_name(
8192
}
8293
}
8394
}
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+
}

src/crustls.h

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,13 @@ typedef enum rustls_result {
8989
RUSTLS_RESULT_CERT_SCT_UNKNOWN_LOG = 7323,
9090
} rustls_result;
9191

92+
/**
93+
* The complete chain of certificates plus private key for
94+
* being certified against someones list of trust anchors (commonly
95+
* called root store). Corresponds to `CertifiedKey` in the Rust API.
96+
*/
97+
typedef struct rustls_cipher_certified_key rustls_cipher_certified_key;
98+
9299
/**
93100
* A client config that is done being constructed and is now read-only.
94101
* Under the hood, this object corresponds to an Arc<ClientConfig>.
@@ -220,6 +227,22 @@ typedef struct rustls_client_hello {
220227
struct rustls_vec_bytes alpn;
221228
} rustls_client_hello;
222229

230+
/**
231+
* Prototype of a callback that can be installed by the application at the
232+
* `rustls_server_config`. This callback will be invoked by a `rustls_server_session`
233+
* once the TLS client hello message has been received.
234+
* `userdata` will be supplied as provided when registering the callback.
235+
* `hello`gives the value of the available client announcements, as interpreted
236+
* by rustls. See the definition of `rustls_client_hello` for details.
237+
*
238+
* NOTE: the passed in `hello` and all its values are only availabe during the
239+
* callback invocations.
240+
*
241+
* EXPERIMENTAL: this feature of crustls is likely to change in the future, as
242+
* the rustls library is re-evaluating their current approach to client hello handling.
243+
*/
244+
typedef const struct rustls_cipher_certified_key *(*rustls_client_hello_callback)(rustls_client_hello_userdata userdata, const struct rustls_client_hello *hello);
245+
223246
/**
224247
* Write the version of the crustls C bindings and rustls itself into the
225248
* provided buffer, up to a max of `len` bytes. Output is UTF-8 encoded
@@ -242,6 +265,22 @@ void rustls_cipher_get_signature_scheme_name(unsigned short scheme,
242265
size_t len,
243266
size_t *out_n);
244267

268+
enum rustls_result rustls_cipher_certified_key_build(const uint8_t *cert_chain,
269+
size_t cert_chain_len,
270+
const uint8_t *private_key,
271+
size_t private_key_len,
272+
const struct rustls_cipher_certified_key **certified_key_out);
273+
274+
/**
275+
* "Free" a certified_key previously returned from
276+
* rustls_cipher_certified_key_build. Since certified_key is actually an
277+
* atomically reference-counted pointer, extant certified_key may still
278+
* hold an internal reference to the Rust object. However, C code must
279+
* consider this pointer unusable after "free"ing it.
280+
* Calling with NULL is fine. Must not be called twice with the same value.
281+
*/
282+
void rustls_cipher_certified_key_free(const struct rustls_cipher_certified_key *config);
283+
245284
/**
246285
* Create a rustls_client_config_builder. Caller owns the memory and must
247286
* eventually call rustls_client_config_builder_build, then free the
@@ -402,13 +441,36 @@ enum rustls_result rustls_server_config_builder_set_ignore_client_order(struct r
402441
* first.
403442
* private_key must point to a byte array of length private_key_len containing
404443
* a private key in PEM-encoded PKCS#8 or PKCS#1 format.
444+
*
445+
* EXPERIMENTAL: installing a client_hello callback will replace any
446+
* configured certified keys and vice versa. Same holds true for the
447+
* set_single_cert variant.
405448
*/
406449
enum rustls_result rustls_server_config_builder_set_single_cert_pem(struct rustls_server_config_builder *builder,
407450
const uint8_t *cert_chain,
408451
size_t cert_chain_len,
409452
const uint8_t *private_key,
410453
size_t private_key_len);
411454

455+
/**
456+
* Provide the configuration a list of certificates where the session
457+
* will select the first one that is compatible with the client's signing
458+
* capabilities. Servers that want to support ECDSA and RSA certificates
459+
* will want the ECSDA to go first in the list.
460+
*
461+
* The built configuration will keep a reference to all certified keys
462+
* provided. The client may `rustls_cipher_certified_key_free()` afterwards
463+
* without the configuration losing them. The same certified key may also
464+
* be appear in multiple configs.
465+
*
466+
* EXPERIMENTAL: installing a client_hello callback will replace any
467+
* configured certified keys and vice versa. Same holds true for the
468+
* set_single_cert variant.
469+
*/
470+
enum rustls_result rustls_server_config_builder_set_certified_keys(struct rustls_server_config_builder *builder,
471+
const struct rustls_cipher_certified_key *const *certified_keys,
472+
size_t certified_keys_len);
473+
412474
/**
413475
* Turn a *rustls_server_config_builder (mutable) into a *rustls_server_config
414476
* (read-only).
@@ -533,9 +595,11 @@ enum rustls_result rustls_server_session_get_sni_hostname(const struct rustls_se
533595
*
534596
* EXPERIMENTAL: this feature of crustls is likely to change in the future, as
535597
* the rustls library is re-evaluating their current approach to client hello handling.
598+
* Installing a client_hello callback will replace any configured certified keys
599+
* and vice versa. Same holds true for the set_single_cert variant.
536600
*/
537601
enum rustls_result rustls_server_config_builder_set_hello_callback(struct rustls_server_config_builder *builder,
538-
void (*callback)(rustls_client_hello_userdata userdata, const struct rustls_client_hello *hello),
602+
rustls_client_hello_callback callback,
539603
rustls_client_hello_userdata userdata);
540604

541605
#endif /* CRUSTLS_H */

0 commit comments

Comments
 (0)