diff options
author | pennae <github@quasiparticle.net> | 2022-07-17 14:13:53 +0200 |
---|---|---|
committer | pennae <github@quasiparticle.net> | 2022-07-17 17:26:05 +0200 |
commit | 56499a11ad76afce78f2344ebfcb2b1ce1ee437f (patch) | |
tree | b4ed61651499523fae5a6b1dd62498a5e13bb233 | |
parent | 5d7f509f1a98c2d45870e3877b4d7bfa756d2d2a (diff) | |
download | minor-skulk-56499a11ad76afce78f2344ebfcb2b1ce1ee437f.tar.gz minor-skulk-56499a11ad76afce78f2344ebfcb2b1ce1ee437f.tar.xz minor-skulk-56499a11ad76afce78f2344ebfcb2b1ce1ee437f.zip |
use SecretKey for key material in crypto
-rw-r--r-- | src/api/auth/account.rs | 17 | ||||
-rw-r--r-- | src/api/auth/password.rs | 9 | ||||
-rw-r--r-- | src/crypto.rs | 77 | ||||
-rw-r--r-- | src/types.rs | 8 |
4 files changed, 58 insertions, 53 deletions
diff --git a/src/api/auth/account.rs b/src/api/auth/account.rs index bff2a66..56ec717 100644 --- a/src/api/auth/account.rs +++ b/src/api/auth/account.rs @@ -90,8 +90,8 @@ pub(crate) async fn create( }, } - let ka = SecretBytes::generate(); - let wrapwrap_kb = SecretBytes::generate(); + let ka = SecretKey::generate(); + let wrapwrap_kb = SecretKey::generate(); let auth_salt = SaltString::generate(rand::rngs::OsRng); let stretched = data.authPW.stretch(auth_salt.as_salt())?; let verify_hash = stretched.verify_hash(); @@ -113,8 +113,8 @@ pub(crate) async fn create( .add_user(User { auth_salt, email: data.email.to_owned(), - ka: SecretKey(ka.0), - wrapwrap_kb: SecretKey(wrapwrap_kb.0), + ka, + wrapwrap_kb, verify_hash: VerifyHash(verify_hash), display_name: None, verified: false, @@ -204,8 +204,8 @@ pub(crate) async fn login( let key_fetch_token = KeyFetchToken::generate(); let req = KeyFetchReq::derive_from(&key_fetch_token); let wrapped = req.derive_resp().wrap_keys(&KeyBundle { - ka: SecretBytes(user.ka.0), - wrap_kb: stretched.decrypt_wwkb(&SecretBytes(user.wrapwrap_kb.0)), + ka: user.ka, + wrap_kb: stretched.decrypt_wwkb(&user.wrapwrap_kb), }); db.add_key_fetch(req.token_id, &req.req_hmac_key, &wrapped).await?; Some(key_fetch_token) @@ -380,13 +380,12 @@ pub(crate) async fn reset( let notify_devs = db.get_devices(&data.context).await?; - let wrapwrap_kb = SecretBytes::generate(); + let wrapwrap_kb = SecretKey::generate(); let auth_salt = SaltString::generate(rand::rngs::OsRng); let stretched = data.body.authPW.stretch(auth_salt.as_salt())?; let verify_hash = stretched.verify_hash(); - db.reset_user_auth(&data.context, auth_salt, SecretKey(wrapwrap_kb.0), VerifyHash(verify_hash)) - .await?; + db.reset_user_auth(&data.context, auth_salt, wrapwrap_kb, VerifyHash(verify_hash)).await?; defer.spawn_after_success("api::auth/account/reset(post)", { let client = Arc::clone(client); diff --git a/src/api/auth/password.rs b/src/api/auth/password.rs index e389261..79b7587 100644 --- a/src/api/auth/password.rs +++ b/src/api/auth/password.rs @@ -63,10 +63,9 @@ pub(crate) async fn change_start( let change_req = PasswordChangeReq::derive_from_change_token(&change_token); let key_fetch_token = KeyFetchToken::generate(); let key_req = KeyFetchReq::derive_from(&key_fetch_token); - let wrapped = key_req.derive_resp().wrap_keys(&KeyBundle { - ka: SecretBytes(user.ka.0), - wrap_kb: stretched.decrypt_wwkb(&SecretBytes(user.wrapwrap_kb.0)), - }); + let wrapped = key_req + .derive_resp() + .wrap_keys(&KeyBundle { ka: user.ka, wrap_kb: stretched.decrypt_wwkb(&user.wrapwrap_kb) }); db.add_key_fetch(key_req.token_id, &key_req.req_hmac_key, &wrapped).await?; db.add_password_change(&uid, &change_req.token_id, &change_req.req_hmac_key, None).await?; @@ -106,7 +105,7 @@ impl<const IS_FORGOT: bool> AuthSource for WithChangeToken<IS_FORGOT> { #[allow(non_snake_case)] pub(crate) struct ChangeFinishReq { authPW: AuthPW, - wrapKb: SecretBytes<32>, + wrapKb: SecretKey, // MISSING sessionToken } diff --git a/src/crypto.rs b/src/crypto.rs index 4413663..d681e86 100644 --- a/src/crypto.rs +++ b/src/crypto.rs @@ -17,7 +17,7 @@ use sha2::Sha256; use crate::{ serde::as_hex, - types::{AccountResetID, HawkKey, KeyFetchID, PasswordChangeID, SessionID}, + types::{AccountResetID, HawkKey, KeyFetchID, PasswordChangeID, SecretKey, SessionID}, }; const NAMESPACE: &[u8] = b"identity.mozilla.com/picl/v1/"; @@ -28,6 +28,14 @@ pub fn random_bytes<const N: usize>() -> [u8; N] { result } +fn xor<const N: usize>(l: &[u8; N], r: &[u8; N]) -> [u8; N] { + let mut result = *l; + for (a, b) in result.iter_mut().zip(r.iter()) { + *a ^= b; + } + result +} + #[derive(Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(try_from = "String", into = "String")] pub struct SecretBytes<const N: usize>(pub [u8; N]); @@ -38,16 +46,6 @@ impl<const N: usize> Debug for SecretBytes<N> { } } -impl<const N: usize> SecretBytes<N> { - fn xor(&self, other: &Self) -> Self { - let mut result = self.clone(); - for (a, b) in result.0.iter_mut().zip(other.0.iter()) { - *a ^= b; - } - result - } -} - impl<const N: usize> From<SecretBytes<N>> for String { fn from(sb: SecretBytes<N>) -> Self { hex::encode(&sb.0) @@ -136,11 +134,11 @@ impl<const N: usize> SecretBytes<N> { #[derive(Debug, Deserialize, Serialize)] #[serde(transparent)] -pub struct AuthPW { +pub(crate) struct AuthPW { pw: SecretBytes<32>, } -pub struct StretchedPW { +pub(crate) struct StretchedPW { pw: Output, } @@ -161,16 +159,16 @@ impl StretchedPW { raw.into() } - fn wrap_wrap_key(&self) -> SecretBytes<32> { + fn wrap_wrap_key(&self) -> [u8; 32] { from_hkdf(self.pw.as_bytes(), &[NAMESPACE, b"wrapwrapKey"]) } - pub fn decrypt_wwkb(&self, wwkb: &SecretBytes<32>) -> SecretBytes<32> { - wwkb.xor(&self.wrap_wrap_key()) + pub fn decrypt_wwkb(&self, wwkb: &SecretKey) -> SecretKey { + SecretKey(xor(&wwkb.0, &self.wrap_wrap_key())) } - pub fn rewrap_wkb(&self, wkb: &SecretBytes<32>) -> SecretBytes<32> { - wkb.xor(&self.wrap_wrap_key()) + pub fn rewrap_wkb(&self, wkb: &SecretKey) -> SecretKey { + SecretKey(xor(&wkb.0, &self.wrap_wrap_key())) } } @@ -240,25 +238,25 @@ impl KeyFetchReq { } } -pub struct KeyFetchResp { +pub(crate) struct KeyFetchResp { resp_hmac_key: SecretBytes<32>, resp_xor_key: SecretBytes<64>, } impl KeyFetchResp { pub fn wrap_keys(&self, keys: &KeyBundle) -> WrappedKeyBundle { - let ciphertext = self.resp_xor_key.xor(&keys.to_bytes()); + let ciphertext = xor(&self.resp_xor_key.0, &keys.to_bytes().0); #[allow(clippy::unwrap_used)] let mut hmac = Hmac::<Sha256>::new_from_slice(&self.resp_hmac_key.0).unwrap(); - hmac.update(&ciphertext.0); + hmac.update(&ciphertext); let hmac = *hmac.finalize().into_bytes().as_ref(); WrappedKeyBundle { ciphertext, hmac } } } -pub struct KeyBundle { - pub ka: SecretBytes<32>, - pub wrap_kb: SecretBytes<32>, +pub(crate) struct KeyBundle { + pub ka: SecretKey, + pub wrap_kb: SecretKey, } impl KeyBundle { @@ -272,14 +270,14 @@ impl KeyBundle { #[derive(Debug)] pub struct WrappedKeyBundle { - ciphertext: SecretBytes<64>, + ciphertext: [u8; 64], hmac: [u8; 32], } impl WrappedKeyBundle { pub fn to_bytes(&self) -> [u8; 96] { let mut result = [0; 96]; - result[0..64].copy_from_slice(&self.ciphertext.0); + result[0..64].copy_from_slice(&self.ciphertext); result[64..].copy_from_slice(&self.hmac); result } @@ -349,19 +347,16 @@ mod test { use hex_literal::hex; use password_hash::{Output, SaltString}; - use crate::crypto::{ - AccountResetReq, AccountResetToken, KeyBundle, KeyFetchReq, KeyFetchToken, - PasswordChangeReq, PasswordChangeToken, SessionCredentials, SessionToken, + use crate::{ + crypto::{ + AccountResetReq, AccountResetToken, KeyBundle, KeyFetchReq, KeyFetchToken, + PasswordChangeReq, PasswordChangeToken, SessionCredentials, SessionToken, + }, + types::SecretKey, }; use super::{AuthPW, SecretBytes}; - macro_rules! shex { - ( $s: literal ) => { - SecretBytes(hex!($s)) - }; - } - #[test] fn test_derive_session() { let creds = SessionCredentials::derive_from(&SessionToken(hex!( @@ -409,8 +404,12 @@ mod test { ); let bundle = KeyBundle { - ka: shex!("2021222324252627 28292a2b2c2d2e2f 3031323334353637 38393a3b3c3d3e3f"), - wrap_kb: shex!("7effe354abecbcb2 34a8dfc2d7644b4a d339b525589738f2 d27341bb8622ecd8"), + ka: SecretKey(hex!( + "2021222324252627 28292a2b2c2d2e2f 3031323334353637 38393a3b3c3d3e3f" + )), + wrap_kb: SecretKey(hex!( + "7effe354abecbcb2 34a8dfc2d7644b4a d339b525589738f2 d27341bb8622ecd8" + )), }; assert_eq!( bundle.to_bytes().0, @@ -422,7 +421,7 @@ mod test { let wrapped = resp.wrap_keys(&bundle); assert_eq!( - wrapped.ciphertext.0, + wrapped.ciphertext, hex!( "ee5c58845c7c9412 b11bbd20920c2fdd d83c33c9cd2c2de2 d66b222613364636 fc7e59d854d599f1 0e212801de3a47c3 4333f3b838ee3471 e0f285649c332bbb" @@ -469,7 +468,7 @@ mod test { ); assert_eq!( - stretched.wrap_wrap_key().0, + stretched.wrap_wrap_key(), hex!("3ebea117efa9faf5 7ce195899b290505 8368e7760cc26ea5 8a2a1be0da7fb287") ); Ok(()) diff --git a/src/types.rs b/src/types.rs index aca74cf..342deab 100644 --- a/src/types.rs +++ b/src/types.rs @@ -226,6 +226,14 @@ impl OauthAuthorizationID { } } +impl SecretKey { + pub fn generate() -> Self { + let mut result = Self([0; 32]); + OsRng.fill_bytes(&mut result.0); + result + } +} + #[derive(Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(try_from = "String", into = "String")] pub(crate) struct OauthToken([u8; 32]); |