1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
//! Encryption key management and secure hashing
//!
//! The modules exported are all optional keying schemes that can be
//! toggled with Cargo features.
//!
//! # The default symmetric scheme
//!
//! Trees in the simplest case will use the [`UsernamePassword`]
//! keying scheme, which provides a symmetric encryption mechanism.
//!
//! # Asymmetric schemes
//!
//! To create a split key that allows differentiating between reader
//! and writer keys, enable the `cryptobox` Cargo feature. This will
//! allow you to use an asymmetric scheme to encrypt data in the
//! `storage` segments of your tree, but keep the indexes
//! symmetrically encrypted.
//!
//! # Hardware-bound encryption
//!
//! Infinitree has native support for using a Yubikey's
//! Challenge-Response HMAC-SHA1 mode to encrypt the header of the
//! tree, enabled by the `yubikey` Cargo feature.
//!
//! To allow for configuring the Yubikey, the `yubico_manager` crate
//! is re-exported.
//!
//! # Changing keys
//!
//! Changing of the header key is supported through by creating a
//! special key through [`ChangeHeaderKey::swap_on_seal`] constructor.
//!
//! Because changing the internal keys might potentially require
//! re-encrypting the entire archive in a way that's not possible to
//! do with the Infinitree library, the implementation of such a
//! change is up to your specific use case.
//!
//! For instance, let's assume you crate a symmetrically keyed tree,
//! then write data into the `storage` segment of it.
//!
//! ```no_run
//! use infinitree::{*,
//! crypto::*,
//! object::Writer, fields::VersionedMap, backends::Directory};
//!
//! let key = UsernamePassword::with_credentials("username".to_string(),
//! "old_password".to_string()).unwrap();
//!
//! let mut tree = Infinitree::<VersionedMap<String, ChunkPointer>>::open(
//! Directory::new("/storage").unwrap(),
//! key
//! ).unwrap();
//!
//! // get access to the custom `storage` segment of the tree
//! let mut writer = tree.storage_writer().unwrap();
//!
//! // use the writer to write data
//! let ptr = writer.write(b"my precious secret").unwrap();
//!
//! // then store it in the index
//! tree.index().insert("Gollum?".into(), ptr);
//!
//! tree.commit("My first shenanigans");
//! ```
//!
//! Changing the internal key at this point to
//! e.g. `cryptobox::StorageOnly` would mean that all existing data in
//! the stash, referenced only through [`ChunkPointer`](crate::ChunkPointer)s is now
//! inaccessible.
//!
//! For the sake of straightforward use and space efficiency of chunk
//! references, this is not permitted.
pub use blake3::Hasher;
use ring::aead;
pub(crate) use ring::rand::{SecureRandom, SystemRandom};
use secrecy::{ExposeSecret, Zeroize};
mod error;
mod header;
mod ops;
mod rawkey;
mod scheme;
mod symmetric;
pub(crate) mod symmetric08;
pub(crate) use error::*;
pub(crate) use header::*;
pub(crate) use ops::*;
pub use rawkey::*;
pub use scheme::*;
pub use symmetric::UsernamePassword;
#[cfg(feature = "cryptobox")]
pub mod cryptobox;
#[cfg(feature = "yubikey")]
pub mod yubikey;
const CRYPTO_DIGEST_SIZE: usize = 32;
// TODO: ideally this should be a tuple struct wrapping blake3::Hash,
// implementing Serialize & Deserialize and the rest.
//
// That way we get constant time equality checks for free, which is
// prudent to want, but I'm uncertain about a realistic side-channel
// based on this right now.
/// A cryptographic hash of some data
pub type Digest = [u8; CRYPTO_DIGEST_SIZE];
/// HMAC generated by an AEAD scheme
pub type Tag = [u8; 16];
#[inline]
fn get_aead(key: RawKey) -> aead::LessSafeKey {
let key =
aead::UnboundKey::new(&aead::CHACHA20_POLY1305, key.expose_secret()).expect("bad key");
aead::LessSafeKey::new(key)
}
fn derive_argon2(secret: &[u8], salt_raw: &[u8], password: &[u8]) -> Result<RawKey> {
let salt = blake3::hash(salt_raw);
let mut result = argon2::hash_raw(
password,
salt.as_bytes(),
&argon2::Config {
hash_length: CRYPTO_DIGEST_SIZE as u32,
variant: argon2::Variant::Argon2id,
secret,
..argon2::Config::default()
},
)?;
let mut outbuf = [0; CRYPTO_DIGEST_SIZE];
outbuf.copy_from_slice(&result);
result.zeroize();
Ok(outbuf.into())
}
fn derive_subkey(key: &RawKey, ctx: &str) -> Result<RawKey> {
let outbuf = blake3::derive_key(ctx, key.expose_secret());
Ok(outbuf.into())
}
fn generate_key(rand: &impl SecureRandom) -> Result<RawKey> {
let mut buf = [0; KEY_SIZE];
rand.fill(&mut buf)?;
Ok(buf.into())
}