rustls/msgs/message/
inbound.rs

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