1 | use core::ops::{Deref, DerefMut, Range}; |
2 | |
3 | use crate::enums::{ContentType, ProtocolVersion}; |
4 | use crate::error::{Error, PeerMisbehaved}; |
5 | use 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 | pub struct InboundOpaqueMessage<'a> { |
12 | pub typ: ContentType, |
13 | pub version: ProtocolVersion, |
14 | pub payload: BorrowedPayload<'a>, |
15 | } |
16 | |
17 | impl<'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 message (post-unpadding) is too long. |
60 | pub fn into_tls13_unpadded_message(mut self) -> Result<InboundPlainMessage<'a>, Error> { |
61 | let payload = &mut self.payload; |
62 | |
63 | if payload.len() > MAX_FRAGMENT_LEN + 1 { |
64 | return Err(Error::PeerSentOversizedRecord); |
65 | } |
66 | |
67 | self.typ = unpad_tls13_payload(payload); |
68 | if self.typ == ContentType::Unknown(0) { |
69 | return Err(PeerMisbehaved::IllegalTlsInnerPlaintext.into()); |
70 | } |
71 | |
72 | if payload.len() > MAX_FRAGMENT_LEN { |
73 | return Err(Error::PeerSentOversizedRecord); |
74 | } |
75 | |
76 | self.version = ProtocolVersion::TLSv1_3; |
77 | Ok(self.into_plain_message()) |
78 | } |
79 | } |
80 | |
81 | pub struct BorrowedPayload<'a>(&'a mut [u8]); |
82 | |
83 | impl Deref for BorrowedPayload<'_> { |
84 | type Target = [u8]; |
85 | |
86 | fn deref(&self) -> &Self::Target { |
87 | self.0 |
88 | } |
89 | } |
90 | |
91 | impl DerefMut for BorrowedPayload<'_> { |
92 | fn deref_mut(&mut self) -> &mut Self::Target { |
93 | self.0 |
94 | } |
95 | } |
96 | |
97 | impl<'a> BorrowedPayload<'a> { |
98 | pub fn truncate(&mut self, len: usize) { |
99 | if len >= self.len() { |
100 | return; |
101 | } |
102 | |
103 | self.0 = core::mem::take(&mut self.0) |
104 | .split_at_mut(len) |
105 | .0; |
106 | } |
107 | |
108 | pub(crate) fn into_inner(self) -> &'a mut [u8] { |
109 | self.0 |
110 | } |
111 | |
112 | pub(crate) fn pop(&mut self) -> Option<u8> { |
113 | if self.is_empty() { |
114 | return None; |
115 | } |
116 | |
117 | let len = self.len(); |
118 | let last = self[len - 1]; |
119 | self.truncate(len - 1); |
120 | Some(last) |
121 | } |
122 | } |
123 | |
124 | /// A TLS frame, named `TLSPlaintext` in the standard. |
125 | /// |
126 | /// This inbound type borrows its decrypted payload from the original buffer. |
127 | /// It results from decryption. |
128 | #[derive (Debug)] |
129 | pub struct InboundPlainMessage<'a> { |
130 | pub typ: ContentType, |
131 | pub version: ProtocolVersion, |
132 | pub payload: &'a [u8], |
133 | } |
134 | |
135 | impl InboundPlainMessage<'_> { |
136 | /// Returns true if the payload is a CCS message. |
137 | /// |
138 | /// We passthrough ChangeCipherSpec messages in the deframer without decrypting them. |
139 | /// Note: this is prior to the record layer, so is unencrypted. See |
140 | /// third paragraph of section 5 in RFC8446. |
141 | pub(crate) fn is_valid_ccs(&self) -> bool { |
142 | self.typ == ContentType::ChangeCipherSpec && self.payload == [0x01] |
143 | } |
144 | } |
145 | |
146 | /// Decode a TLS1.3 `TLSInnerPlaintext` encoding. |
147 | /// |
148 | /// `p` is a message payload, immediately post-decryption. This function |
149 | /// removes zero padding bytes, until a non-zero byte is encountered which is |
150 | /// the content type, which is returned. See RFC8446 s5.2. |
151 | /// |
152 | /// ContentType(0) is returned if the message payload is empty or all zeroes. |
153 | fn unpad_tls13_payload(p: &mut BorrowedPayload<'_>) -> ContentType { |
154 | loop { |
155 | match p.pop() { |
156 | Some(0) => {} |
157 | Some(content_type: u8) => return ContentType::from(content_type), |
158 | None => return ContentType::Unknown(0), |
159 | } |
160 | } |
161 | } |
162 | |