encryption/encryptor/
threefish512cbc.rs1use cbc::cipher::{BlockDecryptMut, BlockEncryptMut, KeyIvInit, block_padding::Pkcs7};
7use cbc::{Decryptor, Encryptor};
8use rand_core::{OsRng, RngCore};
9use threefish::Threefish512;
10
11use crate::contract::{Identified, SymmetricCipher};
12use crate::error::{Error, Result};
13use crate::internal::etm;
14use crate::key::Key;
15
16const LABEL: &str = "Threefish-512-CBC";
17const IV_LEN: usize = 64;
19
20#[derive(Debug)]
22pub struct Threefish512CbcEncryptor {
23 key: Key<64>,
24}
25
26impl Threefish512CbcEncryptor {
27 pub const IDENTIFIER: &'static str = "threefish-512-cbc-hmac";
28
29 pub fn new(key: [u8; 64]) -> Self {
30 Self { key: Key::new(key) }
31 }
32
33 pub fn from_slice(key: &[u8]) -> Result<Self> {
34 Ok(Self {
35 key: Key::from_slice(key, LABEL)?,
36 })
37 }
38}
39
40impl Identified for Threefish512CbcEncryptor {
41 fn identifier(&self) -> &str {
42 Self::IDENTIFIER
43 }
44}
45
46impl SymmetricCipher for Threefish512CbcEncryptor {
47 fn encrypt(&self, plaintext: &[u8]) -> Result<Vec<u8>> {
48 let mut iv = [0u8; IV_LEN];
49 OsRng.fill_bytes(&mut iv);
50
51 let ciphertext = Encryptor::<Threefish512>::new_from_slices(self.key.as_bytes(), &iv)
52 .expect("key and iv lengths are fixed")
53 .encrypt_padded_vec_mut::<Pkcs7>(plaintext);
54
55 Ok(etm::pack(&iv, &ciphertext, self.key.as_bytes()))
56 }
57
58 fn decrypt(&self, ciphertext: &[u8]) -> Result<Vec<u8>> {
59 let (iv, body) = etm::unpack_and_verify(ciphertext, IV_LEN, self.key.as_bytes(), LABEL)?;
60
61 Decryptor::<Threefish512>::new_from_slices(self.key.as_bytes(), iv)
62 .expect("key and iv lengths were verified")
63 .decrypt_padded_vec_mut::<Pkcs7>(body)
64 .map_err(|_| Error::DecryptionFailed { label: LABEL })
65 }
66}
67
68#[cfg(test)]
69mod tests {
70 use super::*;
71
72 fn unhex(s: &str) -> Vec<u8> {
74 (0..s.len() / 2)
75 .map(|i| u8::from_str_radix(&s[2 * i..2 * i + 2], 16).unwrap())
76 .collect()
77 }
78
79 #[test]
86 fn bare_block_cipher_matches_official_kat() {
87 use cbc::cipher::{Block, BlockEncrypt, KeyInit};
88
89 let vectors = [
90 (
91 "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
92 "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
93 concat!(
94 "b1a2bbc6ef6025bc40eb3822161f36e375d1bb0aee3186fbd19e47c5d479947b",
95 "7bc2f8586e35f0cff7e7f03084b0b7b1f1ab3961a580a3e97eb41ea14a6d7bbe"
96 ),
97 ),
98 (
99 concat!(
100 "b1a2bbc6ef6025bc40eb3822161f36e375d1bb0aee3186fbd19e47c5d479947b",
101 "7bc2f8586e35f0cff7e7f03084b0b7b1f1ab3961a580a3e97eb41ea14a6d7bbe"
102 ),
103 "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
104 concat!(
105 "f13ca06760dd9bbeab87b6c56f3bbbdb",
106 "e9d08a77978b942ac02d471dc10268f2",
107 "261c3d4330d6ca341f4bd4115dee16a2",
108 "1dcda2a34a0a76fba976174e4cf1e306"
109 ),
110 ),
111 ];
112
113 for (key, plaintext, expected) in vectors {
114 let key = unhex(key);
115 let plaintext = unhex(plaintext);
116 let expected = unhex(expected);
117
118 let cipher = Threefish512::new_from_slice(&key).unwrap();
119 let mut block = Block::<Threefish512>::clone_from_slice(&plaintext);
120 cipher.encrypt_block(&mut block);
121 assert_eq!(block.as_slice(), expected.as_slice());
122 }
123 }
124
125 #[test]
126 fn roundtrip_at_padding_boundaries() {
127 let encryptor = Threefish512CbcEncryptor::new([5u8; 64]);
128 for plaintext in [
129 b"".as_slice(),
130 b"x",
131 b"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
132 b"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdefg",
133 ] {
134 let blob = encryptor.encrypt(plaintext).unwrap();
135 assert_eq!(encryptor.decrypt(&blob).unwrap(), plaintext);
136 }
137 }
138
139 #[test]
142 fn frame_uses_64_byte_iv() {
143 let encryptor = Threefish512CbcEncryptor::new([5u8; 64]);
144 let blob = encryptor.encrypt(b"x").unwrap();
145 assert_eq!(blob.len(), 2 + 64 + 32 + 64);
147 assert_eq!(&blob[..2], b"v1");
148 }
149
150 #[test]
151 fn tampered_blob_fails_mac_before_decrypt() {
152 let encryptor = Threefish512CbcEncryptor::new([5u8; 64]);
153 let mut blob = encryptor.encrypt(b"secret").unwrap();
154 let last = blob.len() - 1;
155 blob[last] ^= 0x01;
156 assert_eq!(
157 encryptor.decrypt(&blob).unwrap_err(),
158 Error::MacVerificationFailed { label: LABEL }
159 );
160 }
161
162 #[test]
163 fn wrong_key_fails_mac() {
164 let blob = Threefish512CbcEncryptor::new([5u8; 64])
165 .encrypt(b"secret")
166 .unwrap();
167 let other = Threefish512CbcEncryptor::new([6u8; 64]);
168 assert_eq!(
169 other.decrypt(&blob).unwrap_err(),
170 Error::MacVerificationFailed { label: LABEL }
171 );
172 }
173
174 #[test]
175 fn iv_is_fresh_per_encryption() {
176 let encryptor = Threefish512CbcEncryptor::new([5u8; 64]);
177 let first = encryptor.encrypt(b"same plaintext").unwrap();
178 let second = encryptor.encrypt(b"same plaintext").unwrap();
179 assert_ne!(first, second);
180 assert_ne!(first[2..66], second[2..66]);
182 }
183
184 #[test]
185 fn invalid_prefix_and_too_short_are_rejected() {
186 let encryptor = Threefish512CbcEncryptor::new([5u8; 64]);
187 assert_eq!(
188 encryptor.decrypt(b"v9 not a blob").unwrap_err(),
189 Error::InvalidPrefix { label: LABEL }
190 );
191 assert_eq!(
192 encryptor.decrypt(b"v1 short").unwrap_err(),
193 Error::TooShort { label: LABEL }
194 );
195 }
196
197 #[test]
198 fn key_length_is_checked() {
199 assert_eq!(
200 Threefish512CbcEncryptor::from_slice(&[0u8; 32]).unwrap_err(),
201 Error::InvalidKeyLength {
202 label: LABEL,
203 expected: 64,
204 got: 32
205 }
206 );
207 }
208}