rops 0.1.7

SOPS-like library in pure Rust
Documentation
use indexmap::IndexMap;

use crate::*;

impl<C: Cipher> RopsMap<EncryptedMap<C>> {
    pub fn decrypt(self, data_key: &DataKey) -> Result<RopsMap<DecryptedMap>, DecryptRopsValueError> {
        Self::decrypt_impl(self, data_key, &mut None)
    }

    pub fn decrypt_and_save_nonces(
        self,
        data_key: &DataKey,
    ) -> Result<(RopsMap<DecryptedMap>, SavedRopsMapNonces<C>), DecryptRopsValueError> {
        let mut saved_nonces = SavedRopsMapNonces::default();
        Self::decrypt_impl(self, data_key, &mut Some(&mut saved_nonces)).map(|tree| (tree, saved_nonces))
    }

    fn decrypt_impl<Ci: Cipher>(
        map: RopsMap<EncryptedMap<Ci>>,
        data_key: &DataKey,
        optional_saved_nonces: &mut Option<&mut SavedRopsMapNonces<Ci>>,
    ) -> Result<RopsMap<DecryptedMap>, DecryptRopsValueError> {
        return decrypt_map_recursive(map, data_key, &KeyPath::default(), optional_saved_nonces);

        fn decrypt_map_recursive<C: Cipher>(
            map: RopsMap<EncryptedMap<C>>,
            data_key: &DataKey,
            key_path: &KeyPath,
            optional_saved_nonces: &mut Option<&mut SavedRopsMapNonces<C>>,
        ) -> Result<RopsMap<DecryptedMap>, DecryptRopsValueError> {
            let mut decrypted_map = IndexMap::with_capacity(map.len());

            for (key, sub_tree) in map.0 {
                let sub_key_path = key_path.join(&key);
                decrypted_map.insert(
                    key,
                    decrypt_tree_recursive(sub_tree, data_key, &sub_key_path, optional_saved_nonces)?,
                );
            }

            Ok(decrypted_map.into())
        }

        fn decrypt_tree_recursive<C: Cipher>(
            tree: RopsTree<EncryptedMap<C>>,
            data_key: &DataKey,
            key_path: &KeyPath,
            optional_saved_nonces: &mut Option<&mut SavedRopsMapNonces<C>>,
        ) -> Result<RopsTree<DecryptedMap>, DecryptRopsValueError> {
            Ok(match tree {
                RopsTree::Sequence(sequence) => sequence
                    .into_iter()
                    .map(|sub_tree| decrypt_tree_recursive(sub_tree, data_key, key_path, optional_saved_nonces))
                    .collect::<Result<Vec<_>, _>>()
                    .map(RopsTree::Sequence)?,
                RopsTree::Map(encrypted_map) => {
                    RopsTree::Map(decrypt_map_recursive(encrypted_map, data_key, key_path, optional_saved_nonces)?)
                }

                RopsTree::Null => RopsTree::Null,
                RopsTree::Leaf(maybe_encrypted_value) => RopsTree::Leaf(match maybe_encrypted_value {
                    RopsMapEncryptedLeaf::Encrypted(encrypted_value) => match optional_saved_nonces {
                        Some(saved_nonces) => {
                            let nonce = encrypted_value.nonce.clone();
                            let decrypted_value = encrypted_value.decrypt(data_key, key_path)?;
                            saved_nonces.insert((key_path.clone(), decrypted_value.clone()), nonce);
                            decrypted_value
                        }
                        None => encrypted_value.decrypt(data_key, key_path)?,
                    },
                    RopsMapEncryptedLeaf::Escaped(escaped_value) => escaped_value,
                }),
            })
        }
    }
}

#[cfg(test)]
mod tests {
    #[cfg(feature = "aes-gcm")]
    mod aes_gcm {
        use crate::*;

        #[test]
        fn decrypts_map() {
            assert_eq!(
                RopsMap::<DecryptedMap>::mock(),
                RopsMap::<EncryptedMap<AES256GCM>>::mock().decrypt(&DataKey::mock()).unwrap()
            )
        }

        #[test]
        fn decryption_can_save_nonces() {
            assert_eq!(
                (RopsMap::<DecryptedMap>::mock(), SavedRopsMapNonces::mock()),
                RopsMap::<EncryptedMap<AES256GCM>>::mock()
                    .decrypt_and_save_nonces(&DataKey::mock())
                    .unwrap()
            )
        }
    }
}