mirror of https://github.com/ctz/rustls
239 lines
7.2 KiB
Rust
239 lines
7.2 KiB
Rust
use crate::enums::{AlertDescription, ContentType, HandshakeType, ProtocolVersion};
|
|
use crate::error::InvalidMessage;
|
|
use crate::msgs::alert::AlertMessagePayload;
|
|
use crate::msgs::base::Payload;
|
|
use crate::msgs::ccs::ChangeCipherSpecPayload;
|
|
use crate::msgs::codec::{Codec, Reader};
|
|
use crate::msgs::enums::AlertLevel;
|
|
use crate::msgs::handshake::HandshakeMessagePayload;
|
|
|
|
mod inbound;
|
|
pub use inbound::{BorrowedPayload, InboundOpaqueMessage, InboundPlainMessage};
|
|
|
|
mod outbound;
|
|
use alloc::vec::Vec;
|
|
|
|
pub use outbound::{OutboundChunks, OutboundOpaqueMessage, OutboundPlainMessage, PrefixedPayload};
|
|
|
|
#[derive(Debug)]
|
|
pub enum MessagePayload<'a> {
|
|
Alert(AlertMessagePayload),
|
|
Handshake {
|
|
parsed: HandshakeMessagePayload<'a>,
|
|
encoded: Payload<'a>,
|
|
},
|
|
ChangeCipherSpec(ChangeCipherSpecPayload),
|
|
ApplicationData(Payload<'a>),
|
|
}
|
|
|
|
impl<'a> MessagePayload<'a> {
|
|
pub fn encode(&self, bytes: &mut Vec<u8>) {
|
|
match self {
|
|
Self::Alert(x) => x.encode(bytes),
|
|
Self::Handshake { encoded, .. } => bytes.extend(encoded.bytes()),
|
|
Self::ChangeCipherSpec(x) => x.encode(bytes),
|
|
Self::ApplicationData(x) => x.encode(bytes),
|
|
}
|
|
}
|
|
|
|
pub fn handshake(parsed: HandshakeMessagePayload<'a>) -> Self {
|
|
Self::Handshake {
|
|
encoded: Payload::new(parsed.get_encoding()),
|
|
parsed,
|
|
}
|
|
}
|
|
|
|
pub fn new(
|
|
typ: ContentType,
|
|
vers: ProtocolVersion,
|
|
payload: &'a [u8],
|
|
) -> Result<Self, InvalidMessage> {
|
|
let mut r = Reader::init(payload);
|
|
match typ {
|
|
ContentType::ApplicationData => Ok(Self::ApplicationData(Payload::Borrowed(payload))),
|
|
ContentType::Alert => AlertMessagePayload::read(&mut r).map(MessagePayload::Alert),
|
|
ContentType::Handshake => {
|
|
HandshakeMessagePayload::read_version(&mut r, vers).map(|parsed| Self::Handshake {
|
|
parsed,
|
|
encoded: Payload::Borrowed(payload),
|
|
})
|
|
}
|
|
ContentType::ChangeCipherSpec => {
|
|
ChangeCipherSpecPayload::read(&mut r).map(MessagePayload::ChangeCipherSpec)
|
|
}
|
|
_ => Err(InvalidMessage::InvalidContentType),
|
|
}
|
|
}
|
|
|
|
pub fn content_type(&self) -> ContentType {
|
|
match self {
|
|
Self::Alert(_) => ContentType::Alert,
|
|
Self::Handshake { .. } => ContentType::Handshake,
|
|
Self::ChangeCipherSpec(_) => ContentType::ChangeCipherSpec,
|
|
Self::ApplicationData(_) => ContentType::ApplicationData,
|
|
}
|
|
}
|
|
|
|
pub(crate) fn into_owned(self) -> MessagePayload<'static> {
|
|
use MessagePayload::*;
|
|
match self {
|
|
Alert(x) => Alert(x),
|
|
Handshake { parsed, encoded } => Handshake {
|
|
parsed: parsed.into_owned(),
|
|
encoded: encoded.into_owned(),
|
|
},
|
|
ChangeCipherSpec(x) => ChangeCipherSpec(x),
|
|
ApplicationData(x) => ApplicationData(x.into_owned()),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<Message<'_>> for PlainMessage {
|
|
fn from(msg: Message) -> Self {
|
|
let typ = msg.payload.content_type();
|
|
let payload = match msg.payload {
|
|
MessagePayload::ApplicationData(payload) => payload.into_owned(),
|
|
_ => {
|
|
let mut buf = Vec::new();
|
|
msg.payload.encode(&mut buf);
|
|
Payload::Owned(buf)
|
|
}
|
|
};
|
|
|
|
Self {
|
|
typ,
|
|
version: msg.version,
|
|
payload,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// A decrypted TLS frame
|
|
///
|
|
/// This type owns all memory for its interior parts. It can be decrypted from an OpaqueMessage
|
|
/// or encrypted into an OpaqueMessage, and it is also used for joining and fragmenting.
|
|
#[derive(Clone, Debug)]
|
|
pub struct PlainMessage {
|
|
pub typ: ContentType,
|
|
pub version: ProtocolVersion,
|
|
pub payload: Payload<'static>,
|
|
}
|
|
|
|
impl PlainMessage {
|
|
pub fn into_unencrypted_opaque(self) -> OutboundOpaqueMessage {
|
|
OutboundOpaqueMessage {
|
|
version: self.version,
|
|
typ: self.typ,
|
|
payload: PrefixedPayload::from(self.payload.bytes()),
|
|
}
|
|
}
|
|
|
|
pub fn borrow_inbound(&self) -> InboundPlainMessage<'_> {
|
|
InboundPlainMessage {
|
|
version: self.version,
|
|
typ: self.typ,
|
|
payload: self.payload.bytes(),
|
|
}
|
|
}
|
|
|
|
pub fn borrow_outbound(&self) -> OutboundPlainMessage<'_> {
|
|
OutboundPlainMessage {
|
|
version: self.version,
|
|
typ: self.typ,
|
|
payload: self.payload.bytes().into(),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// A message with decoded payload
|
|
#[derive(Debug)]
|
|
pub struct Message<'a> {
|
|
pub version: ProtocolVersion,
|
|
pub payload: MessagePayload<'a>,
|
|
}
|
|
|
|
impl Message<'_> {
|
|
pub fn is_handshake_type(&self, hstyp: HandshakeType) -> bool {
|
|
// Bit of a layering violation, but OK.
|
|
if let MessagePayload::Handshake { parsed, .. } = &self.payload {
|
|
parsed.typ == hstyp
|
|
} else {
|
|
false
|
|
}
|
|
}
|
|
|
|
pub fn build_alert(level: AlertLevel, desc: AlertDescription) -> Self {
|
|
Self {
|
|
version: ProtocolVersion::TLSv1_2,
|
|
payload: MessagePayload::Alert(AlertMessagePayload {
|
|
level,
|
|
description: desc,
|
|
}),
|
|
}
|
|
}
|
|
|
|
pub fn build_key_update_notify() -> Self {
|
|
Self {
|
|
version: ProtocolVersion::TLSv1_3,
|
|
payload: MessagePayload::handshake(HandshakeMessagePayload::build_key_update_notify()),
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "std")]
|
|
pub(crate) fn into_owned(self) -> Message<'static> {
|
|
let Self { version, payload } = self;
|
|
Message {
|
|
version,
|
|
payload: payload.into_owned(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl TryFrom<PlainMessage> for Message<'static> {
|
|
type Error = InvalidMessage;
|
|
|
|
fn try_from(plain: PlainMessage) -> Result<Self, Self::Error> {
|
|
Ok(Self {
|
|
version: plain.version,
|
|
payload: MessagePayload::new(plain.typ, plain.version, plain.payload.bytes())?
|
|
.into_owned(),
|
|
})
|
|
}
|
|
}
|
|
|
|
/// Parses a plaintext message into a well-typed [`Message`].
|
|
///
|
|
/// A [`PlainMessage`] must contain plaintext content. Encrypted content should be stored in an
|
|
/// [`InboundOpaqueMessage`] and decrypted before being stored into a [`PlainMessage`].
|
|
impl<'a> TryFrom<InboundPlainMessage<'a>> for Message<'a> {
|
|
type Error = InvalidMessage;
|
|
|
|
fn try_from(plain: InboundPlainMessage<'a>) -> Result<Self, Self::Error> {
|
|
Ok(Self {
|
|
version: plain.version,
|
|
payload: MessagePayload::new(plain.typ, plain.version, plain.payload)?,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub enum MessageError {
|
|
TooShortForHeader,
|
|
TooShortForLength,
|
|
InvalidEmptyPayload,
|
|
MessageTooLarge,
|
|
InvalidContentType,
|
|
UnknownProtocolVersion,
|
|
}
|
|
|
|
/// Content type, version and size.
|
|
pub(crate) const HEADER_SIZE: usize = 1 + 2 + 2;
|
|
|
|
/// Maximum message payload size.
|
|
/// That's 2^14 payload bytes and a 2KB allowance for ciphertext overheads.
|
|
const MAX_PAYLOAD: u16 = 16_384 + 2048;
|
|
|
|
/// Maximum on-the-wire message size.
|
|
#[cfg(feature = "std")]
|
|
pub(crate) const MAX_WIRE_SIZE: usize = MAX_PAYLOAD as usize + HEADER_SIZE;
|