diff --git a/components/fxa-client/src/internal/auth.rs b/components/fxa-client/src/internal/auth.rs deleted file mode 100644 index d2aefc123f..0000000000 --- a/components/fxa-client/src/internal/auth.rs +++ /dev/null @@ -1,258 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -use super::{http_client, util::Xorable, Config}; -pub use crate::AuthorizationParameters; -use crate::{Result, ScopedKey}; -use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine}; -pub use http_client::{ - derive_auth_key_from_session_token, send_authorization_request, send_verification, - AuthorizationRequestParameters, -}; -use jwcrypto::{EncryptionAlgorithm, EncryptionParameters}; -use rc_crypto::{digest, hkdf, hmac, pbkdf2}; -use serde_derive::{Deserialize, Serialize}; -use std::collections::HashMap; - -pub fn get_sync_keys( - config: &Config, - key_fetch_token: &str, - email: &str, - pw: &str, -) -> Result<(Vec, Vec)> { - let acct_keys = get_account_keys(config, key_fetch_token)?; - let wrap_kb = &acct_keys[32..]; - let sync_key = derive_sync_key(email, pw, wrap_kb)?; - let xcs_key = derive_xcs_key(email, pw, wrap_kb)?; - Ok((sync_key, xcs_key)) -} - -pub fn create_keys_jwe( - client_id: &str, - scope: &str, - jwk: &str, - auth_key: &[u8], - config: &Config, - acct_keys: (&[u8], &[u8]), -) -> anyhow::Result { - let scoped: HashMap = - get_scoped_keys(scope, client_id, auth_key, config, acct_keys)?; - let scoped = serde_json::to_string(&scoped)?; - let scoped = scoped.as_bytes(); - let jwk = serde_json::from_str(jwk)?; - let res = jwcrypto::encrypt_to_jwe( - scoped, - EncryptionParameters::ECDH_ES { - enc: EncryptionAlgorithm::A256GCM, - peer_jwk: &jwk, - }, - )?; - Ok(res) -} - -#[derive(Serialize, Deserialize)] -struct Epk { - crv: String, - kty: String, - x: String, - y: String, -} - -fn kwe(name: &str, email: &str) -> Vec { - format!("identity.mozilla.com/picl/v1/{}:{}", name, email) - .as_bytes() - .to_vec() -} - -fn kw(name: &str) -> Vec { - format!("identity.mozilla.com/picl/v1/{}", name) - .as_bytes() - .to_vec() -} - -pub fn get_scoped_keys( - scope: &str, - client_id: &str, - auth_key: &[u8], - config: &Config, - acct_keys: (&[u8], &[u8]), -) -> anyhow::Result> { - let key_data = http_client::get_scoped_key_data_response(scope, client_id, auth_key, config)?; - let mut scoped_keys: HashMap = HashMap::new(); - key_data - .as_object() - .ok_or_else(|| anyhow::Error::msg("Key data not an object"))? - .keys() - .try_for_each(|key| -> anyhow::Result<()> { - let val = key_data - .as_object() - .ok_or_else(|| anyhow::Error::msg("Key data not an object"))? - .get(key) - .ok_or_else(|| anyhow::Error::msg("Key does not exist"))?; - scoped_keys.insert(key.clone(), get_key_for_scope(key, val, acct_keys)?); - Ok(()) - })?; - Ok(scoped_keys) -} - -fn get_key_for_scope( - key: &str, - val: &serde_json::Value, - acct_keys: (&[u8], &[u8]), -) -> anyhow::Result { - let (sync_key, xcs_key) = acct_keys; - let sync_key = URL_SAFE_NO_PAD.encode(sync_key); - let xcs_key = URL_SAFE_NO_PAD.encode(xcs_key); - let kid = format!( - "{}-{}", - val.as_object() - .ok_or_else(|| anyhow::Error::msg("Json is not an object"))? - .get("keyRotationTimestamp") - .ok_or_else(|| anyhow::Error::msg("Key rotation timestamp doesn't exist"))? - .as_u64() - .ok_or_else(|| anyhow::Error::msg("Key rotation timestamp is not a number"))?, - xcs_key - ); - Ok(ScopedKey { - scope: key.to_string(), - kid, - k: sync_key, - kty: "oct".to_string(), - }) -} - -fn derive_xcs_key(email: &str, pwd: &str, wrap_kb: &[u8]) -> Result> { - let unwrap_kb = derive_unwrap_kb(email, pwd)?; - let kb = xored(wrap_kb, &unwrap_kb)?; - Ok(sha256(&kb)?[0..16].into()) -} - -fn sha256(kb: &[u8]) -> Result> { - let ret = digest::digest(&digest::SHA256, kb)?; - let ret: &[u8] = ret.as_ref(); - Ok(ret.to_vec()) -} - -fn derive_hkdf_sha256_key(ikm: &[u8], salt: &[u8], info: &[u8], len: usize) -> Result> { - let salt = hmac::SigningKey::new(&digest::SHA256, salt); - let mut out = vec![0u8; len]; - hkdf::extract_and_expand(&salt, ikm, info, &mut out)?; - Ok(out) -} - -fn quick_stretch_pwd(email: &str, pwd: &str) -> Result> { - let salt = kwe("quickStretch", email); - let mut out = [0u8; 32]; - pbkdf2::derive( - pwd.as_bytes(), - &salt, - 1000, - pbkdf2::HashAlgorithm::SHA256, - &mut out, - )?; - Ok(out.to_vec()) -} - -pub fn auth_pwd(email: &str, pwd: &str) -> Result { - let stretched = quick_stretch_pwd(email, pwd)?; - let salt = b""; - let context = kw("authPW"); - let derived = derive_hkdf_sha256_key(&stretched, salt, &context, 32)?; - Ok(hex::encode(derived)) -} - -#[derive(Serialize, Deserialize)] -struct Credentials { - key: Vec, - id: Vec, - extra: Vec, - out: Vec, -} - -fn derive_hawk_credentials(token_hex: &str, context: &str, size: usize) -> Result { - let token = hex::decode(token_hex)?; - let out = derive_hkdf_sha256_key(&token, &[0u8; 0], &kw(context), size)?; - let key = out[32..64].to_vec(); - let extra = out[64..].to_vec(); - Ok(Credentials { - key, - id: out[0..32].to_vec(), - extra, - out: out.to_vec(), - }) -} - -fn xored(a: &[u8], b: &[u8]) -> Result> { - a.xored_with(b) -} - -fn derive_unwrap_kb(email: &str, pwd: &str) -> Result> { - let stretched_pw = quick_stretch_pwd(email, pwd)?; - let out = derive_hkdf_sha256_key(&stretched_pw, &[0u8; 0], &kw("unwrapBkey"), 32)?; - Ok(out) -} - -fn derive_sync_key(email: &str, pwd: &str, wrap_kb: &[u8]) -> Result> { - let unwrap_kb = derive_unwrap_kb(email, pwd)?; - let kb = xored(wrap_kb, &unwrap_kb)?; - derive_hkdf_sha256_key( - &kb, - &[0u8; 0], - "identity.mozilla.com/picl/v1/oldsync".as_bytes(), - 64, - ) -} - -fn get_account_keys(config: &Config, key_fetch_token: &str) -> Result> { - let creds = derive_hawk_credentials(key_fetch_token, "keyFetchToken", 96)?; - let key_request_key = &creds.extra[0..32]; - let more_creds = derive_hkdf_sha256_key(key_request_key, &[0u8; 0], &kw("account/keys"), 96)?; - let _resp_hmac_key = &more_creds[0..32]; - let resp_xor_key = &more_creds[32..96]; - let bundle = http_client::get_keys_bundle(config, &creds.out)?; - // Missing MAC matching since this is only for tests - xored(resp_xor_key, &bundle[0..64]) -} - -#[cfg(test)] -mod tests { - - // Test vectors used from https://github.com/mozilla/fxa-auth-server/wiki/onepw-protocol#test-vectors - use super::*; - const EMAIL: &str = "andré@example.org"; - const PASSWORD: &str = "pässwörd"; - #[test] - fn test_derive_quick_stretch() { - let qs = quick_stretch_pwd(EMAIL, PASSWORD).unwrap(); - let expected = "e4e8889bd8bd61ad6de6b95c059d56e7b50dacdaf62bd84644af7e2add84345d"; - assert_eq!(expected, hex::encode(qs)); - } - - #[test] - fn test_auth_pw() { - let auth_pw = auth_pwd(EMAIL, PASSWORD).unwrap(); - let expected = "247b675ffb4c46310bc87e26d712153abe5e1c90ef00a4784594f97ef54f2375"; - assert_eq!(auth_pw, expected); - } - - #[test] - fn test_derive_unwrap_kb() { - let unwrap_kb = derive_unwrap_kb(EMAIL, PASSWORD).unwrap(); - let expected = "de6a2648b78284fcb9ffa81ba95803309cfba7af583c01a8a1a63e567234dd28"; - assert_eq!(hex::encode(unwrap_kb), expected); - } - - #[test] - fn test_kb() { - let wrap_kb = - hex::decode("7effe354abecbcb234a8dfc2d7644b4ad339b525589738f2d27341bb8622ecd8") - .unwrap(); - let unwrap_kb = - hex::decode("de6a2648b78284fcb9ffa81ba95803309cfba7af583c01a8a1a63e567234dd28") - .unwrap(); - let kb = xored(&wrap_kb, &unwrap_kb).unwrap(); - let expected = "a095c51c1c6e384e8d5777d97e3c487a4fc2128a00ab395a73d57fedf41631f0"; - assert_eq!(expected, hex::encode(kb)); - } -}