/// Functions for performing cryptographic operations on the main data structures. use super::*; use openssl::{ error::ErrorStack, encrypt::{Encrypter, Decrypter}, pkey::{PKey, Public, Private}, symm::{Cipher, encrypt as openssl_encrypt, decrypt as openssl_decrypt}, rand::rand_bytes, rsa::{Rsa, Padding as OpensslPadding}, hash::{hash, MessageDigest}, sign::{Signer, Verifier} }; use serde_block_tree::{self, to_vec, from_vec, write_to}; use serde::de::{DeserializeOwned}; use std::str::FromStr; use strum_macros::{EnumString, EnumDiscriminants, Display}; /// Data that may or may not be encrypted. #[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] pub enum Cryptotext { /// The inner value of `T` is plaintext. Plain(T), /// The inner value is ciphertext. Cipher(Vec), } /// Errors that can occur during cryptographic operations. #[derive(Debug)] pub enum Error { NoReadCap, NoKeyAvailable, MissingPrivateKey, KeyVariantUnsupported, BlockNotEncrypted, InvalidFormat, Message(String), Serde(serde_block_tree::Error), } impl Display for Error { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { Error::NoReadCap => write!(f, "no readcap"), Error::NoKeyAvailable => write!(f, "no key available"), Error::MissingPrivateKey => write!(f, "private key was missing"), Error::KeyVariantUnsupported => write!(f, "unsupported key variant"), Error::BlockNotEncrypted => write!(f, "block was not encrypted"), Error::InvalidFormat => write!(f, "invalid format"), Error::Message(message) => f.write_str(message.as_str()), Error::Serde(serde_err) => serde_err.fmt(f), } } } impl From for Error { fn from(error: ErrorStack) -> Error { Error::Message(error.to_string()) } } impl From for Error { fn from(error: serde_block_tree::Error) -> Error { Error::Serde(error) } } pub type Result = std::result::Result; /// A cryptographic hash. #[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Hashable, Clone, EnumDiscriminants)] #[strum_discriminants(derive(EnumString, Display))] pub enum Hash { Sha2_256([u8; 32]), #[serde(with = "BigArray")] Sha2_512([u8; 64]), } impl Hash { /// The character that's used to separate a hash type from its value in its string /// representation. const HASH_SEP: char = ':'; } impl From for Hash { fn from(discriminant: HashDiscriminants) -> Hash { match discriminant { HashDiscriminants::Sha2_512 => Hash::Sha2_512([0u8; 64]), HashDiscriminants::Sha2_256 => Hash::Sha2_256([0u8; 32]) } } } impl AsRef<[u8]> for Hash { fn as_ref(&self) -> &[u8] { match self { Hash::Sha2_256(arr) => arr, Hash::Sha2_512(arr) => arr } } } impl AsMut<[u8]> for Hash { fn as_mut(&mut self) -> &mut [u8] { match self { Hash::Sha2_256(arr) => arr, Hash::Sha2_512(arr) => arr } } } impl TryFrom<&str> for Hash { type Error = Error; fn try_from(string: &str) -> Result { let mut split: Vec<&str> = string.split(Self::HASH_SEP).collect(); if split.len() != 2 { return Err(Error::InvalidFormat) }; let second = split.pop().ok_or(Error::InvalidFormat)?; let first = split.pop().ok_or(Error::InvalidFormat)?; let mut hash = Hash::from(HashDiscriminants::from_str(first) .map_err(|_| Error::InvalidFormat)?); base64_url::decode_to_slice(second, hash.as_mut()) .map_err(|_| Error::InvalidFormat)?; Ok(hash) } } impl Display for Hash { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let hash_type: HashDiscriminants = self.into(); let hash_data = base64_url::encode(self.as_ref()); write!(f, "{}{}{}", hash_type, Hash::HASH_SEP, hash_data) } } const RSA_SIG_LEN: usize = 512; /// A cryptographic signature. #[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] pub enum Signature { #[serde(with = "BigArray")] Rsa([u8; RSA_SIG_LEN]), } pub enum SignatureId { Rsa } impl Signature { fn new(id: SignatureId) -> Signature { match id { SignatureId::Rsa => Signature::Rsa([0; RSA_SIG_LEN]) } } fn as_slice(&self) -> &[u8] { match self { Signature::Rsa(buf) => buf.as_slice() } } fn as_mut_slice(&mut self) -> &mut [u8] { match self { Signature::Rsa(buf) => buf.as_mut_slice() } } } impl Default for Signature { fn default() -> Self { Signature::Rsa([0; RSA_SIG_LEN]) } } /// Identifies a type of cryptographic key. The variants of this enum match those of `Key`. pub enum KeyId { // TODO: Write a macro to generate this from `Key`. Aes256Cbc, Aes256Ctr, Rsa, } #[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] pub enum RsaPadding { None, Pkcs1, Pkcs1Oaep, Pkcs1Pss, } impl Copy for RsaPadding {} impl From for OpensslPadding { fn from(padding: RsaPadding) -> OpensslPadding { match padding { RsaPadding::None => OpensslPadding::NONE, RsaPadding::Pkcs1 => OpensslPadding::PKCS1, RsaPadding::Pkcs1Oaep => OpensslPadding::PKCS1_OAEP, RsaPadding::Pkcs1Pss => OpensslPadding::PKCS1_PSS, } } } /// A cryptographic key. #[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] 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. Aes256Cbc { key: [u8; 32], iv: [u8; 16], }, /// A key for the AES 256 cipher in counter mode. Aes256Ctr { key: [u8; 32], iv: [u8; 16] }, Rsa { public: Vec, private: Option>, padding: RsaPadding, }, } 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::Rsa { private, .. } => match private { Some(key) => Some(key.as_slice()), None => None, }, } } /// Returns the IV part of this`Key`. fn iv_slice(&self) -> Option<&[u8]> { match self { Key::Aes256Cbc { iv, .. } => Some(iv), _ => None, } } pub fn generate(id: KeyId) -> Result { match id { KeyId::Aes256Cbc => { let mut key = [0; 32]; let mut iv = [0; 16]; rand_bytes(&mut key).map_err(Error::from)?; rand_bytes(&mut iv).map_err(Error::from)?; Ok(Key::Aes256Cbc { key, iv }) }, KeyId::Aes256Ctr => { let mut key = [0; 32]; let mut iv = [0; 16]; rand_bytes(&mut key).map_err(Error::from)?; rand_bytes(&mut iv).map_err(Error::from)?; Ok(Key::Aes256Ctr { key, iv }) }, KeyId::Rsa => { let key = PKey::from_rsa(Rsa::generate(4096)?)?; let public= key.public_key_to_der().map_err(Error::from)?; let private = Some(key.private_key_to_der().map_err(Error::from)?); Ok(Key::Rsa { public, private, padding: RsaPadding::Pkcs1 }) }, } } /// Hashes this key and returns the `Principal` it is for. pub fn to_principal(&self) -> Principal { let slice = match self { Key::Rsa { public, .. } => public.as_slice(), Key::Aes256Cbc { key, .. } => key, Key::Aes256Ctr { key, .. } => key, }; let mut buf = [0; 32]; let bytes = hash(MessageDigest::sha256(), slice).unwrap(); buf.copy_from_slice(&*bytes); Principal(Hash::Sha2_256(buf)) } } enum EncryptionAlgo<'a> { Symmetric { cipher: Cipher, key: &'a [u8], iv: Option<&'a [u8]> }, Asymmetric { key: PKey, rsa_padding: Option, } } impl<'a> EncryptionAlgo<'a> { fn encrypt(&self, slice: &[u8]) -> Result> { match self { EncryptionAlgo::Symmetric { cipher, key, iv } => { openssl_encrypt(*cipher, key, *iv, slice).map_err(Error::from) }, EncryptionAlgo::Asymmetric { key, rsa_padding } => { let mut encrypter = Encrypter::new(key).map_err(Error::from)?; if let Some(padding) = rsa_padding { encrypter.set_rsa_padding(*padding).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<'a> TryFrom<&'a Key> for EncryptionAlgo<'a> { type Error = Error; fn try_from(key: &'a Key) -> Result> { 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: key_slice, iv } => Ok(EncryptionAlgo::Symmetric { cipher: Cipher::aes_256_ctr(), key: key_slice, iv: Some(iv), }), Key::Rsa { public, padding, .. } => { let pkey = PKey::public_key_from_der(public.as_slice()) .map_err(|err| Error::Message(err.to_string())); Ok(EncryptionAlgo::Asymmetric { key: pkey?, rsa_padding: Some((*padding).into()) }) }, } } } enum DecryptionAlgo<'a> { Symmetric { cipher: Cipher, key: &'a [u8], iv: Option<&'a [u8]> }, Asymmetric(PKey), } impl<'a> DecryptionAlgo<'a> { fn decrypt(&self, slice: &[u8]) -> Result> { 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) }, } } } impl<'a> TryFrom<&'a Key> for DecryptionAlgo<'a> { type Error = Error; fn try_from(key: &'a Key) -> Result> { 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: key_slice, iv } => Ok(DecryptionAlgo::Symmetric { cipher: Cipher::aes_256_ctr(), key: key_slice, iv: Some(iv), }), Key::Rsa { private, .. } => { let private = private.as_ref().ok_or(Error::MissingPrivateKey)?; let pkey = PKey::private_key_from_der(private.as_slice()).map_err(Error::from); Ok(DecryptionAlgo::Asymmetric(pkey?)) } } } } struct SignAlgo { key: PKey, digest: MessageDigest, signature: Signature } impl SignAlgo { fn sign<'a, I: Iterator>(&mut self, parts: I) -> Result<()> { let mut signer = Signer::new(self.digest, &self.key).map_err(Error::from)?; for part in parts { signer.update(part).map_err(Error::from)?; } let buf = self.signature.as_mut_slice(); signer.sign(buf).map_err(Error::from)?; Ok(()) } } impl TryFrom<&Key> for SignAlgo { type Error = Error; fn try_from(key: &Key) -> Result { match key { Key::Rsa { private: Some(private), .. } => { let rsa = Rsa::private_key_from_der(private.as_slice()).map_err(Error::from)?; Ok(SignAlgo { key: PKey::from_rsa(rsa).map_err(Error::from)?, digest: MessageDigest::sha256(), signature: Signature::new(SignatureId::Rsa) }) }, _ => Err(Error::KeyVariantUnsupported) } } } struct VerifyAlgo { key: PKey, digest: MessageDigest, } impl VerifyAlgo { fn verify<'a, I: Iterator>( &'a self, parts: I, signature: &[u8] ) -> Result { let mut verifier = Verifier::new(self.digest, &self.key).map_err(Error::from)?; for part in parts { verifier.update(part).map_err(Error::from)?; } verifier.verify(signature).map_err(Error::from) } } impl TryFrom<&Key> for VerifyAlgo { type Error = Error; fn try_from(key: &Key) -> Result { match key { Key::Rsa { public, .. } => { let rsa = Rsa::public_key_from_der(public.as_slice()).map_err(Error::from)?; Ok(VerifyAlgo { key: PKey::from_rsa(rsa).map_err(Error::from)?, digest: MessageDigest::sha256() }) }, _ => Err(Error::KeyVariantUnsupported) } } } pub(crate) fn encrypt_block( versioned_block: VersionedBlock, principal: &Principal, key: &Key ) -> Result { let VersionedBlock::V0(mut block) = versioned_block; let body = match block.body { Cryptotext::Cipher(_) => return Ok(VersionedBlock::V0(block)), Cryptotext::Plain(body) => body }; let (principal_owned, read_cap) = block.readcaps.remove_entry(principal) .ok_or(Error::NoReadCap)?; let block_key = decrypt(read_cap, key)?; let new_body = encrypt_slice(&body, &block_key)?; block.body = Cryptotext::Cipher(new_body); block.readcaps.insert(principal_owned, encrypt(&block_key, key)?); Ok(VersionedBlock::V0(block)) } pub(crate) fn decrypt_block( versioned_block: VersionedBlock, principal: &Principal, key: &Key ) -> Result { let VersionedBlock::V0(mut block) = versioned_block; let body = match block.body { Cryptotext::Plain(_) => return Ok(VersionedBlock::V0(block)), Cryptotext::Cipher(body) => body }; let (principal_owned, read_cap) = block.readcaps.remove_entry(principal) .ok_or(Error::NoReadCap)?; let block_key = decrypt(read_cap, key)?; let new_body = decrypt_slice(&body, &block_key)?; block.body = Cryptotext::Plain(new_body); block.readcaps.insert(principal_owned, Cryptotext::Plain(block_key)); Ok(VersionedBlock::V0(block)) } fn encrypt(value: &T, key: &Key) -> Result> { let data = to_vec(value).map_err(Error::from)?; let vec = encrypt_slice(&data, key); Ok(Cryptotext::Cipher(vec?)) } fn decrypt(cryptotext: Cryptotext, key: &Key) -> Result { let data = match cryptotext { Cryptotext::Plain(value) => return Ok(value), Cryptotext::Cipher(data) => data }; let vec = decrypt_slice(&data, key); from_vec(&vec?).map_err(Error::from) } fn encrypt_slice(plaintext: &[u8], key: &Key) -> Result> { let algo = EncryptionAlgo::try_from(key)?; algo.encrypt(plaintext) } fn decrypt_slice(ciphertext: &[u8], key: &Key) -> Result> { let algo = DecryptionAlgo::try_from(key)?; algo.decrypt(ciphertext) } #[derive(Serialize)] struct SigHeader<'a> { path: &'a Path, readcaps: &'a HashMap>, writecap: &'a Writecap, } impl<'a> From<&'a Block> for SigHeader<'a> { fn from(block: &'a Block) -> SigHeader<'a> { SigHeader { path: &block.path, readcaps: &block.readcaps, writecap: &block.writecap, } } } fn get_body(block: &Block) -> Result<&[u8]> { let body = match &block.body { Cryptotext::Cipher(body) => body, Cryptotext::Plain(_) => { return Err(Error::BlockNotEncrypted); } }; Ok(body) } pub(crate) fn sign_block(versioned_block: &mut VersionedBlock, writecap: Writecap) -> Result<()> { let VersionedBlock::V0(block) = versioned_block; block.writecap = writecap; let body = get_body(block)?; let sig_header = SigHeader::from(&*block); let header = to_vec(&sig_header)?; let mut sign_algo = SignAlgo::try_from(&block.writecap.signing_key)?; sign_algo.sign([header.as_slice(), body].into_iter())?; block.signature = sign_algo.signature; Ok(()) } pub(crate) fn verify_block(versioned_block: &VersionedBlock) -> Result { let VersionedBlock::V0(block) = versioned_block; let body = get_body(block)?; let sig_header = SigHeader::from(&*block); let header = to_vec(&sig_header)?; let verify_algo = VerifyAlgo::try_from(&block.writecap.signing_key)?; let parts = [header.as_slice(), body].into_iter(); if !verify_algo.verify(parts, block.signature.as_slice())? { return Ok(false); } Ok(true) } #[derive(Serialize)] struct WritecapSig<'a> { issued_to: &'a Principal, path: &'a Path, expires: &'a Epoch, signing_key: &'a Key, } impl<'a> From<&'a Writecap> for WritecapSig<'a> { fn from(writecap: &'a Writecap) -> WritecapSig<'a> { WritecapSig { issued_to: &writecap.issued_to, path: &writecap.path, expires: &writecap.expires, signing_key: &writecap.signing_key, } } } pub(crate) fn sign_writecap(writecap: &mut Writecap, key: &Key) -> Result<()> { let mut sign_algo = SignAlgo::try_from(key)?; let sig_input = to_vec(&WritecapSig::from(&*writecap))?; sign_algo.sign([sig_input.as_slice()].into_iter())?; writecap.signature = sign_algo.signature; Ok(()) } /// The types of errors which can occur when verifying a writecap chain is authorized to write to /// a given path. #[derive(Debug, PartialEq)] pub(crate) enum WritecapAuthzErr { /// The chain is not valid for use on the given path. UnauthorizedPath, /// At least one writecap in the chain is expired. Expired, /// The given writecaps do not actually form a chain. NotChained, /// There is an invalid signature in the chain. InvalidSignature, /// The principal the root writecap was issued to does not own the given path. RootDoesNotOwnPath, /// An error occured while serializing a writecap. Serde(String), /// A cryptographic error occurred while attempting to verify a writecap. Crypto(String), } /// Verifies that the given `Writecap` actually grants permission to write to the given `Path`. pub(crate) fn verify_writecap( mut writecap: &Writecap, path: &Path ) -> std::result::Result<(), WritecapAuthzErr> { let mut prev: Option<&Writecap> = None; let mut sig_input = Vec::new(); let now = Epoch::now(); loop { if !writecap.path.contains(path) { return Err(WritecapAuthzErr::UnauthorizedPath); } if writecap.expires <= now { return Err(WritecapAuthzErr::Expired); } if let Some(prev) = &prev { if prev.signing_key.to_principal() != writecap.issued_to { return Err(WritecapAuthzErr::NotChained); } } let sig = WritecapSig::from(writecap); sig_input.clear(); write_to(&sig, &mut sig_input).map_err(|e| WritecapAuthzErr::Serde(e.to_string()))?; let verify_algo = VerifyAlgo::try_from(&writecap.signing_key) .map_err(|e| WritecapAuthzErr::Crypto(e.to_string()))?; let valid = verify_algo .verify([sig_input.as_slice()].into_iter(), writecap.signature.as_slice()) .map_err(|e| WritecapAuthzErr::Crypto(e.to_string()))?; if !valid { return Err(WritecapAuthzErr::InvalidSignature); } match &writecap.next { Some(next) => { prev = Some(writecap); writecap = next; }, None => { // We're at the root key. As long as the signer of this writecap is the owner of // the path, then the writecap is valid. if writecap.signing_key.to_principal() == path.owner { return Ok(()); } else { return Err(WritecapAuthzErr::RootDoesNotOwnPath) } } } } } #[cfg(test)] mod tests { use super::*; use test_helpers::*; #[test] 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_slice_is_correct() { let key = Key::Aes256Cbc { key: KEY, iv: IV }; let iv_part = key.iv_slice().unwrap(); assert_eq!(IV, iv_part); } fn encrypt_decrypt_block_test_case( mut actual: VersionedBlock, principal: &Principal, key: &Key ) -> Result<()> { let expected = actual.clone(); actual = encrypt_block(actual, principal, key)?; actual = decrypt_block(actual, principal, key)?; assert_eq!(expected, actual); Ok(()) } fn make_versioned_block(principal: Principal, block_key: Key) -> Result { let mut block = make_block()?; block.readcaps.clear(); block.readcaps.insert(principal, Cryptotext::Plain(block_key)); Ok(VersionedBlock::V0(block)) } #[test] fn encrypt_decrypt_block_aes256cbc() -> Result<()> { let principal = make_principal(); let block_key = Key::Aes256Cbc { key: BLOCK_KEY, iv: BLOCK_IV }; let block = make_versioned_block(principal.clone(), block_key)?; let key = Key::Aes256Cbc { key: KEY, iv: IV }; encrypt_decrypt_block_test_case(block, &principal, &key) } #[test] fn encrypt_decrypt_block_aes256ctr() -> Result<()> { let principal = make_principal(); let block_key = Key::Aes256Ctr { key: BLOCK_KEY, iv: BLOCK_IV }; let block = make_versioned_block(principal.clone(), block_key)?; let key = Key::Aes256Ctr { key: KEY, iv: IV }; encrypt_decrypt_block_test_case(block, &principal, &key) } #[test] fn encrypt_decrypt_block_rsa() -> Result<()> { let principal = make_principal(); let block_key = Key::Aes256Ctr { key: BLOCK_KEY, iv: BLOCK_IV }; let block = make_versioned_block(principal.clone(), block_key)?; let key = Key::generate(KeyId::Rsa)?; encrypt_decrypt_block_test_case(block, &principal, &key) } #[test] fn rsa_sign_and_verify() -> Result<()> { let key = Key::generate(KeyId::Rsa)?; let header = b"About: lyrics".as_slice(); let message = b"Everything that feels so good is bad bad bad.".as_slice(); let mut signer = SignAlgo::try_from(&key)?; signer.sign([header, message].into_iter())?; let verifier = VerifyAlgo::try_from(&key)?; let verified = verifier.verify([header, message].into_iter(), signer.signature.as_slice())?; assert_eq!(true, verified); Ok(()) } #[test] fn sign_verify_block_rsa() -> Result<()> { let principal = make_principal(); let block_key = Key::Aes256Ctr { key: BLOCK_KEY, iv: BLOCK_IV }; let mut block = make_versioned_block(principal.clone(), block_key)?; let key = Key::generate(KeyId::Rsa)?; let writecap = Writecap { issued_to: Principal(Hash::Sha2_256(PRINCIPAL)), path: make_path(vec!["contacts", "emergency"]), expires: Epoch(1649904316), signing_key: key, signature: Signature::Rsa(SIGNATURE), next: None, }; block = encrypt_block(block, &principal, &writecap.signing_key)?; sign_block(&mut block, writecap)?; assert_eq!(true, verify_block(&block)?); Ok(()) } #[test] fn hash_to_string() { let hash = make_principal().0; let string = hash.to_string(); assert_eq!("Sha2_256:dSip4J0kurN5VhVo_aTipM-ywOOWrqJuRRVQ7aa-bew", string) } #[test] fn hash_to_string_round_trip() -> Result<()> { let expected = make_principal().0; let string = expected.to_string(); let actual = Hash::try_from(string.as_str())?; assert_eq!(expected, actual); Ok(()) } #[test] fn verify_writecap_valid() -> Result<()> { let writecap = make_writecap()?; let result = verify_writecap(&writecap, &writecap.path); assert_eq!(Ok(()), result); Ok(()) } #[test] fn verify_writecap_invalid_signature() -> Result<()> { let mut writecap = make_writecap()?; writecap.signature = Signature::default(); let result = verify_writecap(&writecap, &writecap.path); assert_eq!(Err(WritecapAuthzErr::InvalidSignature), result); Ok(()) } #[test] fn verify_writecap_invalid_path_not_contained() -> Result<()> { let writecap = make_writecap()?; let mut path = writecap.path.clone(); path.components.pop(); // `path` is now a superpath of `writecap.path`, thus the writecap is not authorized to // write to it. let result = verify_writecap(&writecap, &path); assert_eq!(Err(WritecapAuthzErr::UnauthorizedPath), result); Ok(()) } #[test] fn verify_writecap_invalid_expired() -> Result<()> { let mut writecap = make_writecap()?; writecap.expires = Epoch::now() - Duration::from_secs(1); let result = verify_writecap(&writecap, &writecap.path); assert_eq!(Err(WritecapAuthzErr::Expired), result); Ok(()) } #[test] fn verify_writecap_invalid_not_chained() -> Result<()> { let (mut root_writecap, root_key) = make_self_signed_writecap()?; root_writecap.issued_to = Principal(Hash::Sha2_256([0; 32])); sign_writecap(&mut root_writecap, &root_key)?; let node_key = Key::Rsa { public: Vec::from(NODE_PUBLIC_KEY), private: None, padding: RsaPadding::Pkcs1 }; let writecap = make_writecap_trusted_by( root_writecap, &root_key, &node_key, vec!["apps", "contacts"])?; let result = verify_writecap(&writecap, &writecap.path); assert_eq!(Err(WritecapAuthzErr::NotChained), result); Ok(()) } #[test] fn verify_writecap_invalid_root_doesnt_own_path() -> Result<()> { let (mut root_writecap, root_key) = make_self_signed_writecap()?; let owner = Principal(Hash::Sha2_256([0; 32])); root_writecap.path = make_path_with_owner(owner, vec![]); sign_writecap(&mut root_writecap, &root_key)?; let node_key = Key::Rsa { public: Vec::from(NODE_PUBLIC_KEY), private: None, padding: RsaPadding::Pkcs1 }; let writecap = make_writecap_trusted_by( root_writecap, &root_key, &node_key, vec!["apps", "contacts"])?; let result = verify_writecap(&writecap, &writecap.path); assert_eq!(Err(WritecapAuthzErr::RootDoesNotOwnPath), result); Ok(()) } /// Tests that validate the dependencies of this module. mod dependency_tests { use super::*; use openssl::{ ec::{EcGroup, EcKey}, nid::Nid, }; /// This test validates that data encrypted with AES 256 CBC can later be decrypted. #[test] fn aes_256_cbc_roundtrip() { 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()); } /// Tests that the keys for the SECP256K1 curve are the expected sizes. #[test] fn secp256k1_key_lengths() { let group = EcGroup::from_curve_name(Nid::SECP256K1).unwrap(); let key = EcKey::generate(&group).unwrap(); let public = key.public_key_to_der().unwrap(); let private = key.private_key_to_der().unwrap(); let public_len = public.len(); let private_len = private.len(); assert_eq!(88, public_len); assert_eq!(118, private_len); } #[test] fn ed25519_key_lengths() { let key = PKey::generate_x25519().unwrap(); let public = key.public_key_to_der().unwrap(); let private = key.private_key_to_der().unwrap(); let public_len = public.len(); let private_len = private.len(); assert_eq!(44, public_len); assert_eq!(48, private_len); } #[test] fn rsa_signature_len() -> Result<()> { use openssl::rsa::Rsa; let key = Key::generate(KeyId::Rsa)?; let sign_algo = SignAlgo::try_from(&key)?; let private = match &key { Key::Rsa { private: Some(private), .. } => private, _ => return Err(Error::Message("Incorrect key returned.".to_string())) }; let rsa = Rsa::private_key_from_der(private)?; let pkey = PKey::from_rsa(rsa)?; let signer = Signer::new(sign_algo.digest, &pkey)?; assert_eq!(RSA_SIG_LEN, signer.len()?); Ok(()) } } }