//! scrypt memory requirements scale linearly with parameters `N` and `r`. //! This makes it unsuitable for embedded environments with the parameters //! used in Spectre. //! Our work-around is to derive the Spectre _user key_ using scrypt on the //! host, encrypt it with XOR using a key derived using argon2, which //! has parameters for specifying the memory and time requirements separately. //! This encrypted key is then stored on the keyboard, to be decrypted again. #![cfg_attr(not(feature = "std"), no_std)] #![feature(allocator_api)] extern crate alloc; use alloc::alloc::Allocator; use argon2::{Algorithm, Argon2, ParamsBuilder, Version}; use crate::blocks::Argon2Blocks; pub mod blocks; pub type Key = [u8; 64]; /// KiB used by the argon2 algorithm. /// Lower than the default, to fit in the constrained memory of embedded devices. pub const ARGON2_M_COST: u32 = 1024; /// Compensate the difficulty by increasing the iterations proportionally. pub const ARGON2_T_COST: u32 = argon2::Params::DEFAULT_T_COST * argon2::Params::DEFAULT_M_COST / ARGON2_M_COST; pub const ARGON2_P_COST: u32 = argon2::Params::DEFAULT_P_COST; pub const ARGON2_SALT_PREFIX: &[u8] = b"acid-firmware\0"; pub fn derive_encryption_key(username: &[u8], secret: &[u8], allocator: impl Allocator) -> Key { let argon2 = Argon2::new( Algorithm::Argon2id, Version::default(), ParamsBuilder::default() .m_cost(ARGON2_M_COST) .t_cost(ARGON2_T_COST) .p_cost(ARGON2_P_COST) .build() .unwrap(), ); let mut blocks = Argon2Blocks::new_in(ARGON2_M_COST as usize, &allocator).unwrap(); let mut key: Key = [0u8; _]; // Username is prefixed to form a salt that is long enough for Argon2. let mut salt = Vec::with_capacity_in(username.len() + ARGON2_SALT_PREFIX.len(), &allocator); salt.extend_from_slice(ARGON2_SALT_PREFIX); salt.extend_from_slice(username); argon2 .hash_password_into_with_memory(secret, &salt, &mut key, &mut blocks) .unwrap(); key }