ytls_client/client_ctx/
c_application.rs1mod p_wrapped;
4
5use crate::{CtxError, Rfc8446Error};
6use ytls_traits::CtxApplicationProcessor;
7use ytls_traits::ShutdownComplete;
8use ytls_traits::{TlsLeftIn, TlsLeftOut, TlsRight};
9
10use ytls_traits::CryptoConfig;
11use ytls_traits::SecretStore;
12
13use ytls_record::Content;
14use ytls_record::Record;
15use ytls_util::Nonce12;
16
17use ytls_record::HandshakeMsg;
18use ytls_record::MsgType;
19use ytls_record::WrappedMsgType;
20use ytls_record::WrappedRecord;
21
22#[cfg(feature = "zeroize")]
23use zeroize::{Zeroize, ZeroizeOnDrop};
24
25#[cfg_attr(feature = "zeroize", derive(Zeroize, ZeroizeOnDrop))]
27pub struct ClientApplicationCtx<Crypto> {
28 application_server_key: [u8; 32],
30 application_client_key: [u8; 32],
31 #[cfg_attr(feature = "zeroize", zeroize(skip))]
32 application_server_iv: Nonce12,
33 #[cfg_attr(feature = "zeroize", zeroize(skip))]
34 application_client_iv: Nonce12,
35 _pt: core::marker::PhantomData<Crypto>,
36}
37
38impl<Crypto> ClientApplicationCtx<Crypto>
39where
40 Crypto: CryptoConfig,
41{
42 pub fn with_required<S: SecretStore>(_crypto: Crypto, s: &S) -> Self {
43 let mut application_server_key: [u8; 32] = [0; 32];
44 let mut application_client_key: [u8; 32] = [0; 32];
45 let mut application_server_iv_raw: [u8; 12] = [0; 12];
46 let mut application_client_iv_raw: [u8; 12] = [0; 12];
47
48 application_server_key.copy_from_slice(s.load_ap_server_key());
49 application_client_key.copy_from_slice(s.load_ap_client_key());
50 application_server_iv_raw.copy_from_slice(s.load_ap_server_iv());
51 application_client_iv_raw.copy_from_slice(s.load_ap_client_iv());
52
53 let application_server_iv = Nonce12::from_ks_iv(&application_server_iv_raw);
54 let application_client_iv = Nonce12::from_ks_iv(&application_client_iv_raw);
55
56 Self {
57 _pt: core::marker::PhantomData,
58 application_server_key,
59 application_client_key,
60 application_server_iv,
61 application_client_iv,
62 }
63 }
64}
65
66impl<Crypto> CtxApplicationProcessor for ClientApplicationCtx<Crypto>
67where
68 Crypto: CryptoConfig,
69{
70 type Error = CtxError;
71
72 fn spin_application<Li: TlsLeftIn, Lo: TlsLeftOut, R: TlsRight>(
73 &mut self,
74 li: &mut Li,
75 lo: &mut Lo,
76 right: &mut R,
77 ) -> Result<Option<ShutdownComplete>, Self::Error> {
78 let init_data = li.left_buf_in();
79 let init_len = init_data.len();
80 let mut data = init_data;
81
82 if init_len == 0 {
83 return Ok(None);
84 }
85
86 #[allow(unused_assignments)]
87 let mut consumed = 0;
88
89 loop {
90 let (rec, remaining) =
91 Record::parse_client_appdata(data).map_err(|e| CtxError::Record(e))?;
92
93 consumed = init_len - remaining.len();
94
95 if let Content::ApplicationData = rec.content() {
96 let key = self.application_server_key;
97 let nonce: [u8; 12] = match self.application_server_iv.use_and_incr() {
98 Some(cur) => cur,
99 None => return Err(CtxError::ExhaustedIv),
100 };
101
102 let cipher = Crypto::aead_chaha20poly1305(&key);
103 let full_payload = rec.as_bytes();
104 let full_payload_len = full_payload.len();
105 let mut tag: [u8; 16] = [0; 16];
106
107 let body_len = full_payload_len - 16;
108 let mut body: [u8; 8192] = [0; 8192];
109 body[0..body_len].copy_from_slice(&full_payload[0..body_len]);
110 tag.copy_from_slice(&full_payload[body_len..body_len + 16]);
111 let additional_data = rec.header_as_bytes();
112 use ytls_traits::CryptoChaCha20Poly1305Processor;
113 cipher
114 .decrypt_in_place(&nonce, &additional_data, &mut body[0..body_len], &tag)
115 .map_err(|_| CtxError::Rfc8446(Rfc8446Error::Decrypt))?;
116
117 let wrapped_rec = WrappedRecord::parse_server_ap(self, &body[0..body_len])
118 .map_err(|_| CtxError::Rfc8446(Rfc8446Error::Unexpected))?;
119
120 match wrapped_rec.msg() {
121 WrappedMsgType::Handshake(HandshakeMsg {
122 msg: MsgType::NewSessionTicket,
123 ..
124 }) => {
125 }
127 WrappedMsgType::ApplicationData => {
128 right.on_decrypted(&body[0..body_len - 1]);
130 }
131 WrappedMsgType::Alert(_) => {}
132 _ => return Err(CtxError::Rfc8446(Rfc8446Error::Unexpected)),
133 }
134
135 let d = right.on_encrypt();
136
137 if d.len() > 0 {
138 let client_key = self.application_client_key;
139 let client_nonce: [u8; 12] = match self.application_client_iv.use_and_incr() {
140 Some(cur) => cur,
141 None => return Err(CtxError::ExhaustedIv),
142 };
143
144 use ytls_record::WrappedAppStaticRecordBuilder;
145 use ytls_traits::WrappedApplicationBuilder;
146
147 let mut record_encrypt =
148 WrappedAppStaticRecordBuilder::<8192>::application_data(d)
149 .map_err(CtxError::Builder)?;
150
151 let cipher = Crypto::aead_chaha20poly1305(&client_key);
152
153 let tag = if let Ok([additional_data, encrypt_payload]) =
155 record_encrypt.as_disjoint_mut_for_aead()
156 {
157 cipher
158 .encrypt_in_place(
159 &client_nonce,
160 &additional_data,
161 encrypt_payload.as_mut(),
162 )
163 .map_err(|_| CtxError::Bug("Encrypt failure."))?
164 } else {
165 return Err(CtxError::Bug(
166 "Disjoint for AEAD failed at Application data.",
167 ));
168 };
169 record_encrypt.set_auth_tag(&tag);
170
171 lo.send_record_out(record_encrypt.as_encoded_bytes());
172 }
173 }
174
175 if remaining.len() == 0 {
176 break;
177 }
178
179 data = remaining;
180 }
181 li.left_buf_mark_discard_in(consumed);
182
183 Ok(None)
184 }
185}