tpfs_krypt 7.1.8

An interface for accessing secrets
Documentation
use super::*;
use crate::{
    crypto::translator::KeyPairInstance, errors::KeyManagementError, unit_tests::prelude::*,
    KeyManagement, KeyType, Signing,
};
use proptest::prelude::*;
use secrecy::ExposeSecret;
use strum::IntoEnumIterator;
use tempfile::{tempdir, TempDir};

fn create_file_key_manager() -> (TempDir, FileKeyManager) {
    let directory = tempdir().unwrap();
    let path = directory.as_ref().to_str().unwrap();
    let config = FileKeyManagerConfig {
        keypair_directory_path: String::from(path),
    };
    println!("Directory KeyPair Path: {}", config.keypair_directory_path);

    (directory, FileKeyManager::new(config))
}

fn add_keypairs(count: u8, key_type: KeyType, key_manager: &mut FileKeyManager) -> Vec<String> {
    std::iter::repeat_with(|| key_manager.generate_keypair(key_type).unwrap().id.value)
        .take(count as usize)
        .collect()
}

#[test]
fn valid_config_passes() {
    let directory = tempdir().unwrap();
    let path = String::from(directory.as_ref().to_str().unwrap());
    let manager_config = FileKeyManagerConfig {
        keypair_directory_path: path,
    };

    let result = manager_config.validate();

    assert!(result.is_ok());
}

#[test]
fn nonexisting_path_config_fails() {
    let path = String::from("/foo/bar");
    let manager_config = FileKeyManagerConfig {
        keypair_directory_path: path.clone(),
    };

    let result = manager_config.validate();

    match result {
        Err(KeyManagementError::InvalidDirectoryPath { path: err_path }) => {
            assert_eq!(&path, &err_path)
        }
        _ => panic!("Expected an InvalidDirectoryPath error for: {}", &path),
    }
}

#[test]
fn invalid_file_used_for_directory() {
    let path = String::from("/etc/resolv.conf");
    let manager_config = FileKeyManagerConfig {
        keypair_directory_path: path.clone(),
    };

    let result = manager_config.validate();

    match result {
        Err(KeyManagementError::InvalidDirectoryPath { path: err_path }) => {
            assert_eq!(&path, &err_path)
        }
        _ => panic!("Expected an InvalidDirectoryPath error for: {}", &path),
    }
}

// Ignored since on ci the user is root
// and has full access.
#[test]
#[ignore]
fn invalid_inaccessible_path() {
    let path = String::from("/root/");
    let manager_config = FileKeyManagerConfig {
        keypair_directory_path: path.clone(),
    };

    let result = manager_config.validate();

    match result {
        Err(KeyManagementError::IoError { message, .. }) => {
            assert!(message.contains("Could not read directory"));
            assert!(message.contains(&path));
        }
        _ => panic!("Expected an IoError for: {}", &path),
    }
}

#[test]
fn addresses_initialized_empty() {
    let (_drop_me, sut) = create_file_key_manager();

    let addresses = sut.get_key_ids().unwrap();

    assert_eq!(addresses.len(), 0);
}

proptest! {
    #![proptest_config(ProptestConfig::with_cases(KeyType::iter().len() as u32))]
    #[test]
    fn addresses_generate_counts(key_type in keytype_strategy()) {
        let (_drop_me, mut sut) = create_file_key_manager();
        const TEST_COUNT: u8 = 4;
        let mut expected_addresses = add_keypairs(TEST_COUNT, key_type, &mut sut);
        expected_addresses.sort();

        let addresses = sut.get_key_ids().unwrap();
        let mut addresses: Vec<String> = addresses
            .into_iter()
            .map(|address| address.value)
            .collect();
        addresses.sort();

        prop_assert_eq!(addresses.len() as u8, TEST_COUNT);
        prop_assert_eq!(&expected_addresses, &addresses);
    }
}

