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
use super::{Mac, PACKET_MIN_SIZE};

#[cfg(doc)]
use super::Packet;

const MIN_PAD_SIZE: usize = 4;
const MIN_ALIGN: usize = 8;

/// A trait with common methods and associated types involved
/// in the manipulation of [`OpeningCipher`] and [`SealingCipher`].
pub trait CipherCore {
    /// The associated error type returned by the `open` method.
    type Err: From<binrw::Error> + From<std::io::Error>;

    /// The _Message Authentication Code_ associated to the cipher.
    type Mac: Mac;

    /// Gets a reference to the _Message Authentication Code_ for this [`CipherCore`].
    fn mac(&self) -> &Self::Mac;

    /// The size of a [`CipherCore`]'s block.
    fn block_size(&self) -> usize;

    /// Calculate the necessary padding size for the provided payload `size`.
    fn padding(&self, payload: usize) -> u8 {
        let align = self.block_size().max(MIN_ALIGN);

        let size = if self.mac().etm() {
            std::mem::size_of::<u8>() + payload
        } else {
            std::mem::size_of::<u32>() + std::mem::size_of::<u8>() + payload
        };
        let padding = align - size % align;

        let padding = if padding < MIN_PAD_SIZE {
            padding + align
        } else {
            padding
        };

        if size + padding < self.block_size().max(PACKET_MIN_SIZE) {
            (padding + align) as u8
        } else {
            padding as u8
        }
    }
}

/// A cipher able to `open` a [`Packet`] and retrieve it's payload.
pub trait OpeningCipher: CipherCore {
    /// Decrypt the received `buf` using the [`OpeningCipher`].
    fn decrypt<B: AsMut<[u8]>>(&mut self, buf: B) -> Result<(), Self::Err>;

    /// Compare the received `buf` against the received _Message Authentication Code_.
    fn open<B: AsRef<[u8]>>(&mut self, buf: B, mac: Vec<u8>, seq: u32) -> Result<(), Self::Err>;

    /// Decompress the received `buf` using the [`OpeningCipher`].
    fn decompress(&mut self, buf: Vec<u8>) -> Result<Vec<u8>, Self::Err>;
}

/// A cipher able to `seal` a payload to create a [`Packet`].
pub trait SealingCipher: CipherCore {
    /// Decompress the `buf` using the [`SealingCipher`].
    fn compress<B: AsRef<[u8]>>(&mut self, buf: B) -> Result<Vec<u8>, Self::Err>;

    /// Pad the `buf` to match [`SealingCipher`]'s block size with random data,
    /// by increasing it by `padding` bytes and prefixing the `buf` it with it's len.
    fn pad(&mut self, buf: Vec<u8>, padding: u8) -> Result<Vec<u8>, Self::Err>;

    /// Encrypt the `buf` using using the [`SealingCipher`].
    fn encrypt<B: AsMut<[u8]>>(&mut self, buf: B) -> Result<(), Self::Err>;

    /// Generate a seal from the HMAC algorithm to produce a _Message Authentication Code_.
    fn seal<B: AsRef<[u8]>>(&mut self, buf: B, seq: u32) -> Result<Vec<u8>, Self::Err>;
}