summaryrefslogtreecommitdiff
path: root/src/crypto.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/crypto.rs')
-rw-r--r--src/crypto.rs139
1 files changed, 104 insertions, 35 deletions
diff --git a/src/crypto.rs b/src/crypto.rs
index 617bd7a..7fba9cd 100644
--- a/src/crypto.rs
+++ b/src/crypto.rs
@@ -15,15 +15,23 @@ use scrypt::scrypt;
use serde::{Deserialize, Serialize};
use sha2::Sha256;
+use crate::{
+ serde::as_hex,
+ types::{AccountResetID, KeyFetchID, PasswordChangeID, SessionID},
+};
+
const NAMESPACE: &[u8] = b"identity.mozilla.com/picl/v1/";
+pub fn random_bytes<const N: usize>() -> [u8; N] {
+ let mut result = [0; N];
+ rand::rngs::OsRng.fill_bytes(&mut result);
+ result
+}
+
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(try_from = "String", into = "String")]
pub struct SecretBytes<const N: usize>(pub [u8; N]);
-#[derive(Clone, PartialEq, Eq)]
-pub struct TokenID(pub [u8; 32]);
-
impl<const N: usize> Debug for SecretBytes<N> {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
fmt.write_fmt(format_args!("SecretBytes {{ raw: {} }}", hex::encode(&self.0)))
@@ -73,7 +81,7 @@ mod from_hkdf {
mod private {
pub trait Seal {}
impl<const N: usize> Seal for super::super::SecretBytes<N> {}
- impl Seal for super::super::TokenID {}
+ impl<const N: usize> Seal for [u8; N] {}
impl<L: Seal, R: Seal> Seal for (L, R) {}
}
@@ -90,11 +98,11 @@ mod from_hkdf {
}
}
- impl FromHkdf for super::TokenID {
- const SIZE: usize = 32;
+ impl<const N: usize> FromHkdf for [u8; N] {
+ const SIZE: usize = N;
fn from_hkdf(bytes: &[u8]) -> Self {
#[allow(clippy::expect_used)]
- Self(bytes.try_into().expect("hkdf failed"))
+ bytes.try_into().expect("hkdf failed")
}
}
@@ -166,29 +174,59 @@ impl StretchedPW {
}
}
-pub struct SessionCredentials {
- pub token_id: TokenID,
+#[derive(Clone, Serialize, Deserialize, PartialEq, Eq)]
+pub(crate) struct SessionToken(#[serde(with = "as_hex")] [u8; 32]);
+
+impl Debug for SessionToken {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_tuple("SessionToken").field(&hex::encode(self.0)).finish()
+ }
+}
+
+impl SessionToken {
+ pub fn generate() -> Self {
+ Self(random_bytes())
+ }
+}
+
+pub(crate) struct SessionCredentials {
+ pub token_id: SessionID,
pub req_hmac_key: SecretBytes<32>,
}
impl SessionCredentials {
- pub fn derive(seed: &SecretBytes<32>) -> Self {
+ pub fn derive_from(seed: &SessionToken) -> Self {
let (token_id, req_hmac_key) = from_hkdf(&seed.0, &[NAMESPACE, b"sessionToken"]);
- Self { token_id, req_hmac_key }
+ Self { token_id: SessionID(token_id), req_hmac_key }
+ }
+}
+
+#[derive(Clone, Serialize, Deserialize, PartialEq, Eq)]
+pub(crate) struct KeyFetchToken(#[serde(with = "as_hex")] [u8; 32]);
+
+impl Debug for KeyFetchToken {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_tuple("KeyFetchToken").field(&hex::encode(self.0)).finish()
+ }
+}
+
+impl KeyFetchToken {
+ pub fn generate() -> Self {
+ Self(random_bytes())
}
}
-pub struct KeyFetchReq {
- pub token_id: TokenID,
+pub(crate) struct KeyFetchReq {
+ pub token_id: KeyFetchID,
pub req_hmac_key: SecretBytes<32>,
key_request_key: SecretBytes<32>,
}
impl KeyFetchReq {
- pub fn from_token(key_fetch_token: &SecretBytes<32>) -> Self {
+ pub fn derive_from(key_fetch_token: &KeyFetchToken) -> Self {
let (token_id, (req_hmac_key, key_request_key)) =
from_hkdf(&key_fetch_token.0, &[NAMESPACE, b"keyFetchToken"]);
- Self { token_id, req_hmac_key, key_request_key }
+ Self { token_id: KeyFetchID(token_id), req_hmac_key, key_request_key }
}
pub fn derive_resp(&self) -> KeyFetchResp {
@@ -243,32 +281,62 @@ impl WrappedKeyBundle {
}
}
-pub struct PasswordChangeReq {
- pub token_id: TokenID,
+#[derive(Clone, Serialize, Deserialize, PartialEq, Eq)]
+pub(crate) struct PasswordChangeToken(#[serde(with = "as_hex")] [u8; 32]);
+
+impl Debug for PasswordChangeToken {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_tuple("PasswordChangeToken").field(&hex::encode(self.0)).finish()
+ }
+}
+
+impl PasswordChangeToken {
+ pub fn generate() -> Self {
+ Self(random_bytes())
+ }
+}
+
+pub(crate) struct PasswordChangeReq {
+ pub token_id: PasswordChangeID,
pub req_hmac_key: SecretBytes<32>,
}
impl PasswordChangeReq {
- pub fn from_change_token(token: &SecretBytes<32>) -> Self {
+ pub fn derive_from_change_token(token: &PasswordChangeToken) -> Self {
let (token_id, req_hmac_key) = from_hkdf(&token.0, &[NAMESPACE, b"passwordChangeToken"]);
- Self { token_id, req_hmac_key }
+ Self { token_id: PasswordChangeID(token_id), req_hmac_key }
}
- pub fn from_forgot_token(token: &SecretBytes<32>) -> Self {
+ pub fn derive_from_forgot_token(token: &PasswordChangeToken) -> Self {
let (token_id, req_hmac_key) = from_hkdf(&token.0, &[NAMESPACE, b"passwordForgotToken"]);
- Self { token_id, req_hmac_key }
+ Self { token_id: PasswordChangeID(token_id), req_hmac_key }
+ }
+}
+
+#[derive(Clone, Serialize, Deserialize, PartialEq, Eq)]
+pub(crate) struct AccountResetToken(#[serde(with = "as_hex")] [u8; 32]);
+
+impl Debug for AccountResetToken {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_tuple("AccountResetToken").field(&hex::encode(self.0)).finish()
+ }
+}
+
+impl AccountResetToken {
+ pub fn generate() -> Self {
+ Self(random_bytes())
}
}
-pub struct AccountResetReq {
- pub token_id: TokenID,
+pub(crate) struct AccountResetReq {
+ pub token_id: AccountResetID,
pub req_hmac_key: SecretBytes<32>,
}
impl AccountResetReq {
- pub fn from_token(token: &SecretBytes<32>) -> Self {
+ pub fn derive_from(token: &AccountResetToken) -> Self {
let (token_id, req_hmac_key) = from_hkdf(&token.0, &[NAMESPACE, b"accountResetToken"]);
- Self { token_id, req_hmac_key }
+ Self { token_id: AccountResetID(token_id), req_hmac_key }
}
}
@@ -278,7 +346,8 @@ mod test {
use password_hash::{Output, SaltString};
use crate::crypto::{
- AccountResetReq, KeyBundle, KeyFetchReq, PasswordChangeReq, SessionCredentials,
+ AccountResetReq, AccountResetToken, KeyBundle, KeyFetchReq, KeyFetchToken,
+ PasswordChangeReq, PasswordChangeToken, SessionCredentials, SessionToken,
};
use super::{AuthPW, SecretBytes};
@@ -291,7 +360,7 @@ mod test {
#[test]
fn test_derive_session() {
- let creds = SessionCredentials::derive(&SecretBytes(hex!(
+ let creds = SessionCredentials::derive_from(&SessionToken(hex!(
"a0a1a2a3a4a5a6a7 a8a9aaabacadaeaf b0b1b2b3b4b5b6b7 b8b9babbbcbdbebf"
)));
assert_eq!(
@@ -306,9 +375,9 @@ mod test {
#[test]
fn test_key_fetch() {
- let key_fetch = KeyFetchReq::from_token(&shex!(
+ let key_fetch = KeyFetchReq::derive_from(&KeyFetchToken(hex!(
"8081828384858687 88898a8b8c8d8e8f 9091929394959697 98999a9b9c9d9e9f"
- ));
+ )));
assert_eq!(
key_fetch.token_id.0,
hex!("3d0a7c02a15a62a2882f76e39b6494b500c022a8816e048625a495718998ba60")
@@ -404,9 +473,9 @@ mod test {
#[test]
fn test_password_change() {
- let req = PasswordChangeReq::from_change_token(&shex!(
+ let req = PasswordChangeReq::derive_from_change_token(&PasswordChangeToken(hex!(
"0000000000000000 0000000000000000 0000000000000000 0000000000000000"
- ));
+ )));
assert_eq!(
req.token_id.0,
hex!("5a9f93f66c26fd1c 1ea9826fafc422e9 4b9c9f833cd2bfa5 da18c8d3317224aa")
@@ -419,9 +488,9 @@ mod test {
#[test]
fn test_password_forgot() {
- let req = PasswordChangeReq::from_forgot_token(&shex!(
+ let req = PasswordChangeReq::derive_from_forgot_token(&PasswordChangeToken(hex!(
"0000000000000000 0000000000000000 0000000000000000 0000000000000000"
- ));
+ )));
assert_eq!(
req.token_id.0,
hex!("570e79050fd157a9 b8e7d7d6f88a3f67 e36207c5dfabe7d8 a80994502a624e07")
@@ -434,9 +503,9 @@ mod test {
#[test]
fn test_account_reset() {
- let req = AccountResetReq::from_token(&shex!(
+ let req = AccountResetReq::derive_from(&AccountResetToken(hex!(
"0000000000000000 0000000000000000 0000000000000000 0000000000000000"
- ));
+ )));
assert_eq!(
req.token_id.0,
hex!("8ade842449ab0285 e7b22de9d428cd5b 3c38ea0aa78e2956 a6a69ec66818d864")