Slint handle acceptation

This commit is contained in:
Jakub Hlusička 2026-01-20 02:55:17 +01:00
parent 35c017535e
commit b5535d6f52
9 changed files with 128 additions and 69 deletions

View file

@ -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" }

View file

@ -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"] }

View file

@ -1,6 +1,8 @@
use std::{env, path::PathBuf};
fn main() {
#[cfg(feature = "cmd")]
{
use std::{env, path::PathBuf};
let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
println!(
"cargo:rustc-link-search=native={}",
@ -24,4 +26,5 @@ fn main() {
"cargo:rerun-if-changed={}",
libsodium_install_dir.join("lib/libsodium.a").display()
);
}
}

View file

@ -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());

View file

@ -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();

View file

@ -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);
assert_eq!(
output.len(),
result.len(),
"Derived invalid encryption key."
);
output.copy_from_slice(&result);
}
// 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, &params, output, &PSRAM_ALLOCATOR) {
// Ok(()) => 0,
// Err(_) => -1,
// }
todo!()
}
0
}
#[unsafe(no_mangle)]

View file

@ -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.

View file

@ -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()`.

View file

@ -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 <string> dummy: "ÄÖÜÁÉÍÓÚÝŔŚĹŹĆŃĚĽŽŠČŘĎŤŇŮÅäöüáéíóúýŕśĺźćńěľžščřďťňůåß„“”‘’—–@&$%+=¡¿¢£$¥€²³¼½¬¤¦§©®™°";
default-font-family: "IBM Plex Mono";
@ -11,6 +27,7 @@ export component AppWindow inherits Window {
width: 960px;
in-out property <int> 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,17 +56,33 @@ export component AppWindow inherits Window {
LineEdit {
input-type: InputType.text;
text: "LineEdit";
accepted(text) => {
root.accepted(text);
}
/*changed text => {
root.changed(self.text);
}*/
}
}
/*
TabWidget {
Tab {
title: "first";
Button {
text: "Button";
text: "First";
}
}
Tab {
title: "second";
Button {
text: "Button";
text: "Second";
}
}
}
*/
}
}
}
}