|
@@ -2,12 +2,30 @@
|
|
|
|
|
|
use super::*;
|
|
|
use openssl::{
|
|
|
- symm::{Cipher, Crypter, Mode}
|
|
|
+ symm::{Cipher, encrypt as openssl_encrypt, decrypt as openssl_decrypt}
|
|
|
};
|
|
|
+use serde_block_tree::{to_vec, from_vec};
|
|
|
+use serde::de::{DeserializeOwned};
|
|
|
+
|
|
|
+/// Data that may or may not be encrypted.
|
|
|
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
|
|
+pub enum Cryptotext<T: Serialize> {
|
|
|
+ /// The inner value of `T` is in plaintext.
|
|
|
+ Plain(T),
|
|
|
+ /// The inner value of `T` is in ciphertext.
|
|
|
+ Cipher(Vec<u8>),
|
|
|
+}
|
|
|
+
|
|
|
+pub trait Hydrate<const N: usize> {
|
|
|
+ fn hydrate(template: Self, data: [u8; N]) -> Self;
|
|
|
+}
|
|
|
|
|
|
/// Errors that can occur during cryptographic operations.
|
|
|
+#[derive(Debug)]
|
|
|
pub enum Error {
|
|
|
- NoReadCap
|
|
|
+ NoReadCap,
|
|
|
+ Encryption(String),
|
|
|
+ Decryption(String),
|
|
|
}
|
|
|
|
|
|
pub type Result<T> = std::result::Result<T, Error>;
|
|
@@ -33,20 +51,78 @@ pub enum Signature {
|
|
|
#[allow(dead_code)]
|
|
|
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
|
|
pub enum Key {
|
|
|
- Xsalsa20Poly1305([u8; 32]),
|
|
|
+ // For keys that contain IVs, the key always comes first. The function `key_len` can be used
|
|
|
+ // to get the length of the key part of a variant's array.
|
|
|
+
|
|
|
+ /// A key for the AES 256 cipher in Cipher Block Chaining mode. Note that this includes the
|
|
|
+ /// initialization vector, so that a value of this variant contains all the information needed
|
|
|
+ /// to fully initialize a cipher context.
|
|
|
+ #[serde(with = "BigArray")]
|
|
|
+ Aes256Cbc([u8; 32 + 16]),
|
|
|
+ /// A key for the AES 256 cipher in counter mode.
|
|
|
+ Aes256Ctr([u8; 32]),
|
|
|
}
|
|
|
|
|
|
impl Key {
|
|
|
- /// Returns the data in the key as a slice.
|
|
|
- fn as_slice(&self) -> &[u8] {
|
|
|
- let Key::Xsalsa20Poly1305(array) = self;
|
|
|
- array
|
|
|
+ /// Returns the array in this `Key` as a slice.
|
|
|
+ pub fn as_slice(&self) -> &[u8] {
|
|
|
+ match self {
|
|
|
+ Key::Aes256Cbc(array) => array,
|
|
|
+ Key::Aes256Ctr(array) => array,
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- /// Returns the data in the key as a mutable slice.
|
|
|
- fn as_mut_slice(&mut self) -> &mut [u8] {
|
|
|
- let Key::Xsalsa20Poly1305(array) = self;
|
|
|
- array
|
|
|
+ /// Returns the array in this `Key` as a mutable slice.
|
|
|
+ pub fn as_mut_slice(&mut self) -> &mut [u8] {
|
|
|
+ match self {
|
|
|
+ Key::Aes256Cbc(array) => array,
|
|
|
+ Key::Aes256Ctr(array) => array,
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Returns the length of the key part of the array in this `Key`. Note that the bytes of the
|
|
|
+ /// key come before the bytes of the IV.
|
|
|
+ const fn key_len(&self) -> usize {
|
|
|
+ let (array_len, key_len) = match self {
|
|
|
+ Key::Aes256Cbc(array) => (array.len(), 32),
|
|
|
+ Key::Aes256Ctr(array) => (array.len(), 32)
|
|
|
+ };
|
|
|
+ debug_assert!(key_len <= array_len);
|
|
|
+ key_len
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Returns the key part of this `Key`.
|
|
|
+ pub fn key_slice(&self) -> &[u8] {
|
|
|
+ let key_len = self.key_len();
|
|
|
+ &self.as_slice()[..key_len]
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Returns the IV part of this`Key`.
|
|
|
+ pub fn iv_slice(&self) -> Option<&[u8]> {
|
|
|
+ let key_len = self.key_len();
|
|
|
+ let slice = self.as_slice();
|
|
|
+ if key_len == slice.len() {
|
|
|
+ None
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ Some(&slice[key_len..])
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Packs the given key and IV into the given output buffer.
|
|
|
+ pub fn pack(output: &mut [u8], key: &[u8], iv: &[u8]) {
|
|
|
+ debug_assert_eq!(output.len(), key.len() + iv.len());
|
|
|
+ let key_len = key.len();
|
|
|
+ output[..key_len].copy_from_slice(key);
|
|
|
+ output[key_len..].copy_from_slice(iv);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Returns the cipher that this key is for.
|
|
|
+ fn cipher(&self) -> Cipher {
|
|
|
+ match self {
|
|
|
+ Key::Aes256Cbc(_) => Cipher::aes_256_cbc(),
|
|
|
+ Key::Aes256Ctr(_) => Cipher::aes_256_ctr(),
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -67,16 +143,16 @@ pub(crate) fn encrypt_block(
|
|
|
versioned_block: VersionedBlock, principal: &Principal, key: &Key
|
|
|
) -> Result<VersionedBlock> {
|
|
|
let VersionedBlock::V0(mut block) = versioned_block;
|
|
|
- let mut body = match block.body {
|
|
|
+ let body = match block.body {
|
|
|
Cryptotext::Cipher(_) => return Ok(VersionedBlock::V0(block)),
|
|
|
Cryptotext::Plain(body) => body
|
|
|
};
|
|
|
let (principal_owned, read_cap) = block.read_caps.remove_entry(principal)
|
|
|
.ok_or(Error::NoReadCap)?;
|
|
|
let block_key = decrypt(read_cap, key)?;
|
|
|
- encrypt_in_place(&mut body, &block_key)?;
|
|
|
- block.body = Cryptotext::Cipher(body);
|
|
|
- block.read_caps.insert(principal_owned, encrypt(block_key, key)?);
|
|
|
+ let new_body = encrypt_slice(&body, &block_key)?;
|
|
|
+ block.body = Cryptotext::Cipher(new_body);
|
|
|
+ block.read_caps.insert(principal_owned, encrypt(&block_key, key)?);
|
|
|
Ok(VersionedBlock::V0(block))
|
|
|
}
|
|
|
|
|
@@ -85,90 +161,98 @@ pub(crate) fn decrypt_block(
|
|
|
versioned_block: VersionedBlock, principal: &Principal, key: &Key
|
|
|
) -> Result<VersionedBlock> {
|
|
|
let VersionedBlock::V0(mut block) = versioned_block;
|
|
|
- let mut body = match block.body {
|
|
|
+ let body = match block.body {
|
|
|
Cryptotext::Plain(_) => return Ok(VersionedBlock::V0(block)),
|
|
|
Cryptotext::Cipher(body) => body
|
|
|
};
|
|
|
let (principal_owned, read_cap) = block.read_caps.remove_entry(principal)
|
|
|
.ok_or(Error::NoReadCap)?;
|
|
|
let block_key = decrypt(read_cap, key)?;
|
|
|
- decrypt_in_place(&mut body, &block_key)?;
|
|
|
- block.body = Cryptotext::Plain(body);
|
|
|
+ let new_body = decrypt_slice(&body, &block_key)?;
|
|
|
+ block.body = Cryptotext::Plain(new_body);
|
|
|
block.read_caps.insert(principal_owned, Cryptotext::Plain(block_key));
|
|
|
Ok(VersionedBlock::V0(block))
|
|
|
}
|
|
|
|
|
|
-fn encrypt<T: AsRef<[u8]> + AsMut<[u8]>>(mut data: T, key: &Key) -> Result<Cryptotext<T>> {
|
|
|
- encrypt_in_place(data.as_mut(), key)?;
|
|
|
- Ok(Cryptotext::Cipher(data))
|
|
|
+fn encrypt<T: Serialize>(value: &T, key: &Key) -> Result<Cryptotext<T>> {
|
|
|
+ let data = to_vec(value).map_err(|err| Error::Encryption(err.to_string()))?;
|
|
|
+ let vec = encrypt_slice(data.as_slice(), key);
|
|
|
+ Ok(Cryptotext::Cipher(vec?))
|
|
|
}
|
|
|
|
|
|
-fn decrypt<T: AsRef<[u8]> + AsMut<[u8]>>(cryptotext: Cryptotext<T>, key: &Key) -> Result<T> {
|
|
|
- let mut data = match cryptotext {
|
|
|
- Cryptotext::Plain(data) => return Ok(data),
|
|
|
+fn decrypt<T: Serialize + DeserializeOwned>(cryptotext: Cryptotext<T>, key: &Key) -> Result<T> {
|
|
|
+ let data = match cryptotext {
|
|
|
+ Cryptotext::Plain(value) => return Ok(value),
|
|
|
Cryptotext::Cipher(data) => data
|
|
|
};
|
|
|
- decrypt_in_place(data.as_mut(), key)?;
|
|
|
- Ok(data)
|
|
|
+ let vec = decrypt_slice(data.as_ref(), key);
|
|
|
+ let value = from_vec(&vec?).map_err(|err| Error::Decryption(err.to_string()));
|
|
|
+ value
|
|
|
}
|
|
|
|
|
|
-fn encrypt_in_place(_plaintext: &mut [u8], _key: &Key) -> Result<()> {
|
|
|
- unimplemented!();
|
|
|
+fn encrypt_slice(plaintext: &[u8], key: &Key) -> Result<Vec<u8>> {
|
|
|
+ let cipher = key.cipher();
|
|
|
+ let key_slice = key.key_slice();
|
|
|
+ let iv_slice = key.iv_slice();
|
|
|
+ openssl_encrypt(cipher, key_slice, iv_slice, plaintext)
|
|
|
+ .map_err(|err| Error::Encryption(err.to_string()))
|
|
|
}
|
|
|
|
|
|
-fn decrypt_in_place(_ciphertext: &mut [u8], _key: &Key) -> Result<()> {
|
|
|
- unimplemented!();
|
|
|
+fn decrypt_slice(ciphertext: &[u8], key: &Key) -> Result<Vec<u8>> {
|
|
|
+ let cipher = key.cipher();
|
|
|
+ let key_slice = key.key_slice();
|
|
|
+ let iv_slice = key.iv_slice();
|
|
|
+ openssl_decrypt(cipher, key_slice, iv_slice, ciphertext)
|
|
|
+ .map_err(|err| Error::Decryption(err.to_string()))
|
|
|
}
|
|
|
|
|
|
-/// Tests that validate the dependencies of this module.
|
|
|
#[cfg(test)]
|
|
|
-mod dependency_tests {
|
|
|
+mod tests {
|
|
|
use super::*;
|
|
|
|
|
|
- static KEY: [u8; 32] = [
|
|
|
+ const KEY: [u8; 32] = [
|
|
|
0xB2, 0xB3, 0xDA, 0x5A, 0x1A, 0xF6, 0xB3, 0x78, 0x30, 0xAB, 0x1D, 0x33, 0x33, 0xE7, 0xE3,
|
|
|
0x5B, 0xBB, 0xF9, 0xFE, 0xD0, 0xC1, 0xF7, 0x90, 0x34, 0x69, 0xB7, 0xE7, 0xC6, 0x1C, 0x46,
|
|
|
0x85, 0x48,
|
|
|
];
|
|
|
|
|
|
- static IV: [u8; 16] = [
|
|
|
+ const IV: [u8; 16] = [
|
|
|
0x60, 0xE0, 0xBE, 0x45, 0xA9, 0xAB, 0x5D, 0x99, 0x3B, 0x3A, 0x1E, 0x54, 0x18, 0xE0, 0x46,
|
|
|
0xDE,
|
|
|
];
|
|
|
|
|
|
- /// This test validates that data decrypted with the `Crypter` API matches data that was
|
|
|
- /// previously encrypted using it.
|
|
|
#[test]
|
|
|
- fn cryptor() {
|
|
|
- let expected = b"We attack at the crack of noon!";
|
|
|
-
|
|
|
- let cipher = Cipher::aes_256_cbc();
|
|
|
- let mut encrypter = Crypter::new(
|
|
|
- cipher,
|
|
|
- Mode::Encrypt,
|
|
|
- &KEY,
|
|
|
- Some(&IV)).unwrap();
|
|
|
- let mut ciphertext = vec![0; expected.len() + cipher.block_size()];
|
|
|
- let mut written = encrypter.update(expected.as_slice(), &mut ciphertext).unwrap();
|
|
|
- written += encrypter.finalize(&mut ciphertext[written..]).unwrap();
|
|
|
- ciphertext.truncate(written);
|
|
|
-
|
|
|
- let mut actual = vec![0u8; ciphertext.len() + cipher.block_size()];
|
|
|
- let mut decrypter = Crypter::new(
|
|
|
- cipher,
|
|
|
- Mode::Decrypt,
|
|
|
- &KEY,
|
|
|
- Some(&IV)).unwrap();
|
|
|
- let mut written = decrypter.update(&ciphertext, &mut actual).unwrap();
|
|
|
- written += decrypter.finalize(&mut actual[written..]).unwrap();
|
|
|
- actual.truncate(written);
|
|
|
-
|
|
|
- assert_eq!(expected, actual.as_slice());
|
|
|
+ fn key_aes256cbc_key_part_is_correct() {
|
|
|
+ let mut array = [0u8; KEY.len() + IV.len()];
|
|
|
+ Key::pack(&mut array, &KEY, &IV);
|
|
|
+ let key = Key::Aes256Cbc(array);
|
|
|
+ let key_part = key.key_slice();
|
|
|
+ assert_eq!(KEY, key_part);
|
|
|
}
|
|
|
-}
|
|
|
|
|
|
-/// Tests for the code that is actually in this module.
|
|
|
-#[cfg(test)]
|
|
|
-mod tests {
|
|
|
+ #[test]
|
|
|
+ fn key_aes256cbc_iv_part_is_correct() {
|
|
|
+ let mut array = [0u8; KEY.len() + IV.len()];
|
|
|
+ Key::pack(&mut array, &KEY, &IV);
|
|
|
+ let key = Key::Aes256Cbc(array);
|
|
|
+ let iv_part = key.iv_slice().unwrap();
|
|
|
+ assert_eq!(IV, iv_part);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Tests that validate the dependencies of this module.
|
|
|
+ mod dependency_tests {
|
|
|
+ /// This test validates that data decrypted with the `Crypter` API matches data that was
|
|
|
+ /// previously encrypted using it.
|
|
|
+ #[test]
|
|
|
+ fn crypter() {
|
|
|
+ use super::*;
|
|
|
+ let expected = b"We attack at the crack of noon!";
|
|
|
+ let cipher = Cipher::aes_256_cbc();
|
|
|
|
|
|
-}
|
|
|
+ let ciphertext = openssl_encrypt(cipher, &KEY, Some(&IV), expected).unwrap();
|
|
|
+ let actual = openssl_decrypt(cipher, &KEY, Some(&IV), ciphertext.as_slice()).unwrap();
|
|
|
+
|
|
|
+ assert_eq!(expected, actual.as_slice());
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|