acid/firmware2/password-hash/src/lib.rs
2026-01-19 20:13:25 +01:00

54 lines
2 KiB
Rust

//! 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
}