bdrck 0.22.5

Generic common foundational utilities.
Documentation
// Copyright 2015 Axel Rasmussen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::crypto::digest::*;
use crate::crypto::key::*;
use crate::crypto::keystore::*;
use crate::crypto::secret::Secret;
use crate::testing::temp;
use std::fs;

fn new_password(password: &str) -> Secret {
    let bytes = password.as_bytes();
    let mut s = Secret::with_len(bytes.len()).unwrap();
    unsafe { s.as_mut_slice() }.copy_from_slice(bytes);
    s
}

fn new_password_key(password: &str, salt: &Salt) -> Key {
    Key::new_password(
        &new_password(password),
        salt,
        OPS_LIMIT_INTERACTIVE,
        MEM_LIMIT_INTERACTIVE,
    )
    .unwrap()
}

#[test]
fn test_keystore_save_round_trip() {
    crate::init().unwrap();

    let file = temp::File::new_file().unwrap();

    let wrap_key = Key::new_random().unwrap();
    let master_digest: Option<Digest>;

    {
        let mut keystore = DiskKeyStore::new(file.path(), false).unwrap();
        assert!(keystore.add_key(&wrap_key).unwrap());
        master_digest = Some(keystore.get_master_key().unwrap().get_digest());
    }

    {
        let mut keystore = DiskKeyStore::new(file.path(), false).unwrap();
        keystore.open(&wrap_key).unwrap();
        assert_eq!(
            master_digest.unwrap(),
            keystore.get_master_key().unwrap().get_digest()
        );
    }
}

#[test]
fn test_keystore_open_with_added_key() {
    crate::init().unwrap();

    let file = temp::File::new_file().unwrap();

    let salt = Salt::default();
    let keya = new_password_key("foo", &salt);
    let keyb = new_password_key("bar", &salt);
    assert_ne!(keya.get_digest(), keyb.get_digest());
    let master_digest: Option<Digest>;

    {
        let mut keystore = DiskKeyStore::new(file.path(), false).unwrap();
        assert!(keystore.add_key(&keya).unwrap());
        master_digest = Some(keystore.get_master_key().unwrap().get_digest());
        assert!(keystore.add_key(&keyb).unwrap());
    }

    {
        let mut keystore = DiskKeyStore::new(file.path(), false).unwrap();
        keystore.open(&keyb).unwrap();
        assert_eq!(
            master_digest.unwrap(),
            keystore.get_master_key().unwrap().get_digest()
        );
    }
}

#[test]
fn test_add_duplicate_key() {
    crate::init().unwrap();

    let file = temp::File::new_file().unwrap();

    let wrap_key = Key::new_random().unwrap();
    // Note that creating a new DiskKeyStore automatically adds the given key.
    let mut keystore = DiskKeyStore::new(file.path(), false).unwrap();
    keystore.add_key(&wrap_key).unwrap();
    // Check that adding the same key again is a no-op (returns false).
    assert!(!keystore.add_key(&wrap_key).unwrap());
}

#[test]
fn test_remove_unused_key() {
    crate::init().unwrap();

    let file = temp::File::new_file().unwrap();

    let salt = Salt::default();
    let keya = new_password_key("foo", &salt);
    let keyb = new_password_key("bar", &salt);
    assert_ne!(keya.get_digest(), keyb.get_digest());

    let mut keystore = DiskKeyStore::new(file.path(), false).unwrap();
    keystore.add_key(&keya).unwrap();
    // Test that removing some other key returns false, since it isn't in the
    // DiskKeyStore.
    assert!(!keystore.remove_key(&keyb).unwrap());
}

#[test]
fn test_remove_only_key() {
    crate::init().unwrap();

    let file = temp::File::new_file().unwrap();

    let key = Key::new_random().unwrap();
    let mut keystore = DiskKeyStore::new(file.path(), false).unwrap();
    keystore.add_key(&key).unwrap();
    // Test that removing the sole key is treated as an error.
    assert!(keystore.remove_key(&key).is_err());
}

#[test]
fn test_remove_first_key() {
    crate::init().unwrap();

    let file = temp::File::new_file().unwrap();

    let key_a = Key::new_random().unwrap();
    let key_b = Key::new_random().unwrap();
    // Create a keystore with one initial key.
    let mut keystore = DiskKeyStore::new(file.path(), false).unwrap();
    keystore.add_key(&key_a).unwrap();
    // Add a second key.
    assert!(keystore.add_key(&key_b).unwrap());
    // Try removing the original key - this should succeed, since there is a
    // second valid key.
    assert!(keystore.remove_key(&key_a).unwrap());
}

#[test]
fn test_keystore_state_after_new() {
    crate::init().unwrap();

    let ks = KeyStore::new().unwrap();
    // Freshly-created keystore owns its in-memory master key.
    assert!(ks.is_open());
    // But it has no wrapping keys yet, so persisting it would lose the master.
    assert!(!ks.is_persistable());
}

#[test]
fn test_keystore_get_id_is_hex() {
    crate::init().unwrap();

    let ks = KeyStore::new().unwrap();
    let id = ks.get_id();
    // The id is the hex-lower encoding of the encrypted auth token. It should
    // be non-empty, entirely lowercase-hex, and even-length.
    assert!(!id.is_empty());
    assert_eq!(0, id.len() % 2);
    assert!(
        id.chars()
            .all(|c| c.is_ascii_hexdigit() && !c.is_uppercase())
    );
}

