|
@@ -2,7 +2,10 @@
|
|
|
|
|
|
use super::*;
|
|
|
use openssl::{
|
|
|
- symm::{Cipher, encrypt as openssl_encrypt, decrypt as openssl_decrypt}
|
|
|
+ error::ErrorStack,
|
|
|
+ encrypt::{Encrypter, Decrypter},
|
|
|
+ pkey::{PKey, Public, Private},
|
|
|
+ symm::{Cipher, encrypt as openssl_encrypt, decrypt as openssl_decrypt},
|
|
|
};
|
|
|
use serde_block_tree::{to_vec, from_vec};
|
|
|
use serde::de::{DeserializeOwned};
|
|
@@ -24,6 +27,8 @@ pub trait Hydrate<const N: usize> {
|
|
|
#[derive(Debug)]
|
|
|
pub enum Error {
|
|
|
NoReadCap,
|
|
|
+ NoKeyAvailable,
|
|
|
+ MissingPrivateKey,
|
|
|
Encryption(String),
|
|
|
Decryption(String),
|
|
|
}
|
|
@@ -57,84 +62,147 @@ pub enum Key {
|
|
|
/// 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]),
|
|
|
+ Aes256Cbc {
|
|
|
+ key: [u8; 32],
|
|
|
+ iv: [u8; 16],
|
|
|
+ },
|
|
|
/// A key for the AES 256 cipher in counter mode.
|
|
|
Aes256Ctr([u8; 32]),
|
|
|
+ Ed25519 {
|
|
|
+ public: [u8; 32],
|
|
|
+ private: Option<[u8; 32]>
|
|
|
+ },
|
|
|
}
|
|
|
|
|
|
-impl Key {
|
|
|
- /// 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,
|
|
|
- }
|
|
|
- }
|
|
|
+enum EncryptionAlgo<'a> {
|
|
|
+ Symmetric {
|
|
|
+ cipher: Cipher,
|
|
|
+ key: &'a [u8],
|
|
|
+ iv: Option<&'a [u8]>
|
|
|
+ },
|
|
|
+ Asymmetric(PKey<Public>),
|
|
|
+}
|
|
|
|
|
|
- /// 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,
|
|
|
- }
|
|
|
- }
|
|
|
+enum DecryptionAlgo<'a> {
|
|
|
+ Symmetric {
|
|
|
+ cipher: Cipher,
|
|
|
+ key: &'a [u8],
|
|
|
+ iv: Option<&'a [u8]>
|
|
|
+ },
|
|
|
+ Asymmetric(PKey<Private>),
|
|
|
+}
|
|
|
|
|
|
- /// 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
|
|
|
+impl From<ErrorStack> for Error {
|
|
|
+ fn from(error: ErrorStack) -> Error {
|
|
|
+ Error::Encryption(error.to_string())
|
|
|
}
|
|
|
+}
|
|
|
|
|
|
- /// Returns the key part of this `Key`.
|
|
|
- pub fn key_slice(&self) -> &[u8] {
|
|
|
- let key_len = self.key_len();
|
|
|
- &self.as_slice()[..key_len]
|
|
|
+impl Key {
|
|
|
+ /// Returns the key part of this `Key`. Note that if this `Key` is for an asymmetric algorithm,
|
|
|
+ /// then this returns the *private* key (if it is present).
|
|
|
+ fn key_slice(&self) -> Option<&[u8]> {
|
|
|
+ match self {
|
|
|
+ Key::Aes256Cbc { key, .. } => Some(key),
|
|
|
+ Key::Aes256Ctr(key) => Some(key),
|
|
|
+ Key::Ed25519 { private, .. } => match private {
|
|
|
+ Some(key) => Some(key.as_slice()),
|
|
|
+ None => None,
|
|
|
+ },
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/// 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..])
|
|
|
+ fn iv_slice(&self) -> Option<&[u8]> {
|
|
|
+ match self {
|
|
|
+ Key::Aes256Cbc { iv, .. } => Some(iv),
|
|
|
+ _ => None,
|
|
|
}
|
|
|
}
|
|
|
+}
|
|
|
|
|
|
- /// 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);
|
|
|
+impl<'a> TryFrom<&'a Key> for EncryptionAlgo<'a> {
|
|
|
+ type Error = Error;
|
|
|
+ fn try_from(key: &'a Key) -> Result<EncryptionAlgo<'a>> {
|
|
|
+ match key {
|
|
|
+ Key::Aes256Cbc { key: key_slice, iv } => Ok(EncryptionAlgo::Symmetric {
|
|
|
+ cipher: Cipher::aes_256_cbc(),
|
|
|
+ key: key_slice,
|
|
|
+ iv: Some(iv),
|
|
|
+ }),
|
|
|
+ Key::Aes256Ctr(key_slice) => Ok(EncryptionAlgo::Symmetric {
|
|
|
+ cipher: Cipher::aes_256_ctr(),
|
|
|
+ key: key_slice,
|
|
|
+ iv: None
|
|
|
+ }),
|
|
|
+ Key::Ed25519 { public, .. } => {
|
|
|
+ let pkey = PKey::public_key_from_der(public.as_slice())
|
|
|
+ .map_err(|err| Error::Encryption(err.to_string()));
|
|
|
+ Ok(EncryptionAlgo::Asymmetric(pkey?))
|
|
|
+ },
|
|
|
+ }
|
|
|
}
|
|
|
+}
|
|
|
|
|
|
- /// Returns the cipher that this key is for.
|
|
|
- fn cipher(&self) -> Cipher {
|
|
|
+impl<'a> EncryptionAlgo<'a> {
|
|
|
+ fn encrypt(&self, slice: &[u8]) -> Result<Vec<u8>> {
|
|
|
match self {
|
|
|
- Key::Aes256Cbc(_) => Cipher::aes_256_cbc(),
|
|
|
- Key::Aes256Ctr(_) => Cipher::aes_256_ctr(),
|
|
|
+ EncryptionAlgo::Symmetric { cipher, key, iv } => {
|
|
|
+ openssl_encrypt(*cipher, key, *iv, slice).map_err(Error::from)
|
|
|
+ },
|
|
|
+ EncryptionAlgo::Asymmetric(pkey) => {
|
|
|
+ let encrypter = Encrypter::new(pkey).map_err(Error::from)?;
|
|
|
+ let buffer_len = encrypter.encrypt_len(slice).map_err(Error::from)?;
|
|
|
+ let mut ciphertext = vec![0; buffer_len];
|
|
|
+ let ciphertext_len = encrypter.encrypt(slice, &mut ciphertext)
|
|
|
+ .map_err(Error::from)?;
|
|
|
+ ciphertext.truncate(ciphertext_len);
|
|
|
+ Ok(ciphertext)
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-impl AsRef<[u8]> for Key {
|
|
|
- fn as_ref(&self) -> &[u8] {
|
|
|
- self.as_slice()
|
|
|
+impl<'a> TryFrom<&'a Key> for DecryptionAlgo<'a> {
|
|
|
+ type Error = Error;
|
|
|
+ fn try_from(key: &'a Key) -> Result<DecryptionAlgo<'a>> {
|
|
|
+ match key {
|
|
|
+ Key::Aes256Cbc { key: key_slice, iv } => Ok(DecryptionAlgo::Symmetric {
|
|
|
+ cipher: Cipher::aes_256_cbc(),
|
|
|
+ key: key_slice,
|
|
|
+ iv: Some(iv),
|
|
|
+ }),
|
|
|
+ Key::Aes256Ctr(key_slice) => Ok(DecryptionAlgo::Symmetric {
|
|
|
+ cipher: Cipher::aes_256_ctr(),
|
|
|
+ key: key_slice,
|
|
|
+ iv: None
|
|
|
+ }),
|
|
|
+ Key::Ed25519 { private, .. } => {
|
|
|
+ let private = private.ok_or(Error::MissingPrivateKey)?;
|
|
|
+ let pkey = PKey::private_key_from_der(private.as_slice())
|
|
|
+ .map_err(|err| Error::Decryption(err.to_string()));
|
|
|
+ Ok(DecryptionAlgo::Asymmetric(pkey?))
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-impl AsMut<[u8]> for Key {
|
|
|
- fn as_mut(&mut self) -> &mut [u8] {
|
|
|
- self.as_mut_slice()
|
|
|
+impl<'a> DecryptionAlgo<'a> {
|
|
|
+ fn decrypt(&self, slice: &[u8]) -> Result<Vec<u8>> {
|
|
|
+ match self {
|
|
|
+ DecryptionAlgo::Symmetric { cipher, key, iv } => {
|
|
|
+ openssl_decrypt(*cipher, key, *iv, slice).map_err(Error::from)
|
|
|
+ },
|
|
|
+ DecryptionAlgo::Asymmetric(pkey) => {
|
|
|
+ let decrypter = Decrypter::new(pkey).map_err(Error::from)?;
|
|
|
+ let buffer_len = decrypter.decrypt_len(slice).map_err(Error::from)?;
|
|
|
+ let mut plaintext = vec![0; buffer_len];
|
|
|
+ let plaintext_len = decrypter.decrypt(slice, &mut plaintext)
|
|
|
+ .map_err(Error::from)?;
|
|
|
+ plaintext.truncate(plaintext_len);
|
|
|
+ Ok(plaintext)
|
|
|
+ },
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -176,7 +244,7 @@ pub(crate) fn decrypt_block(
|
|
|
|
|
|
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);
|
|
|
+ let vec = encrypt_slice(&data, key);
|
|
|
Ok(Cryptotext::Cipher(vec?))
|
|
|
}
|
|
|
|
|
@@ -185,25 +253,18 @@ fn decrypt<T: Serialize + DeserializeOwned>(cryptotext: Cryptotext<T>, key: &Key
|
|
|
Cryptotext::Plain(value) => return Ok(value),
|
|
|
Cryptotext::Cipher(data) => data
|
|
|
};
|
|
|
- let vec = decrypt_slice(data.as_ref(), key);
|
|
|
- let value = from_vec(&vec?).map_err(|err| Error::Decryption(err.to_string()));
|
|
|
- value
|
|
|
+ let vec = decrypt_slice(&data, key);
|
|
|
+ from_vec(&vec?).map_err(|err| Error::Decryption(err.to_string()))
|
|
|
}
|
|
|
|
|
|
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()))
|
|
|
+ let algo = EncryptionAlgo::try_from(key)?;
|
|
|
+ algo.encrypt(plaintext)
|
|
|
}
|
|
|
|
|
|
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()))
|
|
|
+ let algo = DecryptionAlgo::try_from(key)?;
|
|
|
+ algo.decrypt(ciphertext)
|
|
|
}
|
|
|
|
|
|
#[cfg(test)]
|
|
@@ -222,19 +283,16 @@ mod tests {
|
|
|
];
|
|
|
|
|
|
#[test]
|
|
|
- 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);
|
|
|
+ fn key_aes256cbc_key_slice_is_correct() -> Result<()> {
|
|
|
+ let key = Key::Aes256Cbc { key: KEY, iv: IV };
|
|
|
+ let key_part = key.key_slice().ok_or(Error::NoKeyAvailable);
|
|
|
+ assert_eq!(KEY, key_part?);
|
|
|
+ Ok(())
|
|
|
}
|
|
|
|
|
|
#[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);
|
|
|
+ fn key_aes256cbc_iv_slice_is_correct() {
|
|
|
+ let key = Key::Aes256Cbc { key: KEY, iv: IV };
|
|
|
let iv_part = key.iv_slice().unwrap();
|
|
|
assert_eq!(IV, iv_part);
|
|
|
}
|