proptest! {
    #![proptest_config(ProptestConfig::with_cases(KeyType::iter().len() as u32))]
    #[test]
    fn has_key(key_type in keytype_strategy()) {
        let (_drop_me, mut sut) = create_file_key_manager();
        let in_manager_generated = sut.generate_keypair(key_type).unwrap();
        let generated = KeyPairInstance::generate(key_type).unwrap();

        let in_manager_result = sut.has_key(in_manager_generated.as_ref()).unwrap();
        let generated_result = sut.has_key(&generated.identifier()).unwrap();

        prop_assert!(in_manager_result);
        prop_assert!(!generated_result);
    }
}

proptest! {
    #![proptest_config(ProptestConfig::with_cases(KeyType::iter().len() as u32))]
    #[test]
    fn import_address(key_type in keytype_strategy()) {
        let (_drop_me, mut sut) = create_file_key_manager();
        let generated = KeyPairInstance::generate(key_type).unwrap();
        let addresses = sut.get_key_ids().unwrap();
        prop_assert_eq!(0, addresses.len());

        sut.import_keypair(generated.to_secret().unwrap(), key_type).unwrap();
        let addresses = sut.get_key_ids().unwrap();

        prop_assert_eq!(addresses.len() as u8, 1);
        prop_assert_eq!(generated.identifier(), addresses[0].clone());
    }
}

proptest! {
    #![proptest_config(ProptestConfig::with_cases(KeyType::iter().len() as u32))]
    #[test]
    fn import_derived_key_address((secret, _path) in get_dev_derived_key_strategy(),
                                  key_type in sign_capable_keytype_strategy()) {
        let (_drop_me, mut sut) = create_file_key_manager();
        let from_secret = KeyPairInstance::from_secret(Secret::new(secret.clone()), key_type);

        let from_secret = from_secret.unwrap();
        let addresses = sut.get_key_ids().unwrap();
        prop_assert_eq!(0, addresses.len());

        sut.import_keypair(Secret::new(secret.clone()), key_type).unwrap();
        let addresses = sut.get_key_ids().unwrap();

        prop_assert_eq!(addresses.len() as u8, 1);
        prop_assert_eq!(&from_secret.identifier(), &addresses[0]);
        let from_secret_string = from_secret.to_secret().unwrap();
        prop_assert_eq!(&&secret, &from_secret_string.expose_secret());
    }
}

proptest! {
    // Doesn't actually verify the signature since those tests
    // exist in the crypto module.
    #![proptest_config(ProptestConfig::with_cases(20))]
    #[test]
    fn signing_occurs_from_file_key_manager_interface(
        key_type in sign_capable_keytype_strategy(),
        message in proptest::string::bytes_regex(".+").unwrap()
    ) {
        let (_drop_me, mut sut) = create_file_key_manager();
        let generated_address = sut.generate_keypair(key_type).unwrap();

        let signed = sut.sign(generated_address.as_ref(), message.as_slice()).unwrap();

        prop_assert_eq!(!signed.as_ref().as_ref().is_empty(), true);
    }
}

proptest! {
    #![proptest_config(ProptestConfig::with_cases(KeyType::iter().len() as u32))]
    #[test]
    fn sign_invalid_address(key_type in keytype_strategy()) {
        let (_drop_me, sut) = create_file_key_manager();
        let invalid_address = KeyIdentifier {
            value: "../../5Hh9Gq21Ns4Knd6CjzjMymK6HeW9yYfxdMfhMoDyA8geHVbJ".into(),
            key_type
        };

        let result = sut.sign(&invalid_address, b"Some Message");

        prop_assert!(result.is_err());
    }
}

#[test]
fn sign_non_existing_key() {
    let (_drop_me, sut) = create_file_key_manager();
    let generated = KeyPairInstance::generate(KeyType::SubstrateSr25519).unwrap();
    let generated_address = generated.identifier();

    let result = sut.sign(&generated_address, b"A Message that won't be signed");

    match result {
        Err(KeyManagementError::AddressNotFound { address }) => {
            assert_eq!(&generated_address.value, &address)
        }
        _ => panic!(
            "Expected an AddressNotFound for: {}",
            &generated_address.value
        ),
    }
}