diff --git a/Cargo.toml b/Cargo.toml index 128b730..cebbf51 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,5 +19,7 @@ i-understand-and-accept-the-risks = [] [dependencies] base64ct = { version = "1.6.0", default-features = false, features = ["std"] } +blake3 = { version = "1.5.0", default-features = false, features = ["std", "traits-preview"] } +digest = { version = "0.10.7", default-features = false, features = ["std"] } getrandom = { version = "0.2.12", default-features = false } thiserror = { version = "1.0.57", default-features = false } diff --git a/src/encryption.rs b/src/encryption.rs index c32d36c..38dc599 100644 --- a/src/encryption.rs +++ b/src/encryption.rs @@ -1,3 +1,4 @@ +use crate::key::derive_key; use crate::{Error, InputKeyMaterialList}; pub fn encrypt( @@ -6,6 +7,8 @@ pub fn encrypt( data: impl AsRef<[u8]>, data_context: &[impl AsRef<[u8]>], ) -> Result { + let ikm = ikml.get_latest_ikm()?; + let key = derive_key(ikm, key_context); unimplemented!("encrypt"); } diff --git a/src/kdf.rs b/src/kdf.rs new file mode 100644 index 0000000..50be145 --- /dev/null +++ b/src/kdf.rs @@ -0,0 +1,9 @@ +pub(crate) fn blake3_derive(context: &[u8], ikm: &[u8]) -> Vec { + // TODO: remove this hack as soon as `blake3::derive_key` accepts bytes + use std::fmt::Write; + let context: String = context.iter().fold(String::new(), |mut output, b| { + let _ = write!(output, "{b:02x}"); + output + }); + blake3::derive_key(&context, ikm).to_vec() +} diff --git a/src/key.rs b/src/key.rs new file mode 100644 index 0000000..833c273 --- /dev/null +++ b/src/key.rs @@ -0,0 +1,69 @@ +use crate::ikm::InputKeyMaterial; +use crate::scheme::Scheme; +use digest::Digest; + +fn canonicalize(scheme: Scheme, key_context: &[impl AsRef<[u8]>]) -> Vec { + match key_context.len() { + 0 => Vec::new(), + 1 => key_context[0].as_ref().into(), + n => { + let mut ret = Vec::with_capacity(n * scheme.key_size()); + for ctx_elem in key_context { + let mut elem_hasher = scheme.canonicalization_hasher(); + elem_hasher.update(ctx_elem.as_ref()); + ret.extend_from_slice(&elem_hasher.finalize()); + } + ret + } + } +} + +pub(crate) fn derive_key(ikm: &InputKeyMaterial, key_context: &[impl AsRef<[u8]>]) -> Vec { + let key_context = canonicalize(ikm.scheme, key_context); + let kdf = ikm.scheme.get_kdf(); + kdf(&key_context, &ikm.content) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::scheme::Scheme; + + const EMPTY_CTX: &[[u8; 0]] = &[]; + + #[test] + fn canonicalize_empty() { + let canon = canonicalize(Scheme::XChaCha20Poly1305WithBlake3, EMPTY_CTX); + assert_eq!(canon, vec![]); + } + + #[test] + fn canonicalize_one() { + let s = "test"; + let canon = canonicalize(Scheme::XChaCha20Poly1305WithBlake3, &[s]); + assert_eq!(canon, s.as_bytes()); + } + + #[test] + fn blake3_canonicalize_many() { + let canon = canonicalize( + Scheme::XChaCha20Poly1305WithBlake3, + &["test", "bis", "ter", ""], + ); + assert_eq!( + canon, + [ + 0x48, 0x78, 0xca, 0x04, 0x25, 0xc7, 0x39, 0xfa, 0x42, 0x7f, 0x7e, 0xda, 0x20, 0xfe, + 0x84, 0x5f, 0x6b, 0x2e, 0x46, 0xba, 0x5f, 0xe2, 0xa1, 0x4d, 0xf5, 0xb1, 0xe3, 0x2f, + 0x50, 0x60, 0x32, 0x15, 0x62, 0x01, 0x8f, 0x96, 0x98, 0x6e, 0x0e, 0x48, 0x8e, 0x07, + 0x4d, 0xa6, 0xb7, 0x28, 0xbc, 0x24, 0x2b, 0xf0, 0xcc, 0x6e, 0x5a, 0x5d, 0x4e, 0x78, + 0x02, 0x14, 0x0a, 0x52, 0xbf, 0xe1, 0x58, 0x86, 0x91, 0x9e, 0x10, 0xc3, 0xe4, 0xbf, + 0x61, 0x2f, 0x36, 0x39, 0xbe, 0x86, 0xb9, 0x34, 0x6b, 0xc3, 0x4a, 0x8c, 0x89, 0x14, + 0xbd, 0x78, 0x9b, 0x47, 0x79, 0xf7, 0xc9, 0x83, 0x32, 0x80, 0x1c, 0x1b, 0xaf, 0x13, + 0x49, 0xb9, 0xf5, 0xf9, 0xa1, 0xa6, 0xa0, 0x40, 0x4d, 0xea, 0x36, 0xdc, 0xc9, 0x49, + 0x9b, 0xcb, 0x25, 0xc9, 0xad, 0xc1, 0x12, 0xb7, 0xcc, 0x9a, 0x93, 0xca, 0xe4, 0x1f, + 0x32, 0x62 + ] + ); + } +} diff --git a/src/lib.rs b/src/lib.rs index 9554e49..e2ef5c5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,10 @@ mod encryption; mod error; #[cfg(any(feature = "encryption", feature = "ikm-management"))] mod ikm; +#[cfg(feature = "encryption")] +mod kdf; +#[cfg(feature = "encryption")] +mod key; #[cfg(any(feature = "encryption", feature = "ikm-management"))] mod scheme; diff --git a/src/scheme.rs b/src/scheme.rs index 27a079f..10aaaef 100644 --- a/src/scheme.rs +++ b/src/scheme.rs @@ -5,6 +5,26 @@ pub enum Scheme { XChaCha20Poly1305WithBlake3 = 1, } +impl Scheme { + pub(crate) fn canonicalization_hasher(&self) -> impl digest::Digest { + match self { + Scheme::XChaCha20Poly1305WithBlake3 => blake3::Hasher::new(), + } + } + + pub(crate) fn get_kdf(&self) -> impl Fn(&[u8], &[u8]) -> Vec { + match self { + Scheme::XChaCha20Poly1305WithBlake3 => crate::kdf::blake3_derive, + } + } + + pub(crate) fn key_size(&self) -> usize { + match self { + Scheme::XChaCha20Poly1305WithBlake3 => 32, + } + } +} + impl TryFrom for Scheme { type Error = Error;