diff --git a/firmware2/.cargo/config.toml b/firmware2/.cargo/config.toml index e078e91..356f71f 100644 --- a/firmware2/.cargo/config.toml +++ b/firmware2/.cargo/config.toml @@ -16,6 +16,7 @@ LIBXKBCOMMON_BUILD_DIR = "libxkbcommon/build" SPECTRE_API_BUILD_DIR = "spectre-api/build" SPECTRE_API_SYS_CC = "xtensa-esp32s3-elf-cc.exe" ESP_LOG = "warn" +ESP_BACKTRACE_CONFIG_BACKTRACE_FRAMES = "20" # This is overkill, but we can afford it. SLINT_FONT_SIZES = "8,11,10,12,13,14,15,16,18,20,22,24,32" @@ -23,8 +24,8 @@ SLINT_FONT_SIZES = "8,11,10,12,13,14,15,16,18,20,22,24,32" # Needed for nightly, until llvm upstream has support for Rust Xtensa. # TODO: RE-ENABLE WHEN acid-firmware IS MOVED TO ITS OWN SUBDIRECTORY. # For now, `-Zbuild-std="core,alloc"` can be used instead. -# [unstable] -# build-std = ["alloc", "core"] +[unstable] +build-std = ["alloc", "core"] [patch."https://github.com/Limeth/spectre-api-sys"] spectre-api-sys = { path = "../../../rust/spectre-api-sys" } diff --git a/firmware2/Cargo.toml b/firmware2/Cargo.toml index 79196d0..4c86812 100644 --- a/firmware2/Cargo.toml +++ b/firmware2/Cargo.toml @@ -77,7 +77,7 @@ enumset = "1.1.10" printf-compat = { version = "0.2.1", default-features = false } spectre-api-sys = { git = "https://github.com/Limeth/spectre-api-sys" } sha2 = { version = "0.10.9", default-features = false } -password-hash = { path = "password-hash" } +password-hash = { path = "password-hash", default-features = false } # Crates for serial UART CLI embedded-cli = { version = "0.2.1", default-features = false, features = ["help", "macros"] } diff --git a/firmware2/password-hash/build.rs b/firmware2/password-hash/build.rs index 0614601..c279522 100644 --- a/firmware2/password-hash/build.rs +++ b/firmware2/password-hash/build.rs @@ -1,27 +1,30 @@ -use std::{env, path::PathBuf}; - fn main() { - let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); - println!( - "cargo:rustc-link-search=native={}", - manifest_dir.join("../spectre-api-c/build-host").display() - ); - println!("cargo:rustc-link-lib=static=spectre"); - println!( - "cargo:rerun-if-changed={}", - manifest_dir - .join("../spectre-api-c/build-host/libspectre.a") - .display() - ); + #[cfg(feature = "cmd")] + { + use std::{env, path::PathBuf}; - let libsodium_install_dir = PathBuf::from(env::var("LIBSODIUM_INSTALL_DIR").unwrap()); - println!( - "cargo:rustc-link-search=native={}", - libsodium_install_dir.join("lib").display() - ); - println!("cargo:rustc-link-lib=static=sodium"); - println!( - "cargo:rerun-if-changed={}", - libsodium_install_dir.join("lib/libsodium.a").display() - ); + let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); + println!( + "cargo:rustc-link-search=native={}", + manifest_dir.join("../spectre-api-c/build-host").display() + ); + println!("cargo:rustc-link-lib=static=spectre"); + println!( + "cargo:rerun-if-changed={}", + manifest_dir + .join("../spectre-api-c/build-host/libspectre.a") + .display() + ); + + let libsodium_install_dir = PathBuf::from(env::var("LIBSODIUM_INSTALL_DIR").unwrap()); + println!( + "cargo:rustc-link-search=native={}", + libsodium_install_dir.join("lib").display() + ); + println!("cargo:rustc-link-lib=static=sodium"); + println!( + "cargo:rerun-if-changed={}", + libsodium_install_dir.join("lib/libsodium.a").display() + ); + } } diff --git a/firmware2/password-hash/src/bin.rs b/firmware2/password-hash/src/bin.rs index af7b6e4..294b7ef 100644 --- a/firmware2/password-hash/src/bin.rs +++ b/firmware2/password-hash/src/bin.rs @@ -40,7 +40,8 @@ fn main() { SpectreAlgorithm::Current, ) }; - let encryption_key = derive_encryption_key(username.as_bytes(), secret.as_bytes(), Global); + let salt = username.as_bytes(); // TODO: Derive the same salt that spectre passes to scrypt. + let encryption_key = derive_encryption_key(salt, secret.as_bytes(), Global); let mut encrypted_user_key = encryption_key; assert_eq!(encrypted_user_key.len(), user_key.bytes.len()); diff --git a/firmware2/password-hash/src/lib.rs b/firmware2/password-hash/src/lib.rs index ee17948..2ae53c8 100644 --- a/firmware2/password-hash/src/lib.rs +++ b/firmware2/password-hash/src/lib.rs @@ -11,6 +11,7 @@ extern crate alloc; use alloc::alloc::Allocator; +use alloc::vec::Vec; use argon2::{Algorithm, Argon2, ParamsBuilder, Version}; use crate::blocks::Argon2Blocks; @@ -28,7 +29,11 @@ pub const ARGON2_T_COST: u32 = 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 { +pub fn derive_encryption_key( + unprefixed_salt: &[u8], + secret: &[u8], + allocator: impl Allocator, +) -> Key { let argon2 = Argon2::new( Algorithm::Argon2id, Version::default(), @@ -41,10 +46,11 @@ pub fn derive_encryption_key(username: &[u8], secret: &[u8], allocator: impl All ); 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 is prefixed to form a salt that is long enough for Argon2. + let mut salt = + Vec::with_capacity_in(unprefixed_salt.len() + ARGON2_SALT_PREFIX.len(), &allocator); salt.extend_from_slice(ARGON2_SALT_PREFIX); - salt.extend_from_slice(username); + salt.extend_from_slice(unprefixed_salt); argon2 .hash_password_into_with_memory(secret, &salt, &mut key, &mut blocks) .unwrap(); diff --git a/firmware2/src/ffi/crypto.rs b/firmware2/src/ffi/crypto.rs index 0bc809c..f59ccdf 100644 --- a/firmware2/src/ffi/crypto.rs +++ b/firmware2/src/ffi/crypto.rs @@ -4,6 +4,9 @@ use sha2::{ Digest, Sha256, digest::{consts::U32, generic_array::GenericArray}, }; +use spectre_api_sys::{SpectreKeyPurpose, spectre_purpose_scope}; + +use crate::PSRAM_ALLOCATOR; // Just a way to mark non-null pointers. // Rust's `NonNull` is for mutable pointers only. @@ -43,20 +46,20 @@ unsafe extern "C" fn __spre_crypto_pwhash_scryptsalsa208sha256_ll( let password: &[u8] = core::slice::from_raw_parts(password, password_len); let salt: &[u8] = core::slice::from_raw_parts(salt, salt_len); let output: &mut [u8] = core::slice::from_raw_parts_mut(output, output_len); - let log_n = n.ilog2() as u8; - if n != 1 << log_n { - return -1; - } + let purpose = spectre_purpose_scope(SpectreKeyPurpose::Authentication); + let result = password_hash::derive_encryption_key(salt, password, &PSRAM_ALLOCATOR); - // TODO: Store encrypted password and decrypt it using argon2 - // let params = scrypt::Params::new(log_n, r, p).unwrap(); - // match scrypt::scrypt_in(password, salt, ¶ms, output, &PSRAM_ALLOCATOR) { - // Ok(()) => 0, - // Err(_) => -1, - // } - todo!() + assert_eq!( + output.len(), + result.len(), + "Derived invalid encryption key." + ); + + output.copy_from_slice(&result); } + + 0 } #[unsafe(no_mangle)] diff --git a/firmware2/src/main.rs b/firmware2/src/main.rs index 69e3274..38c34f5 100644 --- a/firmware2/src/main.rs +++ b/firmware2/src/main.rs @@ -22,7 +22,6 @@ use core::sync::atomic::{AtomicBool, Ordering}; use alloc::boxed::Box; use alloc::vec; -use argon2::{Algorithm, Argon2}; use embassy_executor::Spawner; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::channel::Channel; @@ -62,7 +61,6 @@ use spectre_api_sys::SpectreAlgorithm; use static_cell::StaticCell; use {esp_alloc as _, esp_backtrace as _}; -use crate::crypto::Argon2Blocks; use crate::keymap::create_hid_report_interceptor; use crate::logging::LOG_LEVEL_FILTER; use crate::matrix::IoeMatrix; @@ -384,21 +382,6 @@ async fn main(_spawner: Spawner) { let mut user_controller = UserController::new(); - warn!("Yeet"); - - let key = password_hash::hash(&PSRAM_ALLOCATOR); - - warn!("Argon2 passed. Key = {key:02x?}"); - - unsafe { - let user_key = &*spectre_api_sys::spectre_user_key( - c"Jakub Hlusička".as_ptr(), - c"test".as_ptr(), - SpectreAlgorithm::Current, - ); - warn!("{user_key:?}"); - } - info!("Awaiting on all tasks..."); // TODO: Probably want to select! instead and re-try. diff --git a/firmware2/src/ui/mod.rs b/firmware2/src/ui/mod.rs index b0a1872..675fb8e 100644 --- a/firmware2/src/ui/mod.rs +++ b/firmware2/src/ui/mod.rs @@ -1,12 +1,13 @@ // #![cfg_attr(not(feature = "simulator"), no_main)] -use alloc::boxed::Box; +use alloc::{boxed::Box, ffi::CString}; +use log::{info, warn}; +use slint::SharedString; +use spectre_api_sys::{SpectreAlgorithm, SpectreCounter, SpectreKeyPurpose, SpectreUserKey}; #[cfg(feature = "limit-fps")] use crate::FRAME_DURATION_MIN; -use crate::{ - SIGNAL_LCD_SUBMIT, SIGNAL_UI_RENDER, ui::backend::SlintBackend, -}; +use crate::{SIGNAL_LCD_SUBMIT, SIGNAL_UI_RENDER, ui::backend::SlintBackend}; pub mod backend; pub mod window_adapter; @@ -19,6 +20,33 @@ pub async fn run_renderer_task(backend: SlintBackend) { let main = AppWindow::new().unwrap(); + let mut buffer = CString::default(); + main.on_accepted(|string| { + warn!("Accepted: {string}"); + let Ok(c_string) = CString::new(&*string) else { + warn!("String cannot be converted to a C string: {string:?}"); + return; + }; + unsafe { + let user_key = &*spectre_api_sys::spectre_user_key( + c"test".as_ptr(), + c_string.as_ptr(), + SpectreAlgorithm::Current, + ); + warn!("{user_key:?}"); + let site_key = &*spectre_api_sys::spectre_site_key( + user_key as *const SpectreUserKey, + c"example.org".as_ptr(), + SpectreCounter::Initial, + SpectreKeyPurpose::Authentication, + c"".as_ptr(), + ); + warn!("{site_key:?}"); + + // TODO: Free memory + } + }); + // Instead of having a `loop` in the non-async `SlintBackend::run_event_loop`, we achieve // async by having only one iteration of the loop run, and `await`ing here. // The following block is analogous to `main.run()`. diff --git a/firmware2/ui/main.slint b/firmware2/ui/main.slint index 47c91a0..fbbd715 100644 --- a/firmware2/ui/main.slint +++ b/firmware2/ui/main.slint @@ -1,8 +1,24 @@ +/* +import { + Button, + VerticalBox, + LineEdit, + GridBox, + TabWidget, + Button, +} from "std-widgets.slint"; +*/ import { Button, VerticalBox, LineEdit, GridBox } from "std-widgets.slint"; // See https://github.com/slint-ui/slint/issues/4956 for issues with fonts. import "../fonts/IBM_Plex_Mono/IBMPlexMono-Regular.ttf"; +/* +export enum AppState { + PasswordForm, +} +*/ + export component AppWindow inherits Window { in property dummy: "ÄÖÜÁÉÍÓÚÝŔŚĹŹĆŃĚĽŽŠČŘĎŤŇŮÅäöüáéíóúýŕśĺźćńěľžščřďťňůåß„“”‘’—–@&$%+=¡¿¢£$¥€²³¼½¬¤¦§©®™°"; default-font-family: "IBM Plex Mono"; @@ -11,6 +27,7 @@ export component AppWindow inherits Window { width: 960px; in-out property counter: 42; callback request-increase-value(); + callback accepted(string); VerticalBox { width: 960px; height: 368px; @@ -31,6 +48,7 @@ export component AppWindow inherits Window { Button { text: "Increase value"; clicked => { + root.counter += 1; root.request-increase-value(); } } @@ -38,16 +56,32 @@ export component AppWindow inherits Window { LineEdit { input-type: InputType.text; text: "LineEdit"; + accepted(text) => { + root.accepted(text); + } + /*changed text => { + root.changed(self.text); + }*/ } } - Button { - text: "Button"; - } +/* + TabWidget { + Tab { + title: "first"; + Button { + text: "First"; + } + } - Button { - text: "Button"; + Tab { + title: "second"; + Button { + text: "Second"; + } + } } + */ } } }