#[test]
fn test_keystore_iter_wrapped_keys() {
    crate::init().unwrap();

    let mut ks = KeyStore::new().unwrap();
    assert_eq!(0, ks.iter_wrapped_keys().count());

    let key_a = Key::new_random().unwrap();
    let key_b = Key::new_random().unwrap();
    ks.add_key(&key_a).unwrap();
    assert_eq!(1, ks.iter_wrapped_keys().count());
    ks.add_key(&key_b).unwrap();
    assert_eq!(2, ks.iter_wrapped_keys().count());
}

#[test]
fn test_get_master_key_requires_open() {
    crate::init().unwrap();

    // Serialize a keystore with at least one wrapping key, then reload it
    // (which leaves master_key == None) and verify get_master_key fails.
    let wrap_key = Key::new_random().unwrap();
    let bytes = {
        let mut ks = KeyStore::new().unwrap();
        ks.add_key(&wrap_key).unwrap();
        ks.to_vec().unwrap()
    };

    let ks = KeyStore::load_slice(&bytes).unwrap();
    assert!(!ks.is_open());
    assert!(ks.get_master_key().is_err());
}

#[test]
fn test_add_key_without_master_key() {
    crate::init().unwrap();

    let wrap_key = Key::new_random().unwrap();
    let bytes = {
        let mut ks = KeyStore::new().unwrap();
        ks.add_key(&wrap_key).unwrap();
        ks.to_vec().unwrap()
    };

    let mut ks = KeyStore::load_slice(&bytes).unwrap();
    // Without opening, add_key can't wrap the master key.
    assert!(ks.add_key(&Key::new_random().unwrap()).is_err());
}

#[test]
fn test_open_already_open_is_noop() {
    crate::init().unwrap();

    let mut ks = KeyStore::new().unwrap();
    assert!(ks.is_open());
    // open() on an already-open keystore should succeed without needing the
    // given key to match (the implementation short-circuits).
    let any_key = Key::new_random().unwrap();
    ks.open(&any_key).unwrap();
    assert!(ks.is_open());
}

#[test]
fn test_open_with_unknown_key() {
    crate::init().unwrap();

    let real = Key::new_random().unwrap();
    let other = Key::new_random().unwrap();
    let bytes = {
        let mut ks = KeyStore::new().unwrap();
        ks.add_key(&real).unwrap();
        ks.to_vec().unwrap()
    };

    let mut ks = KeyStore::load_slice(&bytes).unwrap();
    // `other` isn't in the wrapped_keys set, so open() returns an error and
    // leaves the keystore closed.
    assert!(ks.open(&other).is_err());
    assert!(!ks.is_open());
}

#[test]
fn test_disk_keystore_force_overwrite() {
    crate::init().unwrap();

    let file = temp::File::new_file().unwrap();
    // Remove the file up front so the first DiskKeyStore::new creates it.
    fs::remove_file(file.path()).unwrap();

    let wrap_key = Key::new_random().unwrap();
    // Stage 1: create a keystore and persist it by adding a wrapping key.
    {
        let mut ks = DiskKeyStore::new(file.path(), false).unwrap();
        ks.add_key(&wrap_key).unwrap();
    }
    let first_bytes = fs::read(file.path()).unwrap();
    assert!(!first_bytes.is_empty());

    // Stage 2: force_overwrite=true yields a fresh keystore, *not* a load of
    // the existing file.
    let new_key = Key::new_random().unwrap();
    {
        let mut ks = DiskKeyStore::new(file.path(), true).unwrap();
        // Fresh keystore has no wrapping keys carried over from the file.
        assert_eq!(0, ks.iter_wrapped_keys().count());
        ks.add_key(&new_key).unwrap();
    }
    let second_bytes = fs::read(file.path()).unwrap();
    // The file was overwritten, so the bytes (encrypted with a different
    // master key + new nonce) will differ.
    assert_ne!(first_bytes, second_bytes);

    // Stage 3: loading without force_overwrite reads what we wrote in stage 2.
    {
        let mut ks = DiskKeyStore::new(file.path(), false).unwrap();
        assert!(ks.open(&new_key).is_ok());
    }
}

#[test]
fn test_keystore_load_slice_and_load_read() {
    crate::init().unwrap();

    let file = temp::File::new_file().unwrap();
    fs::remove_file(file.path()).unwrap();

    let wrap_key = Key::new_random().unwrap();
    {
        let mut ks = DiskKeyStore::new(file.path(), false).unwrap();
        ks.add_key(&wrap_key).unwrap();
    }

    // load_slice path.
    let bytes = fs::read(file.path()).unwrap();
    let mut ks = KeyStore::load_slice(&bytes).unwrap();
    ks.open(&wrap_key).unwrap();

    // load_read path.
    let f = fs::File::open(file.path()).unwrap();
    let mut ks2 = KeyStore::load_read(f).unwrap();
    ks2.open(&wrap_key).unwrap();
}

#[test]
fn test_unpersistable() {
    crate::init().unwrap();

    let file = temp::File::new_file().unwrap();
    // Explicitly make sure the file doesn't exist.
    fs::remove_file(file.path()).unwrap();
    assert!(!file.path().exists());

    {
        // Construct a new key store, but don't add any keys.
        let keystore = DiskKeyStore::new(file.path(), false).unwrap();
        assert!(!keystore.is_persistable());
    }

    // Since the key store was not persistable, the file should still not exist.
    assert!(!file.path().exists());
}