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
//! Api functions related to cryptographic secretbox encryption / decryption
//! using the xchacha20poly1305 algorithm.
//!
//! See secretbox module-level documentation for usage examples.

use crate::*;

/// length of secretbox key
pub const KEYBYTES: usize =
    libsodium_sys::crypto_secretbox_xchacha20poly1305_KEYBYTES as usize;

/// length of secretbox nonce
pub const NONCEBYTES: usize =
    libsodium_sys::crypto_secretbox_xchacha20poly1305_NONCEBYTES as usize;

/// length of secretbox mac
pub const MACBYTES: usize =
    libsodium_sys::crypto_secretbox_xchacha20poly1305_MACBYTES as usize;

/// encrypt data with crytpo_secretbox_xchacha20poly1305_easy
pub async fn easy<N, M, K>(
    nonce: N,
    message: M,
    shared_key: K,
) -> SodokenResult<BufRead>
where
    N: Into<BufReadSized<NONCEBYTES>> + 'static + Send,
    M: Into<BufRead> + 'static + Send,
    K: Into<BufReadSized<KEYBYTES>> + 'static + Send,
{
    let nonce = nonce.into();
    let message = message.into();
    let shared_key = shared_key.into();
    tokio_exec_blocking(move || {
        let nonce = nonce.read_lock_sized();
        let message = message.read_lock();
        let shared_key = shared_key.read_lock_sized();
        let cipher = safe::sodium::crypto_secretbox_xchacha20poly1305_easy(
            &nonce,
            &message,
            &shared_key,
        )?;
        Ok(cipher.into())
    })
    .await
}

/// calculate the message len for an easy cipher len
pub fn open_easy_msg_len(cipher_len: usize) -> usize {
    cipher_len - MACBYTES
}

/// decrypt data with crypto_secretbox_xchacha20poly1305_open_easy
pub async fn open_easy<N, M, C, K>(
    nonce: N,
    message: M,
    cipher: C,
    shared_key: K,
) -> SodokenResult<()>
where
    N: Into<BufReadSized<NONCEBYTES>> + 'static + Send,
    M: Into<BufWrite> + 'static + Send,
    C: Into<BufRead> + 'static + Send,
    K: Into<BufReadSized<KEYBYTES>> + 'static + Send,
{
    let nonce = nonce.into();
    let message = message.into();
    let cipher = cipher.into();
    let shared_key = shared_key.into();
    tokio_exec_blocking(move || {
        let nonce = nonce.read_lock_sized();
        let mut message = message.write_lock();
        let cipher = cipher.read_lock();
        let shared_key = shared_key.read_lock_sized();
        safe::sodium::crypto_secretbox_xchacha20poly1305_open_easy(
            &nonce,
            &mut message,
            &cipher,
            &shared_key,
        )
    })
    .await
}

#[cfg(test)]
mod tests {
    use crate::*;

    #[tokio::test(flavor = "multi_thread")]
    async fn test_secretbox() -> SodokenResult<()> {
        let nonce: BufReadSized<{ secretbox::xchacha20poly1305::NONCEBYTES }> =
            BufReadSized::new_no_lock(
                [0; secretbox::xchacha20poly1305::NONCEBYTES],
            );
        let shared_key: BufWriteSized<
            { secretbox::xchacha20poly1305::KEYBYTES },
        > = BufWriteSized::new_mem_locked()?;
        let msg = BufRead::new_no_lock(b"test message");

        random::bytes_buf(shared_key.clone()).await?;

        let cipher = secretbox::xchacha20poly1305::easy(
            nonce.clone(),
            msg.clone(),
            shared_key.clone(),
        )
        .await?;
        assert_ne!(&*msg.read_lock(), &*cipher.read_lock());

        let msg_len = secretbox::xchacha20poly1305::open_easy_msg_len(
            cipher.read_lock().len(),
        );
        let msg2 = BufWrite::new_no_lock(msg_len);

        secretbox::xchacha20poly1305::open_easy(
            nonce.clone(),
            msg2.clone(),
            cipher.clone(),
            shared_key.clone(),
        )
        .await?;

        assert_eq!(&*msg.read_lock(), &*msg2.read_lock());

        Ok(())
    }
}