#![feature(allocator_api)] use std::{ alloc::Global, ffi::{CStr, CString}, io::{Cursor, Write, stdin}, str::FromStr, }; use byteorder::{BigEndian, WriteBytesExt}; use itertools::Itertools; use password_hash::{Key, derive_encryption_key, encrypt_with}; use spectre_api_sys::SpectreAlgorithm; macro_rules! read_line { ($lines:expr, $label:expr) => {{ print!("{}: ", $label); std::io::stdout().flush().unwrap(); CString::from_str( &$lines .next() .transpose() .ok() .flatten() .unwrap_or_else(|| panic!("Expected {}.", $label)), ) .unwrap() }}; } fn main() { let print_secrets = std::env::args().contains("--print-secrets"); let mut lines = stdin().lines(); let username = read_line!(lines, "username"); let secret = read_line!(lines, "secret"); let user_key: Key = unsafe { &*spectre_api_sys::spectre_user_key( username.as_ptr(), secret.as_ptr(), SpectreAlgorithm::Current, ) } .bytes; let user_key_id = unsafe { spectre_api_sys::spectre_id_buf(user_key.as_ptr(), user_key.len()) }; let salt = derive_user_key_salt(&username); let encryption_key = derive_encryption_key(&salt, secret.as_bytes(), Global); if print_secrets { println!("\nUser Key (SECRET):\n{:02x}", user_key.iter().format("")); println!( "\nEncryption Key (SECRET):\n{:02x}", encryption_key.iter().format("") ); } let mut encrypted_user_key = user_key; encrypt_with(&mut encrypted_user_key, &encryption_key); println!( "\nUser Key ID:\n{:02x}", user_key_id.bytes.iter().format("") ); println!( "\nEncrypted User Key:\n{:02x}", encrypted_user_key.iter().format("") ); } // Copied from the body of the `spectre_user_key_v3` function. fn derive_user_key_salt(username: &CStr) -> Vec { use spectre_api_sys::*; let mut salt = Cursor::new(Vec::new()); unsafe { let key_scope: &'static CStr = CStr::from_ptr(spectre_purpose_scope(SpectreKeyPurpose::Authentication)); salt.write_all(key_scope.to_bytes()).unwrap(); salt.write_u32::(username.count_bytes() as u32) .unwrap(); salt.write_all(username.to_bytes()).unwrap(); } salt.into_inner() }