summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpennae <github@quasiparticle.net>2022-07-17 14:13:53 +0200
committerpennae <github@quasiparticle.net>2022-07-17 17:26:05 +0200
commit56499a11ad76afce78f2344ebfcb2b1ce1ee437f (patch)
treeb4ed61651499523fae5a6b1dd62498a5e13bb233
parent5d7f509f1a98c2d45870e3877b4d7bfa756d2d2a (diff)
downloadminor-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.rs17
-rw-r--r--src/api/auth/password.rs9
-rw-r--r--src/crypto.rs77
-rw-r--r--src/types.rs8
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]);