1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
/*
 * Created on Sun Sep 10 2023
 *
 * Copyright (c) storycraft. Licensed under the MIT Licence.
 */

pub use rsa::RsaPublicKey;

use alloc::{boxed::Box, collections::VecDeque, vec::Vec};
use core::mem;

use aes::cipher::{AsyncStreamCipher, Key, KeyIvInit};
use arrayvec::ArrayVec;
use rand::thread_rng;
use rsa::Oaep;
use serde::{Deserialize, Serialize};
use sha1::Sha1;

use super::SecurePacket;

type Aes128CfbEnc = cfb_mode::Encryptor<aes::Aes128>;
type Aes128CfbDec = cfb_mode::Decryptor<aes::Aes128>;

#[derive(Debug)]
/// IO-free client secure layer
pub struct LocoClientSecureLayer {
    key: Key<aes::Aes128>,

    read_state: ReadState,

    encrypt_buffer: Vec<u8>,

    /// Read buffer for layer
    pub read_buffer: VecDeque<u8>,

    /// Write buffer for layer
    pub write_buffer: VecDeque<u8>,
}

impl LocoClientSecureLayer {
    /// Create new [`LocoClientSecureLayer`] with given encrypt key
    pub fn new(encrypt_key: [u8; 16]) -> Self {
        Self {
            key: encrypt_key.into(),

            read_state: ReadState::Pending,

            encrypt_buffer: Vec::new(),

            read_buffer: VecDeque::new(),
            write_buffer: VecDeque::new(),
        }
    }

    pub const fn read_state(&self) -> &ReadState {
        &self.read_state
    }

    /// Write handshake packet to [`LocoClientSecureLayer::write_buffer`] using given public key
    pub fn handshake(&mut self, key: &RsaPublicKey) {
        #[derive(Serialize)]
        struct RawHandshakeHeader {
            encrypted_key_size: u32,
            key_type: u32,
            encrypt_type: u32,
        }

        let encrypted_key = key
            .encrypt(
                &mut thread_rng(),
                Oaep::new_with_mgf_hash::<Sha1, Sha1>(),
                self.key.as_slice(),
            )
            .unwrap();

        bincode::serialize_into(
            &mut self.write_buffer,
            &RawHandshakeHeader {
                encrypted_key_size: encrypted_key.len() as u32,
                key_type: 15,    // RSA OAEP SHA1 MGF1 SHA1
                encrypt_type: 2, // AES_CFB128 NOPADDING
            },
        )
        .unwrap();

        self.write_buffer.extend(encrypted_key);
    }

    /// Try to read single [`SecurePacket`] from [`LocoClientSecureLayer::read_buffer`]
    pub fn read(&mut self) -> Option<SecurePacket<Box<[u8]>>> {
        loop {
            match mem::replace(&mut self.read_state, ReadState::Corrupted) {
                ReadState::Pending => {
                    if self.read_buffer.len() < 20 {
                        self.read_state = ReadState::Pending;
                        return None;
                    }

                    let raw_header = {
                        let buf = self.read_buffer.drain(..20).collect::<ArrayVec<u8, 20>>();

                        bincode::deserialize::<RawHeader>(&buf).unwrap()
                    };

                    self.read_state = ReadState::Header(raw_header);
                }

                ReadState::Header(raw_header) => {
                    let size = raw_header.size as usize - 16;

                    if self.read_buffer.len() < size {
                        self.read_state = ReadState::Header(raw_header);
                        return None;
                    }

                    let mut data = self.read_buffer.drain(..size).collect::<Box<[u8]>>();
                    Aes128CfbDec::new(&self.key, &raw_header.iv.into()).decrypt(&mut data);

                    self.read_state = ReadState::Pending;
                    return Some(SecurePacket {
                        iv: raw_header.iv,
                        data,
                    });
                }

                ReadState::Corrupted => unreachable!(),
            }
        }
    }

    /// Write single [`SecurePacket`] to [`LocoClientSecureLayer::write_buffer`]
    pub fn send(&mut self, packet: SecurePacket<impl AsRef<[u8]>>) {
        let encrypted_data = {
            let data = packet.data.as_ref();

            self.encrypt_buffer.extend(data);
            Aes128CfbEnc::new(&self.key, &packet.iv.into()).encrypt(&mut self.encrypt_buffer);

            &mut self.encrypt_buffer
        };

        bincode::serialize_into(
            &mut self.write_buffer,
            &RawHeader {
                size: 16 + encrypted_data.len() as u32,
                iv: packet.iv,
            },
        )
        .unwrap();

        self.write_buffer.extend(encrypted_data.drain(..));
    }
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct RawHeader {
    /// Data size including iv
    pub size: u32,

    /// Encrypted data IV
    pub iv: [u8; 16],
}

#[derive(Debug, Clone, PartialEq)]
pub enum ReadState {
    /// Client is waiting for packet
    Pending,

    /// Client read packet header and waiting for more data
    Header(RawHeader),

    /// Client corrupted and cannot continue
    Corrupted,
}