Skip to main content

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