aboutsummaryrefslogtreecommitdiff
path: root/src/tls.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/tls.rs')
-rw-r--r--src/tls.rs352
1 files changed, 252 insertions, 100 deletions
diff --git a/src/tls.rs b/src/tls.rs
index 0031eee..e6e7ddb 100644
--- a/src/tls.rs
+++ b/src/tls.rs
@@ -47,6 +47,7 @@ use crate::packet;
const TLS1_3_VERSION: u16 = 0x0304;
const TLS_ALERT_ERROR: u64 = 0x100;
+const INTERNAL_ERROR: u64 = 0x01;
#[allow(non_camel_case_types)]
#[repr(transparent)]
@@ -122,6 +123,47 @@ struct SSL_QUIC_METHOD {
extern fn(ssl: *mut SSL, level: crypto::Level, alert: u8) -> c_int,
}
+#[cfg(test)]
+#[repr(C)]
+#[allow(non_camel_case_types)]
+#[allow(dead_code)]
+enum ssl_private_key_result_t {
+ ssl_private_key_success,
+ ssl_private_key_retry,
+ ssl_private_key_failure,
+}
+
+#[cfg(test)]
+#[repr(C)]
+#[allow(non_camel_case_types)]
+struct SSL_PRIVATE_KEY_METHOD {
+ sign: extern fn(
+ ssl: *mut SSL,
+ out: *mut u8,
+ out_len: *mut usize,
+ max_out: usize,
+ signature_algorithm: u16,
+ r#in: *const u8,
+ in_len: usize,
+ ) -> ssl_private_key_result_t,
+
+ decrypt: extern fn(
+ ssl: *mut SSL,
+ out: *mut u8,
+ out_len: *mut usize,
+ max_out: usize,
+ r#in: *const u8,
+ in_len: usize,
+ ) -> ssl_private_key_result_t,
+
+ complete: extern fn(
+ ssl: *mut SSL,
+ out: *mut u8,
+ out_len: *mut usize,
+ max_out: usize,
+ ) -> ssl_private_key_result_t,
+}
+
lazy_static::lazy_static! {
/// BoringSSL Extra Data Index for Quiche Connections
pub static ref QUICHE_EX_DATA_INDEX: c_int = unsafe {
@@ -167,7 +209,7 @@ impl Context {
pub fn new_handshake(&mut self) -> Result<Handshake> {
unsafe {
let ssl = SSL_new(self.as_mut_ptr());
- Ok(Handshake(ssl))
+ Ok(Handshake::new(ssl))
}
}
@@ -277,11 +319,9 @@ impl Context {
}
pub fn set_verify(&mut self, verify: bool) {
- let mode = if verify {
- 0x01 // SSL_VERIFY_PEER
- } else {
- 0x00 // SSL_VERIFY_NONE
- };
+ // true -> 0x01 SSL_VERIFY_PEER
+ // false -> 0x00 SSL_VERIFY_NONE
+ let mode = i32::from(verify);
unsafe {
SSL_CTX_set_verify(self.as_mut_ptr(), mode, ptr::null());
@@ -294,12 +334,12 @@ impl Context {
}
}
- pub fn set_alpn(&mut self, v: &[Vec<u8>]) -> Result<()> {
+ pub fn set_alpn(&mut self, v: &[&[u8]]) -> Result<()> {
let mut protos: Vec<u8> = Vec::new();
for proto in v {
protos.push(proto.len() as u8);
- protos.append(&mut proto.clone());
+ protos.extend_from_slice(proto);
}
// Configure ALPN for servers.
@@ -332,7 +372,7 @@ impl Context {
}
pub fn set_early_data_enabled(&mut self, enabled: bool) {
- let enabled = if enabled { 1 } else { 0 };
+ let enabled = i32::from(enabled);
unsafe {
SSL_CTX_set_early_data_enabled(self.as_mut_ptr(), enabled);
@@ -358,13 +398,25 @@ impl Drop for Context {
}
}
-pub struct Handshake(*mut SSL);
+pub struct Handshake {
+ /// Raw pointer
+ ptr: *mut SSL,
+ /// SSL_process_quic_post_handshake should be called when whenever
+ /// SSL_provide_quic_data is called to process the provided data.
+ provided_data_outstanding: bool,
+}
impl Handshake {
#[cfg(feature = "ffi")]
pub unsafe fn from_ptr(ssl: *mut c_void) -> Handshake {
- let ssl = ssl as *mut SSL;
- Handshake(ssl)
+ Handshake::new(ssl as *mut SSL)
+ }
+
+ fn new(ptr: *mut SSL) -> Handshake {
+ Handshake {
+ ptr,
+ provided_data_outstanding: false,
+ }
}
pub fn get_error(&self, ret_code: c_int) -> c_int {
@@ -439,16 +491,14 @@ impl Handshake {
}
pub fn set_quiet_shutdown(&mut self, mode: bool) {
- unsafe {
- SSL_set_quiet_shutdown(self.as_mut_ptr(), if mode { 1 } else { 0 })
- }
+ unsafe { SSL_set_quiet_shutdown(self.as_mut_ptr(), i32::from(mode)) }
}
pub fn set_host_name(&mut self, name: &str) -> Result<()> {
let cstr = ffi::CString::new(name).map_err(|_| Error::TlsFail)?;
let rc =
unsafe { SSL_set_tlsext_host_name(self.as_mut_ptr(), cstr.as_ptr()) };
- map_result_ssl(self, rc)?;
+ self.map_result_ssl(rc)?;
let param = unsafe { SSL_get0_param(self.as_mut_ptr()) };
@@ -465,14 +515,7 @@ impl Handshake {
buf.len(),
)
};
- map_result_ssl(self, rc)
- }
-
- #[cfg(test)]
- pub fn set_options(&mut self, opts: u32) {
- unsafe {
- SSL_set_options(self.as_mut_ptr(), opts);
- }
+ self.map_result_ssl(rc)
}
pub fn quic_transport_params(&self) -> &[u8] {
@@ -547,6 +590,7 @@ impl Handshake {
pub fn provide_data(
&mut self, level: crypto::Level, buf: &[u8],
) -> Result<()> {
+ self.provided_data_outstanding = true;
let rc = unsafe {
SSL_provide_quic_data(
self.as_mut_ptr(),
@@ -555,7 +599,7 @@ impl Handshake {
buf.len(),
)
};
- map_result_ssl(self, rc)
+ self.map_result_ssl(rc)
}
pub fn do_handshake(&mut self, ex_data: &mut ExData) -> Result<()> {
@@ -563,15 +607,24 @@ impl Handshake {
let rc = unsafe { SSL_do_handshake(self.as_mut_ptr()) };
self.set_ex_data::<Connection>(*QUICHE_EX_DATA_INDEX, std::ptr::null())?;
- map_result_ssl(self, rc)
+ self.set_transport_error(ex_data, rc);
+ self.map_result_ssl(rc)
}
pub fn process_post_handshake(&mut self, ex_data: &mut ExData) -> Result<()> {
+ // If SSL_provide_quic_data hasn't been called since we last called
+ // SSL_process_quic_post_handshake, then there's nothing to do.
+ if !self.provided_data_outstanding {
+ return Ok(());
+ }
+ self.provided_data_outstanding = false;
+
self.set_ex_data(*QUICHE_EX_DATA_INDEX, ex_data)?;
let rc = unsafe { SSL_process_quic_post_handshake(self.as_mut_ptr()) };
self.set_ex_data::<Connection>(*QUICHE_EX_DATA_INDEX, std::ptr::null())?;
- map_result_ssl(self, rc)
+ self.set_transport_error(ex_data, rc);
+ self.map_result_ssl(rc)
}
pub fn reset_early_data_reject(&mut self) {
@@ -625,6 +678,39 @@ impl Handshake {
Some(sigalg.to_string())
}
+ pub fn peer_cert_chain(&self) -> Option<Vec<&[u8]>> {
+ let cert_chain = unsafe {
+ let chain =
+ map_result_ptr(SSL_get0_peer_certificates(self.as_ptr())).ok()?;
+
+ let num = sk_num(chain);
+ if num <= 0 {
+ return None;
+ }
+
+ let mut cert_chain = vec![];
+ for i in 0..num {
+ let buffer =
+ map_result_ptr(sk_value(chain, i) as *const CRYPTO_BUFFER)
+ .ok()?;
+
+ let out_len = CRYPTO_BUFFER_len(buffer);
+ if out_len == 0 {
+ return None;
+ }
+
+ let out = CRYPTO_BUFFER_data(buffer);
+ let slice = slice::from_raw_parts(out, out_len);
+
+ cert_chain.push(slice);
+ }
+
+ cert_chain
+ };
+
+ Some(cert_chain)
+ }
+
pub fn peer_cert(&self) -> Option<&[u8]> {
let peer_cert = unsafe {
let chain =
@@ -643,12 +729,57 @@ impl Handshake {
}
let out = CRYPTO_BUFFER_data(buffer);
- slice::from_raw_parts(out, out_len as usize)
+ slice::from_raw_parts(out, out_len)
};
Some(peer_cert)
}
+ #[cfg(test)]
+ pub fn set_options(&mut self, opts: u32) {
+ unsafe {
+ SSL_set_options(self.as_mut_ptr(), opts);
+ }
+ }
+
+ // Only used for testing handling of failure during key signing.
+ #[cfg(test)]
+ pub fn set_failing_private_key_method(&mut self) {
+ extern fn failing_sign(
+ _ssl: *mut SSL, _out: *mut u8, _out_len: *mut usize, _max_out: usize,
+ _signature_algorithm: u16, _in: *const u8, _in_len: usize,
+ ) -> ssl_private_key_result_t {
+ ssl_private_key_result_t::ssl_private_key_failure
+ }
+
+ extern fn failing_decrypt(
+ _ssl: *mut SSL, _out: *mut u8, _out_len: *mut usize, _max_out: usize,
+ _in: *const u8, _in_len: usize,
+ ) -> ssl_private_key_result_t {
+ ssl_private_key_result_t::ssl_private_key_failure
+ }
+
+ extern fn failing_complete(
+ _ssl: *mut SSL, _out: *mut u8, _out_len: *mut usize, _max_out: usize,
+ ) -> ssl_private_key_result_t {
+ ssl_private_key_result_t::ssl_private_key_failure
+ }
+
+ static QUICHE_PRIVATE_KEY_METHOD: SSL_PRIVATE_KEY_METHOD =
+ SSL_PRIVATE_KEY_METHOD {
+ decrypt: failing_decrypt,
+ sign: failing_sign,
+ complete: failing_complete,
+ };
+
+ unsafe {
+ SSL_set_private_key_method(
+ self.as_mut_ptr(),
+ &QUICHE_PRIVATE_KEY_METHOD,
+ );
+ }
+ }
+
pub fn is_completed(&self) -> bool {
unsafe { SSL_in_init(self.as_ptr()) == 0 }
}
@@ -663,15 +794,84 @@ impl Handshake {
pub fn clear(&mut self) -> Result<()> {
let rc = unsafe { SSL_clear(self.as_mut_ptr()) };
- map_result_ssl(self, rc)
+ self.map_result_ssl(rc)
}
fn as_ptr(&self) -> *const SSL {
- self.0
+ self.ptr
}
fn as_mut_ptr(&mut self) -> *mut SSL {
- self.0
+ self.ptr
+ }
+
+ fn map_result_ssl(&mut self, bssl_result: c_int) -> Result<()> {
+ match bssl_result {
+ 1 => Ok(()),
+
+ _ => {
+ let ssl_err = self.get_error(bssl_result);
+ match ssl_err {
+ // SSL_ERROR_SSL
+ 1 => {
+ log_ssl_error();
+
+ Err(Error::TlsFail)
+ },
+
+ // SSL_ERROR_WANT_READ
+ 2 => Err(Error::Done),
+
+ // SSL_ERROR_WANT_WRITE
+ 3 => Err(Error::Done),
+
+ // SSL_ERROR_WANT_X509_LOOKUP
+ 4 => Err(Error::Done),
+
+ // SSL_ERROR_SYSCALL
+ 5 => Err(Error::TlsFail),
+
+ // SSL_ERROR_PENDING_SESSION
+ 11 => Err(Error::Done),
+
+ // SSL_ERROR_PENDING_CERTIFICATE
+ 12 => Err(Error::Done),
+
+ // SSL_ERROR_WANT_PRIVATE_KEY_OPERATION
+ 13 => Err(Error::Done),
+
+ // SSL_ERROR_PENDING_TICKET
+ 14 => Err(Error::Done),
+
+ // SSL_ERROR_EARLY_DATA_REJECTED
+ 15 => {
+ self.reset_early_data_reject();
+ Err(Error::Done)
+ },
+
+ // SSL_ERROR_WANT_CERTIFICATE_VERIFY
+ 16 => Err(Error::Done),
+
+ _ => Err(Error::TlsFail),
+ }
+ },
+ }
+ }
+
+ fn set_transport_error(&mut self, ex_data: &mut ExData, bssl_result: c_int) {
+ // SSL_ERROR_SSL
+ if self.get_error(bssl_result) == 1 {
+ // SSL_ERROR_SSL can't be recovered so ensure we set a
+ // local_error so the connection is closed.
+ // See https://www.openssl.org/docs/man1.1.1/man3/SSL_get_error.html
+ if ex_data.local_error.is_none() {
+ *ex_data.local_error = Some(ConnectionError {
+ is_app: false,
+ error_code: INTERNAL_ERROR,
+ reason: Vec::new(),
+ })
+ }
+ }
}
}
@@ -692,7 +892,7 @@ impl Drop for Handshake {
pub struct ExData<'a> {
pub application_protos: &'a Vec<Vec<u8>>,
- pub pkt_num_spaces: &'a mut [packet::PktNumSpace; packet::EPOCH_COUNT],
+ pub pkt_num_spaces: &'a mut [packet::PktNumSpace; packet::Epoch::count()],
pub session: &'a mut Option<Vec<u8>>,
@@ -740,13 +940,13 @@ extern fn set_read_secret(
let space = match level {
crypto::Level::Initial =>
- &mut ex_data.pkt_num_spaces[packet::EPOCH_INITIAL],
+ &mut ex_data.pkt_num_spaces[packet::Epoch::Initial],
crypto::Level::ZeroRTT =>
- &mut ex_data.pkt_num_spaces[packet::EPOCH_APPLICATION],
+ &mut ex_data.pkt_num_spaces[packet::Epoch::Application],
crypto::Level::Handshake =>
- &mut ex_data.pkt_num_spaces[packet::EPOCH_HANDSHAKE],
+ &mut ex_data.pkt_num_spaces[packet::Epoch::Handshake],
crypto::Level::OneRTT =>
- &mut ex_data.pkt_num_spaces[packet::EPOCH_APPLICATION],
+ &mut ex_data.pkt_num_spaces[packet::Epoch::Application],
};
let aead = match get_cipher_from_ptr(cipher) {
@@ -791,13 +991,13 @@ extern fn set_write_secret(
let space = match level {
crypto::Level::Initial =>
- &mut ex_data.pkt_num_spaces[packet::EPOCH_INITIAL],
+ &mut ex_data.pkt_num_spaces[packet::Epoch::Initial],
crypto::Level::ZeroRTT =>
- &mut ex_data.pkt_num_spaces[packet::EPOCH_APPLICATION],
+ &mut ex_data.pkt_num_spaces[packet::Epoch::Application],
crypto::Level::Handshake =>
- &mut ex_data.pkt_num_spaces[packet::EPOCH_HANDSHAKE],
+ &mut ex_data.pkt_num_spaces[packet::Epoch::Handshake],
crypto::Level::OneRTT =>
- &mut ex_data.pkt_num_spaces[packet::EPOCH_APPLICATION],
+ &mut ex_data.pkt_num_spaces[packet::Epoch::Application],
};
let aead = match get_cipher_from_ptr(cipher) {
@@ -843,12 +1043,12 @@ extern fn add_handshake_data(
let space = match level {
crypto::Level::Initial =>
- &mut ex_data.pkt_num_spaces[packet::EPOCH_INITIAL],
+ &mut ex_data.pkt_num_spaces[packet::Epoch::Initial],
crypto::Level::ZeroRTT => unreachable!(),
crypto::Level::Handshake =>
- &mut ex_data.pkt_num_spaces[packet::EPOCH_HANDSHAKE],
+ &mut ex_data.pkt_num_spaces[packet::Epoch::Handshake],
crypto::Level::OneRTT =>
- &mut ex_data.pkt_num_spaces[packet::EPOCH_APPLICATION],
+ &mut ex_data.pkt_num_spaces[packet::Epoch::Application],
};
if space.crypto_stream.send.write(buf, false).is_err() {
@@ -966,7 +1166,7 @@ extern fn new_session(ssl: *mut SSL, session: *mut SSL_SESSION) -> c_int {
None => return 0,
};
- let handshake = Handshake(ssl);
+ let handshake = Handshake::new(ssl);
let peer_params = handshake.quic_transport_params();
// Serialize session object into buffer.
@@ -1040,59 +1240,6 @@ fn map_result_ptr<'a, T>(bssl_result: *const T) -> Result<&'a T> {
}
}
-fn map_result_ssl(ssl: &mut Handshake, bssl_result: c_int) -> Result<()> {
- match bssl_result {
- 1 => Ok(()),
-
- _ => {
- let ssl_err = ssl.get_error(bssl_result);
- match ssl_err {
- // SSL_ERROR_SSL
- 1 => {
- log_ssl_error();
-
- Err(Error::TlsFail)
- },
-
- // SSL_ERROR_WANT_READ
- 2 => Err(Error::Done),
-
- // SSL_ERROR_WANT_WRITE
- 3 => Err(Error::Done),
-
- // SSL_ERROR_WANT_X509_LOOKUP
- 4 => Err(Error::Done),
-
- // SSL_ERROR_SYSCALL
- 5 => Err(Error::TlsFail),
-
- // SSL_ERROR_PENDING_SESSION
- 11 => Err(Error::Done),
-
- // SSL_ERROR_PENDING_CERTIFICATE
- 12 => Err(Error::Done),
-
- // SSL_ERROR_WANT_PRIVATE_KEY_OPERATION
- 13 => Err(Error::Done),
-
- // SSL_ERROR_PENDING_TICKET
- 14 => Err(Error::Done),
-
- // SSL_ERROR_EARLY_DATA_REJECTED
- 15 => {
- ssl.reset_early_data_reject();
- Err(Error::Done)
- },
-
- // SSL_ERROR_WANT_CERTIFICATE_VERIFY
- 16 => Err(Error::Done),
-
- _ => Err(Error::TlsFail),
- }
- },
- }
-}
-
fn log_ssl_error() {
let err = [0; 1024];
@@ -1211,9 +1358,6 @@ extern {
ssl: *mut SSL, params: *const u8, params_len: usize,
) -> c_int;
- #[cfg(test)]
- fn SSL_set_options(ssl: *mut SSL, opts: u32) -> u32;
-
fn SSL_set_quic_method(
ssl: *mut SSL, quic_method: *const SSL_QUIC_METHOD,
) -> c_int;
@@ -1224,6 +1368,14 @@ extern {
ssl: *mut SSL, context: *const u8, context_len: usize,
) -> c_int;
+ #[cfg(test)]
+ fn SSL_set_options(ssl: *mut SSL, opts: u32) -> u32;
+
+ #[cfg(test)]
+ fn SSL_set_private_key_method(
+ ssl: *mut SSL, key_method: *const SSL_PRIVATE_KEY_METHOD,
+ );
+
fn SSL_get_peer_quic_transport_params(
ssl: *const SSL, out_params: *mut *const u8, out_params_len: *mut usize,
);