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
175
176
177
178
179
use bytes::BytesMut;
use wolfssl_sys::{wc_ChaCha20Poly1305_Decrypt, wc_ChaCha20Poly1305_Encrypt};
/// Struct for encrypt/decrypt using Chacha20 cipher and authentication
/// using Poly1305
pub struct Chacha20Poly1305Aead {
key: [u8; Self::KEY_SIZE],
}
impl Chacha20Poly1305Aead {
/// Size of Initialisation vector
pub const IV_SIZE: usize = wolfssl_sys::CHACHA20_POLY1305_AEAD_IV_SIZE as usize;
/// Size of key
pub const KEY_SIZE: usize = wolfssl_sys::CHACHA20_POLY1305_AEAD_KEYSIZE as usize;
/// Size of auth tag
pub const AUTHTAG_SIZE: usize = wolfssl_sys::CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE as usize;
/// Creates new `Chacha20Poly1305Aead` instance
pub fn new(key: [u8; Self::KEY_SIZE]) -> Self {
Self { key }
}
/// This function encrypts an input message `plain_text`, using the ChaCha20 stream cipher,
/// It also performs Poly-1305 authentication (on the cipher text),
/// and stores the generated authentication tag in the output buffer
pub fn encrypt(
&self,
iv: [u8; Self::IV_SIZE],
plain_text: &[u8],
) -> Result<(BytesMut, [u8; Self::AUTHTAG_SIZE]), String> {
let mut cipher_text = BytesMut::with_capacity(plain_text.len());
let mut auth_tag = [0u8; Self::AUTHTAG_SIZE];
// SAFETY: [`wc_ChaCha20Poly1305_Encrypt`][0] have the following requirements:
// - `inKey` should be a valid pointer with size `wolfssl::CHACHA20_POLY1305_AEAD_KEYSIZE`
// - `inIv` should be a valid pointer with size `wolfssl::CHACHA20_POLY1305_AEAD_IV_SIZE`
// - `outAuthTag` should be a valid mutable pointer with size equal to `wolfssl::CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE`
// - `inAAD` should be a valid pointer with size equal to `inAADLen`
// - `inPlaintext` should be a valid pointer with size equal to `inPlaintextLen`
// - `outCipherText` should be a valid mutable pointer with size equal to `inPlaintextLen`
//
// First three invariants are maintained using byteslice of respective sizes.
// We do not use `inAAD`, hence it is null and 0.
// `inPlaintext` is again a byteslice and `inPlaintextLen` is using len() of bytesslice
// `outCipherText` we are creating a new BytesMut with same capacity as `inPlaintext` explicitly
// [0]: https://www.wolfssl.com/documentation/manuals/wolfssl/chacha20__poly1305_8h.html#function-wc_chacha20poly1305_encrypt
match unsafe {
wc_ChaCha20Poly1305_Encrypt(
self.key.as_ptr(),
iv.as_ptr(),
std::ptr::null(),
0,
plain_text.as_ptr(),
plain_text.len() as u32,
cipher_text.spare_capacity_mut().as_mut_ptr() as *mut u8,
auth_tag.as_mut_ptr(),
)
} {
0 => {
// SAFETY: Now that we've initialized this memory segment, it is safe to update the
// length to account for the initialized data
unsafe {
cipher_text.set_len(plain_text.len());
}
Ok((cipher_text, auth_tag))
}
_ => Err(String::from("Encrypt failed")),
}
}
/// This function decrypts input `cipher_text`, using the ChaCha20 stream cipher.
/// It also performs Poly-1305 authentication, comparing the given inAuthTag to
/// an authentication generated with the inAAD (arbitrary length additional authentication data).
/// Note: If the generated authentication tag does not match the supplied authentication tag,
/// the text is not decrypted.
pub fn decrypt(
&self,
iv: [u8; Self::IV_SIZE],
cipher_text: &[u8],
auth_tag: [u8; Self::AUTHTAG_SIZE],
) -> Result<BytesMut, String> {
let mut plain_text = BytesMut::with_capacity(cipher_text.len());
// SAFETY: [`wc_ChaCha20Poly1305_Decrypt`][0] have the following requirements:
// - `inKey` should be a valid pointer with size `wolfssl::CHACHA20_POLY1305_AEAD_KEYSIZE`
// - `inIv` should be a valid pointer with size `wolfssl::CHACHA20_POLY1305_AEAD_IV_SIZE`
// - `inAuthTag` should be a valid pointer with size `wolfssl::CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE`
// - `inAAD` should be a valid pointer with size equal to `inAADLen`
// - `inCiphertext` should be a valid pointer with size equal to `inCiphertextLen`
// - `outPlainText` should be a valid mutable pointer with size equal to `inCiphertextLen`
//
// First three invariants are maintained using byteslice of respective sizes.
// We do not use `inAAD`, hence it is null and 0.
// `inCiphertext` is again a byteslice and `inCiphertextLen` is using len() of bytesslice
// `outPlainText` we are creating a new BytesMut with same capacity as `inCiphertext` explicitly
// [0]: https://www.wolfssl.com/documentation/manuals/wolfssl/group__ChaCha20Poly1305.html#function-wc_chacha20poly1305_decrypt
match unsafe {
wc_ChaCha20Poly1305_Decrypt(
self.key.as_ptr(),
iv.as_ptr(),
std::ptr::null(),
0,
cipher_text.as_ptr(),
cipher_text.len() as u32,
auth_tag.as_ptr(),
plain_text.spare_capacity_mut().as_mut_ptr() as *mut u8,
)
} {
0 => {
// SAFETY: Now that we've initialized this memory segment, it is safe to update the
// length to account for the initialized data
unsafe {
plain_text.set_len(cipher_text.len());
}
Ok(plain_text)
}
_ => Err(String::from("Decrypt failed")),
}
}
}
#[cfg(test)]
mod tests {
use super::Chacha20Poly1305Aead as Chacha20;
const KEY: [u8; Chacha20::KEY_SIZE] = [
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e,
0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d,
0x9e, 0x9f,
];
const PLAIN_TEXT: [u8; 114] = [
0x4c, 0x61, 0x64, 0x69, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x47, 0x65, 0x6e, 0x74,
0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c,
0x61, 0x73, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x27, 0x39, 0x39, 0x3a, 0x20, 0x49, 0x66, 0x20,
0x49, 0x20, 0x63, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x6f, 0x66, 0x66, 0x65, 0x72, 0x20, 0x79,
0x6f, 0x75, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x74, 0x69, 0x70,
0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x75, 0x74, 0x75, 0x72, 0x65,
0x2c, 0x20, 0x73, 0x75, 0x6e, 0x73, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x20, 0x77, 0x6f, 0x75,
0x6c, 0x64, 0x20, 0x62, 0x65, 0x20, 0x69, 0x74, 0x2e,
];
const IV: [u8; Chacha20::IV_SIZE] = [
0x07, 0x00, 0x00, 0x00, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
];
const CIPHER_TEXT: [u8; 114] = [
0xd3, 0x1a, 0x8d, 0x34, 0x64, 0x8e, 0x60, 0xdb, 0x7b, 0x86, 0xaf, 0xbc, 0x53, 0xef, 0x7e,
0xc2, 0xa4, 0xad, 0xed, 0x51, 0x29, 0x6e, 0x08, 0xfe, 0xa9, 0xe2, 0xb5, 0xa7, 0x36, 0xee,
0x62, 0xd6, 0x3d, 0xbe, 0xa4, 0x5e, 0x8c, 0xa9, 0x67, 0x12, 0x82, 0xfa, 0xfb, 0x69, 0xda,
0x92, 0x72, 0x8b, 0x1a, 0x71, 0xde, 0x0a, 0x9e, 0x06, 0x0b, 0x29, 0x05, 0xd6, 0xa5, 0xb6,
0x7e, 0xcd, 0x3b, 0x36, 0x92, 0xdd, 0xbd, 0x7f, 0x2d, 0x77, 0x8b, 0x8c, 0x98, 0x03, 0xae,
0xe3, 0x28, 0x09, 0x1b, 0x58, 0xfa, 0xb3, 0x24, 0xe4, 0xfa, 0xd6, 0x75, 0x94, 0x55, 0x85,
0x80, 0x8b, 0x48, 0x31, 0xd7, 0xbc, 0x3f, 0xf4, 0xde, 0xf0, 0x8e, 0x4b, 0x7a, 0x9d, 0xe5,
0x76, 0xd2, 0x65, 0x86, 0xce, 0xc6, 0x4b, 0x61, 0x16,
];
const AUTH_TAG: [u8; Chacha20::AUTHTAG_SIZE] = [
0x6a, 0x23, 0xa4, 0x68, 0x1f, 0xd5, 0x94, 0x56, 0xae, 0xa1, 0xd2, 0x9f, 0x82, 0x47, 0x72,
0x16,
];
#[test]
fn test_chacha20_encrypt() -> Result<(), String> {
let cipher = Chacha20::new(KEY);
let (cipher_text, auth_tag) = cipher.encrypt(IV, &PLAIN_TEXT).unwrap();
assert_eq!(&cipher_text[..], &CIPHER_TEXT);
assert_eq!(auth_tag, AUTH_TAG);
Ok(())
}
#[test]
fn test_chacha20_decrypt() -> Result<(), String> {
let cipher = Chacha20::new(KEY);
let plaint_text = cipher.decrypt(IV, &CIPHER_TEXT, AUTH_TAG).unwrap();
assert_eq!(&plaint_text[..], &PLAIN_TEXT);
Ok(())
}
}