1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
use core::ops::{Deref, DerefMut};
use crate::enums::{ContentType, ProtocolVersion};
use crate::error::{Error, PeerMisbehaved};
use crate::msgs::fragmenter::MAX_FRAGMENT_LEN;
/// A TLS frame, named TLSPlaintext in the standard.
///
/// This inbound type borrows its encrypted payload from a buffer elsewhere.
/// It is used for joining and is consumed by decryption.
pub struct InboundOpaqueMessage<'a> {
pub typ: ContentType,
pub version: ProtocolVersion,
pub payload: BorrowedPayload<'a>,
}
impl<'a> InboundOpaqueMessage<'a> {
/// Construct a new `InboundOpaqueMessage` from constituent fields.
///
/// `payload` is borrowed.
pub fn new(typ: ContentType, version: ProtocolVersion, payload: &'a mut [u8]) -> Self {
Self {
typ,
version,
payload: BorrowedPayload(payload),
}
}
/// Force conversion into a plaintext message.
///
/// This should only be used for messages that are known to be in plaintext. Otherwise, the
/// `InboundOpaqueMessage` should be decrypted into a `PlainMessage` using a `MessageDecrypter`.
pub fn into_plain_message(self) -> InboundPlainMessage<'a> {
InboundPlainMessage {
typ: self.typ,
version: self.version,
payload: self.payload.into_inner(),
}
}
/// For TLS1.3 (only), checks the length msg.payload is valid and removes the padding.
///
/// Returns an error if the message (pre-unpadding) is too long, or the padding is invalid,
/// or the message (post-unpadding) is too long.
pub fn into_tls13_unpadded_message(mut self) -> Result<InboundPlainMessage<'a>, Error> {
let payload = &mut self.payload;
if payload.len() > MAX_FRAGMENT_LEN + 1 {
return Err(Error::PeerSentOversizedRecord);
}
self.typ = unpad_tls13_payload(payload);
if self.typ == ContentType::Unknown(0) {
return Err(PeerMisbehaved::IllegalTlsInnerPlaintext.into());
}
if payload.len() > MAX_FRAGMENT_LEN {
return Err(Error::PeerSentOversizedRecord);
}
self.version = ProtocolVersion::TLSv1_3;
Ok(self.into_plain_message())
}
}
pub struct BorrowedPayload<'a>(&'a mut [u8]);
impl Deref for BorrowedPayload<'_> {
type Target = [u8];
fn deref(&self) -> &Self::Target {
self.0
}
}
impl<'a> DerefMut for BorrowedPayload<'a> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.0
}
}
impl<'a> BorrowedPayload<'a> {
pub fn truncate(&mut self, len: usize) {
if len >= self.len() {
return;
}
self.0 = core::mem::take(&mut self.0)
.split_at_mut(len)
.0;
}
pub(crate) fn into_inner(self) -> &'a mut [u8] {
self.0
}
pub(crate) fn pop(&mut self) -> Option<u8> {
if self.is_empty() {
return None;
}
let len = self.len();
let last = self[len - 1];
self.truncate(len - 1);
Some(last)
}
}
/// A TLS frame, named `TLSPlaintext` in the standard.
///
/// This inbound type borrows its decrypted payload from the original buffer.
/// It results from decryption.
#[derive(Debug)]
pub struct InboundPlainMessage<'a> {
pub typ: ContentType,
pub version: ProtocolVersion,
pub payload: &'a [u8],
}
impl InboundPlainMessage<'_> {
/// Returns true if the payload is a CCS message.
///
/// We passthrough ChangeCipherSpec messages in the deframer without decrypting them.
/// Note: this is prior to the record layer, so is unencrypted. See
/// third paragraph of section 5 in RFC8446.
pub(crate) fn is_valid_ccs(&self) -> bool {
self.typ == ContentType::ChangeCipherSpec && self.payload == [0x01]
}
}
/// Decode a TLS1.3 `TLSInnerPlaintext` encoding.
///
/// `p` is a message payload, immediately post-decryption. This function
/// removes zero padding bytes, until a non-zero byte is encountered which is
/// the content type, which is returned. See RFC8446 s5.2.
///
/// ContentType(0) is returned if the message payload is empty or all zeroes.
fn unpad_tls13_payload(p: &mut BorrowedPayload<'_>) -> ContentType {
loop {
match p.pop() {
Some(0) => {}
Some(content_type) => return ContentType::from(content_type),
None => return ContentType::Unknown(0),
}
}
}