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::secret::Secret;
use crate::crypto::util::randombytes_into;
use rmp_serde;

fn clone_key(key: &Key) -> Key {
    Key::deserialize(key.serialize().unwrap()).unwrap()
}

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 random_secret(len: usize) -> Secret {
    let mut s = Secret::with_len(len).unwrap();
    randombytes_into(unsafe { s.as_mut_slice() });
    s
}

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

    let nonce = Nonce::new();
    let next = Nonce::new();
    assert_eq!(nonce, next);
    assert_ne!(nonce, next.increment());
}

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

    let key = Key::new_random().unwrap();
    let digest = key.get_digest();
    let serialized = rmp_serde::to_vec(&digest).unwrap();
    let deserialized: Digest = rmp_serde::from_slice(serialized.as_slice()).unwrap();
    assert_eq!(digest, deserialized);
}

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

    let key = Key::new_random().unwrap();
    let digest = key.get_digest();
    let s = format!("{:?}", digest);
    // The hand-rolled Debug impl uses the slice hex formatter (`{:x?}`),
    // so the output opens with `[` and contains comma-separated hex bytes.
    assert!(s.starts_with('['), "got {:?}", s);
    assert!(s.ends_with(']'), "got {:?}", s);
    // DIGEST_BYTES (64) one- or two-char hex tokens separated by ", ".
    // Just sanity-check that we have 63 ", " separators.
    assert_eq!(63, s.matches(", ").count(), "got {:?}", s);
}

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

    // A serialized sequence shorter than DIGEST_BYTES should fail.
    let too_short: Vec<u8> = vec![0u8; 5];
    let bytes = rmp_serde::to_vec(&too_short).unwrap();
    assert!(rmp_serde::from_slice::<Digest>(&bytes).is_err());

    // Longer than DIGEST_BYTES should also fail (the Digest visitor explicitly
    // rejects trailing elements).
    let too_long: Vec<u8> = vec![0u8; 100];
    let bytes = rmp_serde::to_vec(&too_long).unwrap();
    assert!(rmp_serde::from_slice::<Digest>(&bytes).is_err());
}

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

    let salt = Salt::default();
    let _key = Key::new_password(
        &new_password("foobar"),
        &salt,
        OPS_LIMIT_INTERACTIVE,
        MEM_LIMIT_INTERACTIVE,
    )
    .unwrap();
}

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

    let a = Key::new_random().unwrap();
    let b = Key::new_random().unwrap();
    let c = clone_key(&a);

    assert_eq!(a.get_digest(), c.get_digest());
    assert_ne!(a.get_digest(), b.get_digest());
}

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

    let key = Key::new_random().unwrap();
    let plaintext = random_secret(1024);
    let (nonce, ciphertext) = key.encrypt(&plaintext, None).unwrap();
    assert_ne!(unsafe { plaintext.as_slice() }, ciphertext.as_slice());
    let decrypted = key.decrypt(nonce.as_ref(), ciphertext.as_slice()).unwrap();
    assert_eq!(unsafe { plaintext.as_slice() }, unsafe {
        decrypted.as_slice()
    });
}

fn secret_from_slice(bytes: &[u8]) -> Secret {
    let mut s = Secret::with_len(bytes.len()).unwrap();
    unsafe { s.as_mut_slice() }.copy_from_slice(bytes);
    s
}

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

    // 40 bytes starting with something that is neither KEY_SERDE_COMPAT_PREFIX
    // (0x81 0xa3 0x4b 0x65 0x79 0x91 0xc4 0x20) nor
    // KEY_SERDE_COMPAT_PREFIX_ALT (0x81 0x00 0x91 0xc4 0x20).
    let mut bytes = vec![0xffu8; 40];
    bytes[0] = 0x00;
    assert!(Key::deserialize(secret_from_slice(&bytes)).is_err());
}

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

    // Valid primary prefix followed by too few bytes of key material.
    let mut too_short: Vec<u8> = vec![0x81, 0xa3, 0x4b, 0x65, 0x79, 0x91, 0xc4, 0x20];
    too_short.extend_from_slice(&[0u8; 16]);
    assert!(Key::deserialize(secret_from_slice(&too_short)).is_err());

    // Valid primary prefix followed by too many bytes of key material.
    let mut too_long: Vec<u8> = vec![0x81, 0xa3, 0x4b, 0x65, 0x79, 0x91, 0xc4, 0x20];
    too_long.extend_from_slice(&[0u8; 48]);
    assert!(Key::deserialize(secret_from_slice(&too_long)).is_err());
}

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

    // The ALT prefix (5 bytes) followed by exactly KEY_BYTES (32) bytes should
    // deserialize successfully.
    let mut bytes: Vec<u8> = vec![0x81, 0x00, 0x91, 0xc4, 0x20];
    bytes.extend_from_slice(&[0x42u8; 32]);
    let key = Key::deserialize(secret_from_slice(&bytes)).unwrap();

    // The resulting key has the same digest regardless of which prefix we
    // deserialize from, as long as the 32 trailing bytes match.
    let mut bytes2: Vec<u8> = vec![0x81, 0xa3, 0x4b, 0x65, 0x79, 0x91, 0xc4, 0x20];
    bytes2.extend_from_slice(&[0x42u8; 32]);
    let key2 = Key::deserialize(secret_from_slice(&bytes2)).unwrap();
    assert_eq!(key.get_digest(), key2.get_digest());
}

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

    let key = Key::new_random().unwrap();
    let plaintext = random_secret(1024);
    let (nonce, ciphertext) = key.encrypt(&plaintext, None).unwrap();
    assert_ne!(unsafe { plaintext.as_slice() }, ciphertext.as_slice());

    let wrong_key = Key::new_random().unwrap();
    let decrypted_result = wrong_key.decrypt(nonce.as_ref(), ciphertext.as_slice());
    assert!(decrypted_result.is_err());
}

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

    // Too short rejected.
    assert!(Nonce::from_slice(&[0u8; 5]).is_err());
    // Too long rejected.
    assert!(Nonce::from_slice(&[0u8; 32]).is_err());
    // Exact length accepted.
    assert!(Nonce::from_slice(&[0u8; NONCE_BYTES]).is_ok());
}

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

    let key = Key::new_random().unwrap();
    let plaintext = random_secret(128);
    let (_nonce, ciphertext) = key.encrypt(&plaintext, None).unwrap();

    // Decrypting without a nonce must fail.
    assert!(key.decrypt(None, &ciphertext).is_err());
}

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

    let key = Key::new_random().unwrap();
    let nonce = Nonce::default();

    // Empty ciphertext has no authentication tag.
    assert!(key.decrypt(Some(&nonce), &[]).is_err());
    // Just under TAG_BYTES is still missing the tag.
    assert!(key.decrypt(Some(&nonce), &[0u8; TAG_BYTES - 1]).is_err());
}

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

    let key = Key::new_random().unwrap();
    let plaintext = random_secret(128);
    let (nonce, ciphertext) = key.encrypt(&plaintext, None).unwrap();

    let wrong_nonce = nonce.unwrap().increment();
    // Decrypting with a different nonce must fail authentication.
    assert!(key.decrypt(Some(&wrong_nonce), &ciphertext).is_err());
}