ytls_client/client_ctx/
c_application.rs

1//! yTLS Client Application Ctx
2
3mod 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/// yTLS Client Application Ctx
26#[cfg_attr(feature = "zeroize", derive(Zeroize, ZeroizeOnDrop))]
27pub struct ClientApplicationCtx<Crypto> {
28    //crypto: Crypto,
29    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                        //
126                    }
127                    WrappedMsgType::ApplicationData => {
128                        //println!("Received {:?}", core::str::from_utf8(&body[0..body_len - 1]));
129                        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                    // TODO: transcript
154                    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}