diff --git a/Cargo.toml b/Cargo.toml index 0599e3f..192fe9f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,21 +12,29 @@ keywords = ["cryptography", "encryption"] categories = ["cryptography"] [features] -default = ["encryption", "ikm-management"] +default = ["aes", "chacha", "ikm-management"] encryption = [] +aes = ["encryption", "aes-gcm", "hkdf", "sha2"] +chacha = ["encryption", "chacha20poly1305", "blake3"] ikm-management = [] encrypt-at = [] [dependencies] -aes-gcm = { version = "0.10.3", default-features = false, features = ["std", "aes"] } base64ct = { version = "1.6.0", default-features = false, features = ["std"] } -blake3 = { version = "1.5.0", default-features = false } -chacha20poly1305 = { version = "0.10.1", default-features = false, features = ["std"] } getrandom = { version = "0.2.12", default-features = false } -hkdf = { version = "0.12.4", default-features = false, features = ["std"] } -sha2 = { version = "0.10.8", default-features = false, features = ["std"] } thiserror = { version = "1.0.57", default-features = false } +# chacha feature: +# - XChaCha20Poly1305WithBlake3 +chacha20poly1305 = { version = "0.10.1", default-features = false, features = ["std"], optional = true } +blake3 = { version = "1.5.0", default-features = false, optional = true } + +# aes feature: +# - Aes128GcmWithSha256 +aes-gcm = { version = "0.10.3", default-features = false, features = ["std", "aes"], optional = true } +hkdf = { version = "0.12.4", default-features = false, features = ["std"], optional = true } +sha2 = { version = "0.10.8", default-features = false, features = ["std"], optional = true } + [dev-dependencies] criterion = "0.5.1" diff --git a/src/coffio.rs b/src/coffio.rs index c60ab53..c511fb5 100644 --- a/src/coffio.rs +++ b/src/coffio.rs @@ -115,6 +115,7 @@ mod tests { ctx } + #[cfg(feature = "chacha")] fn get_ikm_lst_chacha20poly1305_blake3() -> InputKeyMaterialList { InputKeyMaterialList::import( "AQAAAA:AQAAAAEAAAC_vYEw1ujVG5i-CtoPYSzik_6xaAq59odjPm5ij01-e6zz4mUAAAAALJGBiwAAAAAA", @@ -122,6 +123,7 @@ mod tests { .unwrap() } + #[cfg(feature = "aes")] fn get_ikm_lst_aes128gcm_sha256() -> InputKeyMaterialList { InputKeyMaterialList::import( "AQAAAA:AQAAAAIAAAA2lXqTSduZ22J0LiwEhmENjB6pLo0GVKvAQYocJcAAp1f8_2UAAAAAuzDPeAAAAAAA", @@ -130,6 +132,7 @@ mod tests { } #[test] + #[cfg(feature = "chacha")] fn encrypt_decrypt_no_context_chacha20poly1305_blake3() { let lst = get_ikm_lst_chacha20poly1305_blake3(); let key_ctx = get_static_empty_key_ctx(); @@ -151,6 +154,7 @@ mod tests { } #[test] + #[cfg(feature = "aes")] fn encrypt_decrypt_no_context_aes128gcm_sha256() { let lst = get_ikm_lst_aes128gcm_sha256(); let key_ctx = get_static_empty_key_ctx(); @@ -172,6 +176,7 @@ mod tests { } #[test] + #[cfg(feature = "chacha")] fn encrypt_decrypt_with_static_context_chacha20poly1305_blake3() { let lst = get_ikm_lst_chacha20poly1305_blake3(); let key_ctx = get_static_key_ctx(); @@ -193,6 +198,7 @@ mod tests { } #[test] + #[cfg(feature = "aes")] fn encrypt_decrypt_with_static_context_aes128gcm_sha256() { let lst = get_ikm_lst_aes128gcm_sha256(); let key_ctx = get_static_key_ctx(); @@ -214,6 +220,7 @@ mod tests { } #[test] + #[cfg(feature = "chacha")] fn encrypt_decrypt_with_context_chacha20poly1305_blake3() { let lst = get_ikm_lst_chacha20poly1305_blake3(); let key_ctx = KeyContext::from(TEST_KEY_CTX); @@ -235,6 +242,7 @@ mod tests { } #[test] + #[cfg(feature = "aes")] fn encrypt_decrypt_with_context_aes128gcm_sha256() { let lst = get_ikm_lst_aes128gcm_sha256(); let key_ctx = KeyContext::from(TEST_KEY_CTX); @@ -256,6 +264,7 @@ mod tests { } #[test] + #[cfg(feature = "chacha")] fn decrypt_invalid_ciphertext() { let tests = &[ ("", "empty data"), @@ -284,6 +293,7 @@ mod tests { } #[test] + #[cfg(feature = "chacha")] fn invalid_context() { let lst = get_ikm_lst_chacha20poly1305_blake3(); let key_ctx = KeyContext::from(TEST_KEY_CTX); diff --git a/src/error.rs b/src/error.rs index 1f059a4..e4eb2af 100644 --- a/src/error.rs +++ b/src/error.rs @@ -2,6 +2,10 @@ pub(crate) type Result = core::result::Result; #[derive(thiserror::Error, Debug, PartialEq)] pub enum Error { + #[cfg(feature = "aes")] + #[error("cipher error: {0}")] + AesGcmError(aes_gcm::Error), + #[cfg(feature = "chacha")] #[error("cipher error: {0}")] ChaCha20Poly1305Error(chacha20poly1305::Error), #[error("ikm error: no input key material available")] @@ -44,6 +48,14 @@ impl From for Error { } } +#[cfg(all(feature = "aes", not(feature = "chacha")))] +impl From for Error { + fn from(error: aes_gcm::Error) -> Self { + Error::AesGcmError(error) + } +} + +#[cfg(feature = "chacha")] impl From for Error { fn from(error: chacha20poly1305::Error) -> Self { Error::ChaCha20Poly1305Error(error) diff --git a/src/ikm.rs b/src/ikm.rs index bcf6faf..ef6fc0a 100644 --- a/src/ikm.rs +++ b/src/ikm.rs @@ -134,7 +134,7 @@ impl InputKeyMaterial { /// # Examples /// /// ``` -/// use coffio::{InputKeyMaterialList, Scheme}; +/// use coffio::{InputKeyMaterialList, Scheme, DEFAULT_SCHEME}; /// use std::time::{Duration, SystemTime}; /// /// // Create an empty IKM list. @@ -149,7 +149,7 @@ impl InputKeyMaterial { /// let not_before = SystemTime::now(); /// let not_after = not_before + Duration::from_secs(315_569_252); /// let ikm_id_2 = ikml.add_custom_ikm( -/// Scheme::Aes128GcmWithSha256, +/// DEFAULT_SCHEME, /// not_before, /// not_after, /// )?; @@ -219,7 +219,7 @@ impl InputKeyMaterialList { /// # Examples /// /// ``` - /// use coffio::{InputKeyMaterialList, Scheme}; + /// use coffio::{InputKeyMaterialList, Scheme, DEFAULT_SCHEME}; /// use std::time::{Duration, SystemTime}; /// /// let mut ikml = InputKeyMaterialList::new(); @@ -227,7 +227,7 @@ impl InputKeyMaterialList { /// let not_before = SystemTime::now(); /// let not_after = not_before + Duration::from_secs(315_569_252); /// let _ = ikml.add_custom_ikm( - /// Scheme::XChaCha20Poly1305WithBlake3, + /// DEFAULT_SCHEME, /// not_before, /// not_after, /// )?; @@ -319,14 +319,19 @@ impl InputKeyMaterialList { /// Import an IKM list. /// - /// # Examples - /// - /// ``` - /// let stored_ikml = "AQAAAA:AQAAAAEAAAC_vYEw1ujVG5i-CtoPYSzik_6xaAq59odjPm5ij01-e6zz4mUAAAAALJGBiwAAAAAA"; - /// let mut ikml = coffio::InputKeyMaterialList::import(stored_ikml)?; - /// assert_eq!(ikml.len(), 1); - /// # Ok::<(), coffio::Error>(()) - /// ``` + #[cfg_attr( + feature = "chacha", + doc = r##" +# Examples + +``` +let stored_ikml = "AQAAAA:AQAAAAEAAAC_vYEw1ujVG5i-CtoPYSzik_6xaAq59odjPm5ij01-e6zz4mUAAAAALJGBiwAAAAAA"; +let mut ikml = coffio::InputKeyMaterialList::import(stored_ikml)?; +assert_eq!(ikml.len(), 1); +# Ok::<(), coffio::Error>(()) +``` +"## + )] pub fn import(s: &str) -> Result { crate::storage::decode_ikm_list(s) } @@ -376,6 +381,7 @@ mod tests { use std::str::FromStr; #[test] + #[cfg(feature = "chacha")] fn import() { let s = "AQAAAA:AQAAAAEAAAC_vYEw1ujVG5i-CtoPYSzik_6xaAq59odjPm5ij01-e6zz4mUAAAAALJGBiwAAAAAA"; @@ -398,6 +404,7 @@ mod tests { } #[test] + #[cfg(feature = "chacha")] fn from_str() { let s = "AQAAAA:AQAAAAEAAAC_vYEw1ujVG5i-CtoPYSzik_6xaAq59odjPm5ij01-e6zz4mUAAAAALJGBiwAAAAAA"; @@ -460,6 +467,7 @@ mod ikm_management { } #[test] + #[cfg(feature = "chacha")] fn gen_ikm_list() { let mut lst = InputKeyMaterialList::new(); assert_eq!(lst.id_counter, 0); @@ -511,6 +519,7 @@ mod ikm_management { } #[test] + #[cfg(feature = "chacha")] fn import() { let res = InputKeyMaterialList::import(TEST_STR); assert!(res.is_ok(), "res: {res:?}"); @@ -630,6 +639,7 @@ mod ikm_management { } #[test] + #[cfg(feature = "chacha")] fn get_latest_ikm_epoch() { let res = InputKeyMaterialList::import(TEST_STR); assert!(res.is_ok(), "res: {res:?}"); @@ -639,6 +649,7 @@ mod ikm_management { } #[test] + #[cfg(feature = "chacha")] fn get_latest_ikm_1_712_475_802() { let ts = SystemTime::UNIX_EPOCH + Duration::from_secs(1_712_475_802); let res = InputKeyMaterialList::import(TEST_STR); @@ -651,6 +662,7 @@ mod ikm_management { } #[test] + #[cfg(feature = "chacha")] fn get_latest_ikm_1_592_734_902() { let ts = SystemTime::UNIX_EPOCH + Duration::from_secs(1_592_734_902); let res = InputKeyMaterialList::import(TEST_STR); @@ -668,6 +680,7 @@ mod encryption { use super::*; #[test] + #[cfg(feature = "chacha")] fn get_latest_ikm_xchacha20poly1305_blake3() { let mut lst = InputKeyMaterialList::new(); let _ = lst.add_ikm(); @@ -683,6 +696,7 @@ mod encryption { } #[test] + #[cfg(feature = "sha")] fn get_latest_ikm_aes128gcm_sha256() { let mut lst = InputKeyMaterialList::new(); let _ = lst.add_ikm(); diff --git a/src/kdf.rs b/src/kdf.rs index 7cdb1c4..e2dc8db 100644 --- a/src/kdf.rs +++ b/src/kdf.rs @@ -29,6 +29,7 @@ mod tests { ]; #[test] + #[cfg(feature = "chacha")] fn derive_key_no_tp() { let ikm = InputKeyMaterial::from_bytes(TEST_RAW_IKM).unwrap(); let ctx = KeyContext::from(["some", "context"]); @@ -43,6 +44,7 @@ mod tests { } #[test] + #[cfg(feature = "chacha")] fn derive_key_tp_0() { let ikm = InputKeyMaterial::from_bytes(TEST_RAW_IKM).unwrap(); let ctx = KeyContext::from(["some", "context"]); @@ -57,6 +59,7 @@ mod tests { } #[test] + #[cfg(feature = "chacha")] fn derive_key_tp_42() { let ikm = InputKeyMaterial::from_bytes(TEST_RAW_IKM).unwrap(); let ctx = KeyContext::from(["some", "context"]); diff --git a/src/lib.rs b/src/lib.rs index 5e86f04..80f115c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -49,6 +49,9 @@ pub const DEFAULT_IKM_DURATION: u64 = 315_569_252; /// [tropical_year]: https://en.wikipedia.org/wiki/Tropical_year #[cfg(feature = "encryption")] pub const DEFAULT_KEY_CTX_PERIODICITY: u64 = 31_556_925; -/// Default scheme used when adding a new IKM. The value is `XChaCha20Poly1305WithBlake3`. -#[cfg(feature = "ikm-management")] +/// Default scheme used when adding a new IKM. The value is `XChaCha20Poly1305WithBlake3` if the +/// `chacha` feature is enabled, then `Aes128GcmWithSha256` if the `aes` feature is enabled. +#[cfg(all(feature = "ikm-management", feature = "chacha"))] pub const DEFAULT_SCHEME: Scheme = Scheme::XChaCha20Poly1305WithBlake3; +#[cfg(all(feature = "ikm-management", feature = "aes", not(feature = "chacha")))] +pub const DEFAULT_SCHEME: Scheme = Scheme::Aes128GcmWithSha256; diff --git a/src/scheme.rs b/src/scheme.rs index d45341a..4d95aba 100644 --- a/src/scheme.rs +++ b/src/scheme.rs @@ -6,13 +6,13 @@ use crate::error::Result; use crate::kdf::KdfFunction; use crate::Error; -#[cfg(feature = "encryption")] +#[cfg(feature = "aes")] mod aes; -#[cfg(feature = "encryption")] +#[cfg(feature = "chacha")] mod blake3; -#[cfg(feature = "encryption")] +#[cfg(feature = "aes")] mod sha2; -#[cfg(feature = "encryption")] +#[cfg(feature = "chacha")] mod xchacha20poly1305; #[cfg(feature = "encryption")] @@ -53,6 +53,7 @@ pub enum Scheme { /// - Max invocations: no limitation /// - Resources: [RFC 7539](https://doi.org/10.17487/RFC7539) /// [draft-irtf-cfrg-xchacha](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha) + #[cfg(feature = "chacha")] XChaCha20Poly1305WithBlake3 = 1, /// - Key derivation: HKDF-SHA256 /// - Encryption: AES-GCM @@ -61,13 +62,16 @@ pub enum Scheme { /// - Max data size: 64 GB /// - Max invocations: 232 /// - Resources: [NIST SP 800-38D](https://doi.org/10.6028/NIST.SP.800-38D) + #[cfg(feature = "aes")] Aes128GcmWithSha256 = 2, } impl Scheme { pub(crate) fn get_ikm_size(&self) -> usize { match self { + #[cfg(feature = "chacha")] Scheme::XChaCha20Poly1305WithBlake3 => 32, + #[cfg(feature = "aes")] Scheme::Aes128GcmWithSha256 => 32, } } @@ -77,34 +81,36 @@ impl Scheme { impl Scheme { pub(crate) fn get_kdf(&self) -> Box { match self { + #[cfg(feature = "chacha")] Scheme::XChaCha20Poly1305WithBlake3 => Box::new(blake3::blake3_derive), + #[cfg(feature = "aes")] Scheme::Aes128GcmWithSha256 => Box::new(sha2::sha256_derive), } } pub(crate) fn get_decryption(&self) -> Box { match self { - Scheme::XChaCha20Poly1305WithBlake3 => { - Box::new(xchacha20poly1305::xchacha20poly1305_decrypt) - } + #[cfg(feature = "chacha")] + Scheme::XChaCha20Poly1305WithBlake3 => Box::new(xchacha20poly1305::xchacha20poly1305_decrypt), + #[cfg(feature = "aes")] Scheme::Aes128GcmWithSha256 => Box::new(aes::aes128gcm_decrypt), } } pub(crate) fn get_encryption(&self) -> Box { match self { - Scheme::XChaCha20Poly1305WithBlake3 => { - Box::new(xchacha20poly1305::xchacha20poly1305_encrypt) - } + #[cfg(feature = "chacha")] + Scheme::XChaCha20Poly1305WithBlake3 => Box::new(xchacha20poly1305::xchacha20poly1305_encrypt), + #[cfg(feature = "aes")] Scheme::Aes128GcmWithSha256 => Box::new(aes::aes128gcm_encrypt), } } pub(crate) fn get_gen_nonce(&self) -> Box { match self { - Scheme::XChaCha20Poly1305WithBlake3 => { - Box::new(xchacha20poly1305::xchacha20poly1305_gen_nonce) - } + #[cfg(feature = "chacha")] + Scheme::XChaCha20Poly1305WithBlake3 => Box::new(xchacha20poly1305::xchacha20poly1305_gen_nonce), + #[cfg(feature = "aes")] Scheme::Aes128GcmWithSha256 => Box::new(aes::aes128gcm_gen_nonce), } } @@ -115,7 +121,9 @@ impl TryFrom for Scheme { fn try_from(value: SchemeSerializeType) -> Result { match value { + #[cfg(feature = "chacha")] 1 => Ok(Scheme::XChaCha20Poly1305WithBlake3), + #[cfg(feature = "aes")] 2 => Ok(Scheme::Aes128GcmWithSha256), _ => Err(Error::ParsingSchemeUnknownScheme(value)), } diff --git a/src/storage.rs b/src/storage.rs index e2f8958..b82f9eb 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -161,7 +161,7 @@ mod ikm_lst { } #[test] - #[cfg(feature = "ikm-management")] + #[cfg(all(feature = "ikm-management", feature = "chacha"))] fn encode() { use std::time::{Duration, SystemTime}; let bytes_to_system_time = |ts: u64| { @@ -202,6 +202,7 @@ mod ikm_lst { } #[test] + #[cfg(feature = "chacha")] fn decode() { let res = super::decode_ikm_list(TEST_STR); assert!(res.is_ok(), "res: {res:?}");