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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
//! A simple storage-oriented cryptographic library that offers you freedom from choice. It
//! supports hashing, public key signatures, public key & symmetric key encryption, key
//! export/import, and basic key storage.
//!
//! Getting cryptography right can be hard. This library attempts to make things easy by only
//! providing a small number of cryptographic primitives, makes strong decisions about the
//! cryptographic algorithms and their implementations, and tries to limit the number of bad things
//! an end user can do. Changing algorithms should be infrequent, and follows a planned process
//! (see [Cryptographic Versioning](#cryptographic-versioning)). On the plus side, it's pretty hard
//! to misuse this library in ways that leak secrets and compromise security. On the downside, this
//! library is pretty strongly meant for working with stored data, not communication protocols, and
//! cannot support even remotely unusual cryptographic operations. Forcing use of a single preferred
//! set of algorithms also greatly limits hardware compatibility.
//!
//! # User Guidelines
//!
//! You probably shouldn't be using this library directly. Portions of it should instead be
//! exported by an implementor of a Vault, and you should use those. Alternately, you might be
//! using this through [`fog-pack`](https://crates.io/crates/fog-pack), which also re-exports
//! portions of this library. You can expect to see these primitives:
//!
//! General:
//! - [`Vault`]: A structure that can hold onto your cryptographic keys.
//!
//! Hashing:
//! - [`Hash`](hash::Hash): The cryptographic hash of a sequence of bytes.
//! - [`HashState`](hash::HashState): A structure for iteratively feeding in bytes to create a `Hash`.
//!
//! Signatures:
//! - [`Signature`]: A validated cryptographic signature of a `Hash`.
//! - [`UnverifiedSignature`]: A cryptographic signature that hasn't been verified yet.
//! - [`IdentityKey`]: A private key for signing hashes.
//! - [`Identity`]: A public key identity to indicate which `IdentityKey` created a given `Signature`.
//! - [`BareIdKey`]: A private signing key that can be exported without encrypting it.
//!
//! Symmetric-Key Encryption:
//! - [`StreamKey`]: A shared symmetric key for encrypting & decrypting data.
//! - [`StreamId`]: A public, unique identifier for indicating what `StreamKey` should be used
//!   for decrypting encrypted data.
//!
//! Public-Key Encryption:
//! - [`LockKey`]: A private key for decrypting data.
//! - [`LockId`]: A public key to indicate what `LockKey` should be used for decrypting encrypted
//!   data.
//!
//! Encrypted Storage:
//! - [`IdentityLockbox`]: An encrypted container that holds a `IdentityKey`.
//! - [`StreamLockbox`]: An encrypted container that holds a `StreamKey`.
//! - [`LockLockbox`]: An encrypted container that holds a `LockKey`.
//! - [`DataLockbox`]: An encrypted container that holds a byte sequence.
//!
//! # Vault Implementor Guidelines
//!
//! First, re-export the structs listed in the user guidelines. If your vault is entirely in
//! software, you probably want to use the various `ContainedXXX` structs for holding keys, and
//! store them by exporting them for some master key. Avoid letting the keys sit around in some
//! unencrypted form. Your master key can be created by obtaining a 32-byte random byte sequence,
//! prepending it with a 1 (the version byte), and using [`BareStreamKey`]'s `try_from`
//! implementation to encapsulate the sequence. Make sure to zeroize the master key after doing
//! this!
//!
//! If your vault has a hardware or OS component, your hardware vault's capabilities may be
//! limited in its ability to store all types of keys. In this case, you will need to have a
//! software-side implementation to make up for the missing storage. A recommended approach here is
//! to actually accept a reference to a pure-software vault on creation, and let it handle any
//! unsupported operations. Your vault can then capture all operations that it does support.
//!
//! Alternately, if your hardware / OS component supports an extremely small subset of
//! functionality, cannot perform any type of key import/export, and is meant for high risk
//! scenarios, consider not supporting the Vault trait at all. Instead, create your own key store
//! interface, and provide backer implementations for just the supported interfaces (your options
//! being [`SignInterface`], [`StreamInterface`], and [`LockInterface`]).
//!
//! # Cryptographic Algorithms Used
//!
//! The currently used algorithms are:
//!
//! - Hashing: Blake2B with a 32-byte digest
//! - Signing: Ed25519 with ["strict" verification][StrictVerification]
//! - Symmetric Encryption: AEAD cipher using XChaCha20 and Poly1305.
//! - Diffie-Hellman key exchange: X25519
//!
//! # Cryptographic Versioning
//!
//! This library has 4 core cryptographic algorithms that may be upgraded over time:
//!
//! - The hash algorithm
//! - The signing algorithm
//! - The symmetric encryption algorithm (including bulk encryption, AEAD construction, and HMAC)
//! - The Diffie-Hellman (DH) key exchange algorithm (used for encrypting data with a public key)
//!
//! Upgrades should be infrequent, and are done roughly when an existing recommended algorithm is
//! regarded as weak but not yet broken.
//!
//! The ideal upgrade process is:
//!
//! 1. A new algorithm is selected to replace an existing one.
//! 2. The new algorithm is implemented. The relevant MAX_VERSION constant is incremented.
//! 3. After being deployed for 1 year, the relevant DEFAULT_VERSION constant is incremented. This
//!    gives time for library users to support the new algorithm without breaking non-updated
//!    deployments.
//! 4. After 2 more years, the relevant MIN_VERSION constant is incremented. This gives time for
//!    library users to increment the default version on all deployments, then upgrade all existing
//!    stored data as required.
//!
//! This is the best-case upgrade scenario. If an existing algorithm is considered broken, the
//! DEFAULT_VERSION and MIN_VERSION will be incremented as soon as possible. "Broken" here means it
//! is feasible for a well-funded attacker to compromise the algorithm. Breaking compatibility with
//! deployed code is considered an acceptable choice when security is compromised.
//!
//! We are almost certainly going to upgrade the signing and DH exchange algorithms in the future,
//! as we will need to move to post-quantum algorithms. There's no similar looming threat for the
//! hash & symmetric encryption algorithms.
//!
//! [StrictVerification]: https://docs.rs/ed25519-dalek/1.0.1/ed25519_dalek/struct.PublicKey.html#method.verify_strict

