use std::io::{self, Error, ErrorKind};
pub const CONTENT_TYPE_CHANGE_CIPHER_SPEC: u8 = 0x14;
pub const CONTENT_TYPE_ALERT: u8 = 0x15;
pub const CONTENT_TYPE_HANDSHAKE: u8 = 0x16;
pub const CONTENT_TYPE_APPLICATION_DATA: u8 = 0x17;
pub const ALERT_LEVEL_WARNING: u8 = 0x01;
pub const ALERT_DESC_CLOSE_NOTIFY: u8 = 0x00;
pub const VERSION_TLS_1_2_MAJOR: u8 = 0x03;
pub const VERSION_TLS_1_2_MINOR: u8 = 0x03;
pub const HANDSHAKE_TYPE_SERVER_HELLO: u8 = 2;
pub const HANDSHAKE_TYPE_ENCRYPTED_EXTENSIONS: u8 = 8;
pub const HANDSHAKE_TYPE_CERTIFICATE: u8 = 11;
pub const HANDSHAKE_TYPE_CERTIFICATE_VERIFY: u8 = 15;
pub const HANDSHAKE_TYPE_FINISHED: u8 = 20;
pub const MAX_TLS_CIPHERTEXT_LEN: usize = 16384 + 256;
pub const MAX_TLS_PLAINTEXT_LEN: usize = 16384;
pub const TLS_RECORD_HEADER_SIZE: usize = 5;
pub const TLS_MAX_RECORD_SIZE: usize = MAX_TLS_CIPHERTEXT_LEN + TLS_RECORD_HEADER_SIZE;
pub const CIPHERTEXT_READ_BUF_CAPACITY: usize = TLS_MAX_RECORD_SIZE * 2;
pub const PLAINTEXT_READ_BUF_CAPACITY: usize = TLS_MAX_RECORD_SIZE * 2;
pub const OUTGOING_BUFFER_LIMIT: usize = 64 * 1024;
#[cfg(test)]
#[inline]
pub fn strip_content_type_slice(plaintext: &[u8]) -> io::Result<(u8, usize)> {
if plaintext.is_empty() {
return Err(Error::new(ErrorKind::InvalidData, "Empty plaintext"));
}
let content_type = plaintext[plaintext.len() - 1];
if content_type != CONTENT_TYPE_HANDSHAKE
&& content_type != CONTENT_TYPE_APPLICATION_DATA
&& content_type != CONTENT_TYPE_ALERT
{
return Err(Error::new(
ErrorKind::InvalidData,
format!("Invalid content type: 0x{:02x}", content_type),
));
}
Ok((content_type, plaintext.len() - 1))
}
#[cfg(test)]
pub fn strip_content_type(plaintext: &mut Vec<u8>) -> io::Result<u8> {
let (content_type, valid_len) = strip_content_type_slice(plaintext)?;
plaintext.truncate(valid_len);
Ok(content_type)
}
pub fn strip_content_type_with_padding(plaintext: &mut Vec<u8>) -> io::Result<u8> {
if plaintext.is_empty() {
return Err(Error::new(ErrorKind::InvalidData, "Empty plaintext"));
}
while !plaintext.is_empty() && *plaintext.last().unwrap() == 0 {
plaintext.pop();
}
if plaintext.is_empty() {
return Err(Error::new(ErrorKind::InvalidData, "Plaintext is all zeros"));
}
let content_type = plaintext.pop().unwrap();
if content_type != CONTENT_TYPE_HANDSHAKE
&& content_type != CONTENT_TYPE_APPLICATION_DATA
&& content_type != CONTENT_TYPE_ALERT
{
return Err(Error::new(
ErrorKind::InvalidData,
format!("Invalid content type: 0x{:02x}", content_type),
));
}
Ok(content_type)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_strip_content_type_app_data() {
let mut plaintext = vec![0x01, 0x02, 0x03, CONTENT_TYPE_APPLICATION_DATA];
let ct = strip_content_type(&mut plaintext).unwrap();
assert_eq!(ct, CONTENT_TYPE_APPLICATION_DATA);
assert_eq!(plaintext, vec![0x01, 0x02, 0x03]);
}
#[test]
fn test_strip_content_type_handshake() {
let mut plaintext = vec![0xAA, 0xBB, CONTENT_TYPE_HANDSHAKE];
let ct = strip_content_type(&mut plaintext).unwrap();
assert_eq!(ct, CONTENT_TYPE_HANDSHAKE);
assert_eq!(plaintext, vec![0xAA, 0xBB]);
}
#[test]
fn test_strip_content_type_alert() {
let mut plaintext = vec![0x01, 0x00, CONTENT_TYPE_ALERT];
let ct = strip_content_type(&mut plaintext).unwrap();
assert_eq!(ct, CONTENT_TYPE_ALERT);
assert_eq!(plaintext, vec![0x01, 0x00]);
}
#[test]
fn test_strip_content_type_preserves_zeros() {
let mut plaintext = vec![0x01, 0x00, 0x00, CONTENT_TYPE_APPLICATION_DATA];
let ct = strip_content_type(&mut plaintext).unwrap();
assert_eq!(ct, CONTENT_TYPE_APPLICATION_DATA);
assert_eq!(plaintext, vec![0x01, 0x00, 0x00]);
}
#[test]
fn test_strip_content_type_empty() {
let mut plaintext = Vec::new();
assert!(strip_content_type(&mut plaintext).is_err());
}
#[test]
fn test_strip_content_type_invalid() {
let mut plaintext = vec![0x01, 0xFF]; assert!(strip_content_type(&mut plaintext).is_err());
}
#[test]
fn test_strip_with_padding_no_padding() {
let mut plaintext = vec![0x01, 0x02, CONTENT_TYPE_APPLICATION_DATA];
let ct = strip_content_type_with_padding(&mut plaintext).unwrap();
assert_eq!(ct, CONTENT_TYPE_APPLICATION_DATA);
assert_eq!(plaintext, vec![0x01, 0x02]);
}
#[test]
fn test_strip_with_padding_strips_zeros() {
let mut plaintext = vec![0x01, 0x02, CONTENT_TYPE_HANDSHAKE, 0x00, 0x00, 0x00];
let ct = strip_content_type_with_padding(&mut plaintext).unwrap();
assert_eq!(ct, CONTENT_TYPE_HANDSHAKE);
assert_eq!(plaintext, vec![0x01, 0x02]);
}
#[test]
fn test_strip_with_padding_empty() {
let mut plaintext = Vec::new();
assert!(strip_content_type_with_padding(&mut plaintext).is_err());
}
#[test]
fn test_strip_with_padding_all_zeros() {
let mut plaintext = vec![0x00, 0x00, 0x00];
assert!(strip_content_type_with_padding(&mut plaintext).is_err());
}
#[test]
fn test_strip_with_padding_invalid_type() {
let mut plaintext = vec![0x01, 0xFF, 0x00]; assert!(strip_content_type_with_padding(&mut plaintext).is_err());
}
}