diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..3550a30 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.gitignore b/.gitignore index ea8c4bf..2d5df85 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +.direnv diff --git a/Cargo.lock b/Cargo.lock index 0eafdfb..6cab25f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -553,6 +553,7 @@ dependencies = [ "clap", "color-eyre", "crosec", + "hex", "image", "num-traits", "strum", @@ -799,6 +800,12 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "image" version = "0.25.1" diff --git a/crosec/src/commands/fp_set_context.rs b/crosec/src/commands/fp_set_context.rs new file mode 100644 index 0000000..5e1b056 --- /dev/null +++ b/crosec/src/commands/fp_set_context.rs @@ -0,0 +1,67 @@ +use std::{os::fd::AsRawFd, thread::sleep, time::Duration}; + +use bytemuck::{Pod, Zeroable}; +use num::ToPrimitive; +use num_derive::ToPrimitive; + +use crate::{ec_command::ec_command_bytemuck, EcCmdResult}; + +use super::CrosEcCmd; + +pub type UserId = [u8; 32]; + +/* Version 1 of the command is "asynchronous". */ +#[repr(C, align(4))] +#[derive(Pod, Zeroable, Clone, Copy, Debug)] +struct EcParamsFpContextV1 { + /**< enum fp_context_action */ + action: u8, + reserved: [u8; 3], + user_id: UserId, +} + +#[repr(u8)] +#[derive(ToPrimitive)] +enum FpContextAction { + Async = 0, + GetResult = 1, +} + +/// Make sure that the fp mode is Reset before setting the context +/// Related: https://chromium.googlesource.com/chromiumos/platform2/+/HEAD/biod/cros_fp_device.cc#660 +pub fn fp_set_context(file: &mut File, user_id: UserId) -> EcCmdResult<()> { + // From testing, it seems that this can be anything besides all zeroes, but we're going to use these numbers in honor of CoolStar - https://github.com/coolstar/crosfingerprint/blob/5e77307d7542218e173f24eb657b426565ed361a/fingerprint_adapter/eccmd.cpp#L140 + ec_command_bytemuck( + CrosEcCmd::FpContext, + 1, + &EcParamsFpContextV1 { + action: FpContextAction::Async.to_u8().unwrap(), + reserved: Default::default(), + user_id, + }, + file.as_raw_fd(), + )?; + let mut tries = 20; + let delay = Duration::from_millis(100); + loop { + sleep(delay); + let result = ec_command_bytemuck( + CrosEcCmd::FpContext, + 1, + &EcParamsFpContextV1 { + action: FpContextAction::GetResult.to_u8().unwrap(), + reserved: Default::default(), + user_id, + }, + file.as_raw_fd(), + ); + if result.is_ok() { + break result; + } else { + tries -= 1; + if tries == 0 { + break result; + } + } + } +} diff --git a/crosec/src/commands/mod.rs b/crosec/src/commands/mod.rs index 8154e73..bf87d8d 100644 --- a/crosec/src/commands/mod.rs +++ b/crosec/src/commands/mod.rs @@ -21,6 +21,7 @@ pub enum CrosEcCmd { FpInfo = 0x0403, FpFrame = 0x0404, FpTemplate = 0x0405, + FpContext = 0x0406, FpStats = 0x0407, FpSetSeed = 0x0408, FpGetEncryptionStatus = 0x0409, @@ -35,6 +36,7 @@ pub mod fp_download; pub mod fp_get_encryption_status; pub mod fp_info; pub mod fp_mode; +pub mod fp_set_context; pub mod fp_set_seed; pub mod fp_stats; pub mod fp_upload_template; diff --git a/ectool/Cargo.toml b/ectool/Cargo.toml index 3604e14..661dabd 100644 --- a/ectool/Cargo.toml +++ b/ectool/Cargo.toml @@ -14,3 +14,4 @@ num-traits = "0.2.19" image = "0.25.1" strum = "0.26.3" uom = "0.36.0" +hex = "0.4.3" diff --git a/ectool/src/check_user_id.rs b/ectool/src/check_user_id.rs new file mode 100644 index 0000000..ebc7682 --- /dev/null +++ b/ectool/src/check_user_id.rs @@ -0,0 +1,7 @@ +use crosec::commands::fp_set_context::UserId; + +pub fn check_user_id(user_id_str: &str) -> Result { + let mut user_id = UserId::default(); + hex::decode_to_slice(user_id_str.as_bytes(), &mut user_id).map_err(|e| e.to_string())?; + Ok(user_id) +} diff --git a/ectool/src/fp_set_context_command.rs b/ectool/src/fp_set_context_command.rs new file mode 100644 index 0000000..3460070 --- /dev/null +++ b/ectool/src/fp_set_context_command.rs @@ -0,0 +1,11 @@ +use crosec::commands::fp_set_context::{fp_set_context, UserId}; +use crosec::CROS_FP_PATH; +use std::fs::File; + +pub fn fp_context_command(user_id: UserId) -> color_eyre::Result<()> { + let mut file = File::open(CROS_FP_PATH)?; + fp_set_context(&mut file, user_id)?; + let user_id_str = hex::encode(user_id); + println!("Set FP context to user id: 0x{user_id_str}"); + Ok(()) +} diff --git a/ectool/src/main.rs b/ectool/src/main.rs index 950c6c4..5358a6f 100644 --- a/ectool/src/main.rs +++ b/ectool/src/main.rs @@ -5,15 +5,18 @@ use std::fs::File; use charge_control_subcommand::{charge_control_subcommand, ChargeControlSubcommand}; use charge_current_limit_subcommand::charge_current_limit_subcommand; use check_seed::check_seed; +use check_user_id::check_user_id; use clap::{Parser, Subcommand, ValueEnum}; use color_eyre::eyre::Result; use crosec::commands::fp_info::fp_info; use crosec::commands::fp_mode::{fp_mode, FpMode}; +use crosec::commands::fp_set_context::UserId; use crosec::commands::fp_set_seed::{fp_set_seed, FP_CONTEXT_TPM_BYTES}; use crosec::commands::fp_stats::fp_stats; use crosec::commands::get_protocol_info::get_protocol_info; use crosec::wait_event::{event::EcMkbpEventType, wait_event_sync}; use fp_download_subcommand::{fp_download_subcommand, FpDownloadSubcommand}; +use fp_set_context_command::fp_context_command; use fp_upload_template_command::fp_upload_template_command; use get_uptime_info_command::get_uptime_info_commnad; use num_traits::cast::FromPrimitive; @@ -39,8 +42,10 @@ use crosec::{ mod charge_control_subcommand; mod charge_current_limit_subcommand; mod check_seed; +mod check_user_id; mod fp_download_subcommand; mod fp_get_encryption_status_command; +mod fp_set_context_command; mod fp_upload_template_command; mod get_uptime_info_command; @@ -139,6 +144,11 @@ enum Commands { #[arg()] limit: u32, }, + FpSetContext { + /// A 32 byte hex string + #[arg(value_parser = check_user_id)] + user_id: UserId, + }, } fn main() -> Result<()> { @@ -294,6 +304,7 @@ fn main() -> Result<()> { Commands::FpGetEncryptionStatus => fp_get_encryption_status_command()?, Commands::GetUptimeInfo { device } => get_uptime_info_commnad(device)?, Commands::ChargeCurrentLimit { limit } => charge_current_limit_subcommand(limit)?, + Commands::FpSetContext { user_id } => fp_context_command(user_id)?, } Ok(())