#[cfg(feature = "with-serde")]
pub mod serde;

mod error;
pub use self::error::CryptoError;

pub mod hash;

pub mod identity;
use identity::*;

pub mod lock;
use lock::*;

pub mod lockbox;
use lockbox::*;

pub mod stream;
use stream::*;

use rand_core::{CryptoRng, RngCore};

/// Holds a cryptographic random number generator (RNG). This trait is needed so that a RNG can be
/// passed around as a trait object.
pub trait CryptoSrc: CryptoRng + RngCore {}
impl<T: CryptoRng + RngCore> CryptoSrc for T {}

/// A trait to interface with long-term storage of various cryptographic keys.
///
/// Any implementor should store keys in three separate key-value stores: one for `IdentityKey`
/// storage, one for `LockKey` storage, and one for `StreamKey` storage. Each provides a separate
/// lookup by name, or the various keys may be retrieved by looking them up by their public
/// identities.
pub trait Vault {
    /// Create & store a new `IdentityKey`.
    fn new_id(&self, name: String) -> IdentityKey;

    /// Create & store a new `LockKey`.
    fn new_lock(&self, name: String) -> LockKey;

    /// Create & store a new `StreamKey`.
    fn new_stream(&self, name: String) -> StreamKey;

    /// Fetch a stored `IdentityKey` by name. Returns none if no key by that name is stored.
    fn get_id(&self, name: &str) -> Option<IdentityKey>;

    /// Fetch a stored `LockKey` by name. Returns none if no key by that name is stored.
    fn get_lock(&self, name: &str) -> Option<LockKey>;

    /// Fetch a stored `StreamKey` by name. Returns none if no key by that name is stored.
    fn get_stream(&self, name: &str) -> Option<StreamKey>;

    /// Fetch a stored `IdentityKey` by its public `Identity`, also returning the name it is stored
    /// under. Returns none if the key is not in the vault.
    fn find_id(&self, id: Identity) -> Option<(&str, IdentityKey)>;

    /// Fetch a stored `LockKey` by its public `LockId`, also returning the name it is stored
    /// under. Returns none if the key is not in the vault.
    fn find_lock(&self, lock: LockId) -> Option<(&str, LockKey)>;

    /// Fetch a stored `StreamKey` by its public `StreamId`, also returning the name it is stored
    /// under. Returns none if the key is not in the vault.
    fn find_stream(&self, stream: StreamId) -> Option<(&str, StreamKey)>;

    /// Change the lookup name for a `StreamKey`.
    fn rename_id(&self, old_name: &str, new_name: String) -> bool;

    /// Change the lookup name for a `StreamKey`.
    fn rename_lock(&self, old_name: &str, new_name: String) -> bool;

    /// Change the lookup name for a `StreamKey`.
    fn rename_stream(&self, old_name: &str, new_name: String) -> bool;

    /// Remove the `IdentityKey` stored under this name.
    fn remove_id(&self, name: &str) -> bool;

    /// Remove the `LockKey` stored under this name.
    fn remove_lock(&self, name: &str) -> bool;

    /// Remove the `StreamKey` stored under this name.
    fn remove_stream(&self, name: &str) -> bool;

    /// Attempt to decrypt a `LockLockbox` using any of the `LockKey` and `StreamKey` instances
    /// stored. On success, the new `LockKey` is stored in the vault under the provided name.
    fn decrypt_lock_key(&self, name: String, lock: &LockLockboxRef)
        -> Result<LockKey, CryptoError>;

    /// Attempt to decrypt a `IdentityLockbox` using any of the `LockKey` and `StreamKey` instances
    /// stored. On success, the new `IdentityKey` is stored in the vault under the provided name.
    fn decrypt_identity_key(
        &self,
        name: String,
        lock: &IdentityLockboxRef,
    ) -> Result<IdentityKey, CryptoError>;

    /// Attempt to decrypt a `StreamLockbox` using any of the `LockKey` and `StreamKey` instances
    /// stored. On success, the new `StreamKey` is stored in the vault under the provided name.
    fn decrypt_stream_key(
        &self,
        name: String,
        lock: &StreamLockboxRef,
    ) -> Result<StreamKey, CryptoError>;

    /// Attempt to decrypt a `StreamLockbox` using any of the `LockKey` and `StreamKey` instances
    /// stored.
    fn decrypt_data(&self, lock: &DataLockbox) -> Result<Vec<u8>, CryptoError>;
}