mirror of
https://github.com/librekeys/picoforge.git
synced 2026-07-28 08:01:19 -07:00
refactor(hal): rename variables for clarity and extract format_firmware_version helper
- fido/mod.rs: extract format_firmware_version, rename v→entry, s→error_text, val→field_data in TLV parsers - fido/ops.rs: rename auth_x/y→auth_point_x/y, rng→system_rng, err_str→error_string - rescue/ops.rs: rename rx_*→*_response, rdr→cursor, i→offset, val→field_data, opts_val→options_raw, curves_val→raw_curves_value - rescue/constants.rs: rename val→tag_value/color_value in from_u8 methods - transport/fido.rs: rename drain_buf→stale_packet_buffer, buf→packet_buf, start_time→deadline_start, status→keepalive_status/ctap_status_byte - firmwares/mod.rs: rename fw_type→firmware_variant, ver→firmware_version
This commit is contained in:
+105
-97
@@ -106,18 +106,31 @@ pub(crate) fn get_fido_info() -> Result<FidoDeviceInfo, String> {
|
||||
HidTransport::open().map_err(|e| format!("Could not open HID transport: {}", e))?;
|
||||
|
||||
let info_payload = [CtapCommand::GetInfo as u8];
|
||||
let info_res = transport
|
||||
let info_response = transport
|
||||
.send_cbor(CTAPHID_CBOR, &info_payload)
|
||||
.map_err(|e| format!("GetInfo CTAP command failed: {}", e))?;
|
||||
|
||||
let info_val: Value =
|
||||
from_slice(&info_res).map_err(|e| format!("Failed to parse GetInfo CBOR: {}", e))?;
|
||||
let info_value: Value =
|
||||
from_slice(&info_response).map_err(|e| format!("Failed to parse GetInfo CBOR: {}", e))?;
|
||||
|
||||
parse_fido_get_info(&info_val)
|
||||
parse_fido_get_info(&info_value)
|
||||
}
|
||||
|
||||
fn parse_fido_get_info(info_val: &Value) -> Result<FidoDeviceInfo, String> {
|
||||
let map = match info_val {
|
||||
fn format_firmware_version(raw: i128) -> String {
|
||||
if raw > 0xFFFF {
|
||||
format!(
|
||||
"{}.{}.{}",
|
||||
(raw >> 16) & 0xFF,
|
||||
(raw >> 8) & 0xFF,
|
||||
raw & 0xFF
|
||||
)
|
||||
} else {
|
||||
format!("{}.{}", (raw >> 8) & 0xFF, raw & 0xFF)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_fido_get_info(info_value: &Value) -> Result<FidoDeviceInfo, String> {
|
||||
let map = match info_value {
|
||||
Value::Map(m) => m,
|
||||
_ => return Err("GetInfo response is not a CBOR map".into()),
|
||||
};
|
||||
@@ -150,9 +163,9 @@ fn parse_fido_get_info(info_val: &Value) -> Result<FidoDeviceInfo, String> {
|
||||
// 0x01: versions (array of strings)
|
||||
0x01 => {
|
||||
if let Value::Array(arr) = val {
|
||||
for v in arr {
|
||||
if let Value::Text(s) = v {
|
||||
versions.push(s.clone());
|
||||
for entry in arr {
|
||||
if let Value::Text(version_str) = entry {
|
||||
versions.push(version_str.clone());
|
||||
}
|
||||
}
|
||||
log::info!("Device versions (0x01): {:?}", versions);
|
||||
@@ -161,9 +174,9 @@ fn parse_fido_get_info(info_val: &Value) -> Result<FidoDeviceInfo, String> {
|
||||
// 0x02: extensions (array of strings)
|
||||
0x02 => {
|
||||
if let Value::Array(arr) = val {
|
||||
for v in arr {
|
||||
if let Value::Text(s) = v {
|
||||
extensions.push(s.clone());
|
||||
for entry in arr {
|
||||
if let Value::Text(ext_str) = entry {
|
||||
extensions.push(ext_str.clone());
|
||||
}
|
||||
}
|
||||
log::info!("Device extensions (0x02): {:?}", extensions);
|
||||
@@ -171,16 +184,18 @@ fn parse_fido_get_info(info_val: &Value) -> Result<FidoDeviceInfo, String> {
|
||||
}
|
||||
// 0x03: aaguid (byte string)
|
||||
0x03 => {
|
||||
if let Value::Bytes(b) = val {
|
||||
aaguid = hex::encode_upper(b);
|
||||
if let Value::Bytes(g) = val {
|
||||
aaguid = hex::encode_upper(g);
|
||||
log::info!("Device aaguid (0x03): {}", aaguid);
|
||||
}
|
||||
}
|
||||
// 0x04: options (map of string -> bool)
|
||||
0x04 => {
|
||||
if let Value::Map(opts_map) = val {
|
||||
for (k, v) in opts_map {
|
||||
if let (Value::Text(name), Value::Bool(enabled)) = (k, v) {
|
||||
for (option_key, option_value) in opts_map {
|
||||
if let (Value::Text(name), Value::Bool(enabled)) =
|
||||
(option_key, option_value)
|
||||
{
|
||||
options.insert(name.clone(), *enabled);
|
||||
}
|
||||
}
|
||||
@@ -189,17 +204,17 @@ fn parse_fido_get_info(info_val: &Value) -> Result<FidoDeviceInfo, String> {
|
||||
}
|
||||
// 0x05: maxMsgSize
|
||||
0x05 => {
|
||||
if let Value::Integer(n) = val {
|
||||
max_msg_size = *n;
|
||||
if let Value::Integer(raw_size) = val {
|
||||
max_msg_size = *raw_size;
|
||||
log::info!("Device maxMsgSize (0x05): {}", max_msg_size);
|
||||
}
|
||||
}
|
||||
// 0x06: pinUvAuthProtocols (array of unsigned)
|
||||
0x06 => {
|
||||
if let Value::Array(arr) = val {
|
||||
for v in arr {
|
||||
if let Value::Integer(n) = v {
|
||||
pin_protocols.push(*n as u32);
|
||||
for entry in arr {
|
||||
if let Value::Integer(protocol) = entry {
|
||||
pin_protocols.push(*protocol as u32);
|
||||
}
|
||||
}
|
||||
log::info!("Device pinUvAuthProtocols (0x06): {:?}", pin_protocols);
|
||||
@@ -207,8 +222,8 @@ fn parse_fido_get_info(info_val: &Value) -> Result<FidoDeviceInfo, String> {
|
||||
}
|
||||
// 0x07: maxCredentialCountInList
|
||||
0x07 => {
|
||||
if let Value::Integer(n) = val {
|
||||
max_credential_count_in_list = Some(*n);
|
||||
if let Value::Integer(count) = val {
|
||||
max_credential_count_in_list = Some(*count);
|
||||
log::info!(
|
||||
"Device maxCredentialCountInList (0x07): {}",
|
||||
max_credential_count_in_list.unwrap()
|
||||
@@ -217,8 +232,8 @@ fn parse_fido_get_info(info_val: &Value) -> Result<FidoDeviceInfo, String> {
|
||||
}
|
||||
// 0x08: maxCredentialIdLength
|
||||
0x08 => {
|
||||
if let Value::Integer(n) = val {
|
||||
max_credential_id_length = Some(*n);
|
||||
if let Value::Integer(max_len) = val {
|
||||
max_credential_id_length = Some(*max_len);
|
||||
log::info!(
|
||||
"Device maxCredentialIdLength (0x08): {}",
|
||||
max_credential_id_length.unwrap()
|
||||
@@ -228,9 +243,10 @@ fn parse_fido_get_info(info_val: &Value) -> Result<FidoDeviceInfo, String> {
|
||||
// 0x0A: algorithms
|
||||
0x0A => {
|
||||
if let Value::Array(arr) = val {
|
||||
for v in arr {
|
||||
if let Value::Map(m) = v
|
||||
&& let Some(Value::Integer(alg_id)) = m.get(&Value::Text("alg".into()))
|
||||
for alg_entry in arr {
|
||||
if let Value::Map(alg_map) = alg_entry
|
||||
&& let Some(Value::Integer(alg_id)) =
|
||||
alg_map.get(&Value::Text("alg".into()))
|
||||
{
|
||||
if let Some(alg) = CoseAlgorithm::from_i128(*alg_id) {
|
||||
algorithms.push(alg.to_string());
|
||||
@@ -244,8 +260,8 @@ fn parse_fido_get_info(info_val: &Value) -> Result<FidoDeviceInfo, String> {
|
||||
}
|
||||
// 0x0B: maxSerializedLargeBlobArray
|
||||
0x0B => {
|
||||
if let Value::Integer(n) = val {
|
||||
max_serialized_large_blob_array = Some(*n);
|
||||
if let Value::Integer(blob_max) = val {
|
||||
max_serialized_large_blob_array = Some(*blob_max);
|
||||
log::info!(
|
||||
"Device maxSerializedLargeBlobArray (0x0B): {}",
|
||||
max_serialized_large_blob_array.unwrap()
|
||||
@@ -254,8 +270,8 @@ fn parse_fido_get_info(info_val: &Value) -> Result<FidoDeviceInfo, String> {
|
||||
}
|
||||
// 0x0C: forcePinChange
|
||||
0x0C => {
|
||||
if let Value::Bool(b) = val {
|
||||
force_pin_change = Some(*b);
|
||||
if let Value::Bool(force) = val {
|
||||
force_pin_change = Some(*force);
|
||||
log::info!(
|
||||
"Device forcePinChange (0x0C): {}",
|
||||
force_pin_change.unwrap()
|
||||
@@ -264,22 +280,22 @@ fn parse_fido_get_info(info_val: &Value) -> Result<FidoDeviceInfo, String> {
|
||||
}
|
||||
// 0x0D: minPINLength
|
||||
0x0D => {
|
||||
if let Value::Integer(n) = val {
|
||||
min_pin_length = *n;
|
||||
if let Value::Integer(min_len) = val {
|
||||
min_pin_length = *min_len;
|
||||
log::info!("Device minPINLength (0x0D): {}", min_pin_length);
|
||||
}
|
||||
}
|
||||
// 0x0E: firmwareVersion
|
||||
0x0E => {
|
||||
if let Value::Integer(n) = val {
|
||||
firmware_version_raw = *n;
|
||||
if let Value::Integer(fw_ver) = val {
|
||||
firmware_version_raw = *fw_ver;
|
||||
log::info!("Device firmwareVersion (0x0E): {}", firmware_version_raw);
|
||||
}
|
||||
}
|
||||
// 0x0F: maxCredBlobLength
|
||||
0x0F => {
|
||||
if let Value::Integer(n) = val {
|
||||
max_cred_blob_length = Some(*n);
|
||||
if let Value::Integer(cred_blob) = val {
|
||||
max_cred_blob_length = Some(*cred_blob);
|
||||
log::info!(
|
||||
"Device maxCredBlobLength (0x0F): {}",
|
||||
max_cred_blob_length.unwrap()
|
||||
@@ -293,8 +309,8 @@ fn parse_fido_get_info(info_val: &Value) -> Result<FidoDeviceInfo, String> {
|
||||
}
|
||||
// 0x14: remainingDiscoverableCredentials
|
||||
0x14 => {
|
||||
if let Value::Integer(n) = val {
|
||||
remaining_discoverable_credentials = Some(*n);
|
||||
if let Value::Integer(remaining) = val {
|
||||
remaining_discoverable_credentials = Some(*remaining);
|
||||
log::info!(
|
||||
"Device remainingDiscoverableCredentials (0x14): {}",
|
||||
remaining_discoverable_credentials.unwrap()
|
||||
@@ -320,20 +336,7 @@ fn parse_fido_get_info(info_val: &Value) -> Result<FidoDeviceInfo, String> {
|
||||
}
|
||||
}
|
||||
|
||||
let firmware_version = if firmware_version_raw > 0xFFFF {
|
||||
format!(
|
||||
"{}.{}.{}",
|
||||
(firmware_version_raw >> 16) & 0xFF,
|
||||
(firmware_version_raw >> 8) & 0xFF,
|
||||
firmware_version_raw & 0xFF
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"{}.{}",
|
||||
(firmware_version_raw >> 8) & 0xFF,
|
||||
firmware_version_raw & 0xFF
|
||||
)
|
||||
};
|
||||
let firmware_version = format_firmware_version(firmware_version_raw);
|
||||
|
||||
log::info!(
|
||||
"FIDO GetInfo parsed: {} versions, {} extensions, AAGUID={}, FW={}",
|
||||
@@ -568,14 +571,14 @@ pub(crate) fn reset_device() -> Result<String, String> {
|
||||
HidTransport::open().map_err(|e| format!("Could not open HID transport: {}", e))?;
|
||||
|
||||
transport.reset().map_err(|e| {
|
||||
let s = e.to_string();
|
||||
if s.contains("0x30") {
|
||||
let error_text = e.to_string();
|
||||
if error_text.contains("0x30") {
|
||||
return "Reset not allowed. The device must be unplugged and re-plugged within 10 seconds before sending the reset command.".to_string();
|
||||
}
|
||||
if s.contains("0x27") {
|
||||
if error_text.contains("0x27") {
|
||||
return "Reset declined. Touch was not confirmed on the device.".to_string();
|
||||
}
|
||||
format!("Reset failed: {}", s)
|
||||
format!("Reset failed: {}", error_text)
|
||||
})?;
|
||||
|
||||
Ok("Device has been factory reset. All credentials and PIN have been erased.".to_string())
|
||||
@@ -786,18 +789,18 @@ fn parse_management_info(raw: &[u8]) -> Result<ManagementInfo, String> {
|
||||
return Err(format!("truncated management tag 0x{:02X}", tag));
|
||||
}
|
||||
|
||||
let val = &data[i..i + len];
|
||||
let field_data = &data[i..i + len];
|
||||
match tag {
|
||||
0x01 => info.usb_supported = parse_management_u16(val),
|
||||
0x02 if val.len() == 4 => {
|
||||
info.serial = Some(hex::encode_upper(val));
|
||||
0x01 => info.usb_supported = parse_management_u16(field_data),
|
||||
0x02 if field_data.len() == 4 => {
|
||||
info.serial = Some(hex::encode_upper(field_data));
|
||||
}
|
||||
0x03 => info.usb_enabled = parse_management_u16(val),
|
||||
0x05 if val.len() >= 2 => {
|
||||
info.firmware_version = Some(format!("{}.{}", val[0], val[1]));
|
||||
0x03 => info.usb_enabled = parse_management_u16(field_data),
|
||||
0x05 if field_data.len() >= 2 => {
|
||||
info.firmware_version = Some(format!("{}.{}", field_data[0], field_data[1]));
|
||||
}
|
||||
0x0A => {
|
||||
if let Some(locked) = val.first() {
|
||||
if let Some(locked) = field_data.first() {
|
||||
info.config_locked = Some(*locked != 0);
|
||||
}
|
||||
}
|
||||
@@ -937,48 +940,53 @@ fn read_rskey_physical_config(transport: &HidTransport, mut config: AppConfig) -
|
||||
if i + len > data.len() {
|
||||
break;
|
||||
}
|
||||
let val = &data[i..i + len];
|
||||
let field_data = &data[i..i + len];
|
||||
|
||||
match tag_byte {
|
||||
RSKEY_PHY_TAG_VIDPID if val.len() == 4 => {
|
||||
config.vid = format!("{:04X}", u16::from_be_bytes([val[0], val[1]]));
|
||||
config.pid = format!("{:04X}", u16::from_be_bytes([val[2], val[3]]));
|
||||
RSKEY_PHY_TAG_VIDPID if field_data.len() == 4 => {
|
||||
config.vid = format!("{:04X}", u16::from_be_bytes([field_data[0], field_data[1]]));
|
||||
config.pid = format!("{:04X}", u16::from_be_bytes([field_data[2], field_data[3]]));
|
||||
}
|
||||
RSKEY_PHY_TAG_LED_GPIO if !val.is_empty() => {
|
||||
config.led_gpio = val[0];
|
||||
RSKEY_PHY_TAG_LED_GPIO if !field_data.is_empty() => {
|
||||
config.led_gpio = field_data[0];
|
||||
}
|
||||
RSKEY_PHY_TAG_LED_BRIGHTNESS if !val.is_empty() => {
|
||||
config.led_brightness = val[0];
|
||||
RSKEY_PHY_TAG_LED_BRIGHTNESS if !field_data.is_empty() => {
|
||||
config.led_brightness = field_data[0];
|
||||
}
|
||||
RSKEY_PHY_TAG_PRESENCE_TIMEOUT if !val.is_empty() => {
|
||||
config.touch_timeout = val[0];
|
||||
RSKEY_PHY_TAG_PRESENCE_TIMEOUT if !field_data.is_empty() => {
|
||||
config.touch_timeout = field_data[0];
|
||||
}
|
||||
RSKEY_PHY_TAG_USB_PRODUCT => {
|
||||
let s = std::str::from_utf8(val)
|
||||
let product_str = std::str::from_utf8(field_data)
|
||||
.unwrap_or("")
|
||||
.trim_matches(char::from(0));
|
||||
config.product_name = s.to_string();
|
||||
config.product_name = product_str.to_string();
|
||||
}
|
||||
RSKEY_PHY_TAG_OPTS if val.len() >= 2 => {
|
||||
let opts = u16::from_be_bytes([val[0], val[1]]);
|
||||
RSKEY_PHY_TAG_OPTS if field_data.len() >= 2 => {
|
||||
let opts = u16::from_be_bytes([field_data[0], field_data[1]]);
|
||||
config.led_dimmable = opts & RSKEY_OPT_DIMMABLE != 0;
|
||||
config.power_cycle_on_reset = opts & RSKEY_OPT_DISABLE_POWER_RESET == 0;
|
||||
config.led_steady = opts & RSKEY_OPT_LED_STEADY != 0;
|
||||
}
|
||||
RSKEY_PHY_TAG_CURVES if val.len() == 4 => {
|
||||
config.raw_curves_mask = Some(u32::from_be_bytes([val[0], val[1], val[2], val[3]]));
|
||||
RSKEY_PHY_TAG_CURVES if field_data.len() == 4 => {
|
||||
config.raw_curves_mask = Some(u32::from_be_bytes([
|
||||
field_data[0],
|
||||
field_data[1],
|
||||
field_data[2],
|
||||
field_data[3],
|
||||
]));
|
||||
}
|
||||
RSKEY_PHY_TAG_LED_DRIVER if !val.is_empty() => {
|
||||
config.led_driver = Some(val[0]);
|
||||
RSKEY_PHY_TAG_LED_DRIVER if !field_data.is_empty() => {
|
||||
config.led_driver = Some(field_data[0]);
|
||||
}
|
||||
RSKEY_PHY_TAG_LED_ORDER if !val.is_empty() => {
|
||||
config.led_order = Some(val[0]);
|
||||
RSKEY_PHY_TAG_LED_ORDER if !field_data.is_empty() => {
|
||||
config.led_order = Some(field_data[0]);
|
||||
}
|
||||
RSKEY_PHY_TAG_LED_NUM if !val.is_empty() => {
|
||||
config.led_num = Some(val[0]);
|
||||
RSKEY_PHY_TAG_LED_NUM if !field_data.is_empty() => {
|
||||
config.led_num = Some(field_data[0]);
|
||||
}
|
||||
RSKEY_PHY_TAG_ENABLED_USB_ITF if !val.is_empty() => {
|
||||
config.enabled_usb_itf = Some(val[0]);
|
||||
RSKEY_PHY_TAG_ENABLED_USB_ITF if !field_data.is_empty() => {
|
||||
config.enabled_usb_itf = Some(field_data[0]);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@@ -1376,11 +1384,11 @@ pub(crate) fn upload_enterprise_attestation_cert(
|
||||
None,
|
||||
)
|
||||
.map_err(|e| {
|
||||
let s = e.to_string();
|
||||
if s.contains("0x2B") {
|
||||
let error_text = e.to_string();
|
||||
if error_text.contains("0x2B") {
|
||||
return "Device does not support enterprise attestation (0x2B). Ensure firmware is up to date.".to_string();
|
||||
}
|
||||
format!("Failed to obtain PIN token: {}", s)
|
||||
format!("Failed to obtain PIN token: {}", error_text)
|
||||
})?;
|
||||
|
||||
transport
|
||||
@@ -1408,12 +1416,12 @@ pub(crate) fn enable_enterprise_attestation(pin: String) -> Result<String, Strin
|
||||
None,
|
||||
)
|
||||
.map_err(|e| {
|
||||
let s = e.to_string();
|
||||
log::error!("Failed to get PIN token: {}", s);
|
||||
if s.contains("0x2B") {
|
||||
let error_text = e.to_string();
|
||||
log::error!("Failed to get PIN token: {}", error_text);
|
||||
if error_text.contains("0x2B") {
|
||||
return "Device does not support enterprise attestation (0x2B). Ensure firmware is up to date.".to_string();
|
||||
}
|
||||
format!("Failed to obtain PIN token: {}", s)
|
||||
format!("Failed to obtain PIN token: {}", error_text)
|
||||
})?;
|
||||
|
||||
transport
|
||||
|
||||
+51
-51
@@ -342,8 +342,8 @@ impl FidoOperations for HidTransport {
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => {
|
||||
let err_str = e.to_string();
|
||||
log::error!("Failed to enable Enterprise Attestation: {}", err_str);
|
||||
let error_string = e.to_string();
|
||||
log::error!("Failed to enable Enterprise Attestation: {}", error_string);
|
||||
Err(PFError::Device(format!(
|
||||
"EnableEnterpriseAttestation failed: {}",
|
||||
e
|
||||
@@ -387,11 +387,11 @@ impl FidoOperations for HidTransport {
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => {
|
||||
let err_str = e.to_string();
|
||||
log::error!("Failed to send setMinPINLength config: {}", err_str);
|
||||
let error_string = e.to_string();
|
||||
log::error!("Failed to send setMinPINLength config: {}", error_string);
|
||||
|
||||
// Check for PIN policy violation (0x37) - cannot decrease min PIN length
|
||||
if err_str.contains("0x37") {
|
||||
if error_string.contains("0x37") {
|
||||
return Err(PFError::Device(
|
||||
"Cannot decrease minimum PIN length. The FIDO2 security policy only allows increasing the minimum PIN length, not decreasing it. A device reset is required to lower the minimum.".into()
|
||||
));
|
||||
@@ -422,8 +422,8 @@ impl FidoOperations for HidTransport {
|
||||
payload.extend(to_vec(&Value::Map(map)).map_err(|e| PFError::Io(e.to_string()))?);
|
||||
|
||||
log::debug!("Sending GetKeyAgreement command...");
|
||||
let resp = self.send_cbor(CTAPHID_CBOR, &payload)?;
|
||||
let val: Value = from_slice(&resp).map_err(|e| PFError::Io(e.to_string()))?;
|
||||
let response = self.send_cbor(CTAPHID_CBOR, &payload)?;
|
||||
let val: Value = from_slice(&response).map_err(|e| PFError::Io(e.to_string()))?;
|
||||
|
||||
if let Value::Map(m) = val {
|
||||
log::debug!("GetKeyAgreement response: {:?}", m);
|
||||
@@ -454,16 +454,16 @@ impl FidoOperations for HidTransport {
|
||||
let auth_key_agreement = self.get_key_agreement()?;
|
||||
|
||||
// 2. Generate Platform Key Pair (P-256)
|
||||
let rng = ring::rand::SystemRandom::new();
|
||||
let system_rng = ring::rand::SystemRandom::new();
|
||||
let platform_private_key =
|
||||
agreement::EphemeralPrivateKey::generate(&agreement::ECDH_P256, &rng)
|
||||
agreement::EphemeralPrivateKey::generate(&agreement::ECDH_P256, &system_rng)
|
||||
.map_err(|_| PFError::Device("Failed to generate platform ephemeral key".into()))?;
|
||||
let platform_public_key_bytes = platform_private_key
|
||||
.compute_public_key()
|
||||
.map_err(|_| PFError::Device("Failed to compute platform public key".into()))?;
|
||||
|
||||
// 3. Extract Authenticator Public Key (X and Y coordinates)
|
||||
let (auth_x, auth_y) = if let Value::Map(m) = &auth_key_agreement {
|
||||
let (auth_point_x, auth_point_y) = if let Value::Map(m) = &auth_key_agreement {
|
||||
let x = match m.get(&Value::Integer(-2)) {
|
||||
Some(Value::Bytes(b)) => b,
|
||||
_ => return Err(PFError::Device("Invalid KeyAgreement X coordinate".into())),
|
||||
@@ -478,8 +478,8 @@ impl FidoOperations for HidTransport {
|
||||
};
|
||||
|
||||
let mut auth_pub_key_bytes = vec![0x04];
|
||||
auth_pub_key_bytes.extend(auth_x);
|
||||
auth_pub_key_bytes.extend(auth_y);
|
||||
auth_pub_key_bytes.extend(auth_point_x);
|
||||
auth_pub_key_bytes.extend(auth_point_y);
|
||||
|
||||
let auth_unparsed_pub_key =
|
||||
agreement::UnparsedPublicKey::new(&agreement::ECDH_P256, auth_pub_key_bytes);
|
||||
@@ -527,8 +527,8 @@ impl FidoOperations for HidTransport {
|
||||
payload.extend(payload_cbor);
|
||||
|
||||
log::debug!("Sending getPinToken command...");
|
||||
let resp = self.send_cbor(CTAPHID_CBOR, &payload)?;
|
||||
let val: Value = from_slice(&resp).map_err(|e| PFError::Io(e.to_string()))?;
|
||||
let response = self.send_cbor(CTAPHID_CBOR, &payload)?;
|
||||
let val: Value = from_slice(&response).map_err(|e| PFError::Io(e.to_string()))?;
|
||||
|
||||
if let Value::Map(m) = val {
|
||||
log::debug!("getPinToken response: {:?}", m);
|
||||
@@ -572,16 +572,16 @@ impl FidoOperations for HidTransport {
|
||||
let auth_key_agreement = self.get_key_agreement()?;
|
||||
|
||||
// 2. Generate Platform Key Pair (P-256)
|
||||
let rng = ring::rand::SystemRandom::new();
|
||||
let system_rng = ring::rand::SystemRandom::new();
|
||||
let platform_private_key =
|
||||
agreement::EphemeralPrivateKey::generate(&agreement::ECDH_P256, &rng)
|
||||
agreement::EphemeralPrivateKey::generate(&agreement::ECDH_P256, &system_rng)
|
||||
.map_err(|_| PFError::Device("Failed to generate platform ephemeral key".into()))?;
|
||||
let platform_public_key_bytes = platform_private_key
|
||||
.compute_public_key()
|
||||
.map_err(|_| PFError::Device("Failed to compute platform public key".into()))?;
|
||||
|
||||
// 3. Extract Authenticator Public Key (X and Y coordinates)
|
||||
let (auth_x, auth_y) = if let Value::Map(m) = &auth_key_agreement {
|
||||
let (auth_point_x, auth_point_y) = if let Value::Map(m) = &auth_key_agreement {
|
||||
let x = match m.get(&Value::Integer(-2)) {
|
||||
Some(Value::Bytes(b)) => b,
|
||||
_ => return Err(PFError::Device("Invalid KeyAgreement X coordinate".into())),
|
||||
@@ -596,8 +596,8 @@ impl FidoOperations for HidTransport {
|
||||
};
|
||||
|
||||
let mut auth_pub_key_bytes = vec![0x04];
|
||||
auth_pub_key_bytes.extend(auth_x);
|
||||
auth_pub_key_bytes.extend(auth_y);
|
||||
auth_pub_key_bytes.extend(auth_point_x);
|
||||
auth_pub_key_bytes.extend(auth_point_y);
|
||||
|
||||
let auth_unparsed_pub_key =
|
||||
agreement::UnparsedPublicKey::new(&agreement::ECDH_P256, auth_pub_key_bytes);
|
||||
@@ -649,12 +649,12 @@ impl FidoOperations for HidTransport {
|
||||
payload.extend(payload_cbor);
|
||||
|
||||
log::debug!("Sending getPinUvAuthTokenUsingPinWithPermissions command...");
|
||||
let resp = self.send_cbor(CTAPHID_CBOR, &payload)?;
|
||||
let response = self.send_cbor(CTAPHID_CBOR, &payload)?;
|
||||
log::debug!(
|
||||
"getPinUvAuthTokenUsingPinWithPermissions response: {:?}",
|
||||
resp
|
||||
response
|
||||
);
|
||||
let val: Value = from_slice(&resp).map_err(|e| PFError::Io(e.to_string()))?;
|
||||
let val: Value = from_slice(&response).map_err(|e| PFError::Io(e.to_string()))?;
|
||||
|
||||
if let Value::Map(m) = val {
|
||||
log::debug!("getPinUvAuthTokenUsingPinWithPermissions response: {:?}", m);
|
||||
@@ -705,16 +705,16 @@ impl FidoOperations for HidTransport {
|
||||
let auth_key_agreement = self.get_key_agreement()?;
|
||||
|
||||
// 2. Generate Platform Key Pair (P-256)
|
||||
let rng = ring::rand::SystemRandom::new();
|
||||
let system_rng = ring::rand::SystemRandom::new();
|
||||
let platform_private_key =
|
||||
agreement::EphemeralPrivateKey::generate(&agreement::ECDH_P256, &rng)
|
||||
agreement::EphemeralPrivateKey::generate(&agreement::ECDH_P256, &system_rng)
|
||||
.map_err(|_| PFError::Device("Failed to generate platform ephemeral key".into()))?;
|
||||
let platform_public_key_bytes = platform_private_key
|
||||
.compute_public_key()
|
||||
.map_err(|_| PFError::Device("Failed to compute platform public key".into()))?;
|
||||
|
||||
// 3. Extract Authenticator Public Key
|
||||
let (auth_x, auth_y) = if let Value::Map(m) = &auth_key_agreement {
|
||||
let (auth_point_x, auth_point_y) = if let Value::Map(m) = &auth_key_agreement {
|
||||
let x = match m.get(&Value::Integer(-2)) {
|
||||
Some(Value::Bytes(b)) => b,
|
||||
_ => return Err(PFError::Device("Invalid KeyAgreement X coordinate".into())),
|
||||
@@ -729,8 +729,8 @@ impl FidoOperations for HidTransport {
|
||||
};
|
||||
|
||||
let mut auth_pub_key_bytes = vec![0x04];
|
||||
auth_pub_key_bytes.extend(auth_x);
|
||||
auth_pub_key_bytes.extend(auth_y);
|
||||
auth_pub_key_bytes.extend(auth_point_x);
|
||||
auth_pub_key_bytes.extend(auth_point_y);
|
||||
|
||||
let auth_unparsed_pub_key =
|
||||
agreement::UnparsedPublicKey::new(&agreement::ECDH_P256, auth_pub_key_bytes);
|
||||
@@ -796,9 +796,9 @@ impl FidoOperations for HidTransport {
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => {
|
||||
let err_str = e.to_string();
|
||||
log::error!("Failed to send setPin config: {}", err_str);
|
||||
if err_str.contains("0x37") {
|
||||
let error_string = e.to_string();
|
||||
log::error!("Failed to send setPin config: {}", error_string);
|
||||
if error_string.contains("0x37") {
|
||||
return Err(PFError::Device(
|
||||
"New PIN violates policy (e.g. too short).".into(),
|
||||
));
|
||||
@@ -836,16 +836,16 @@ impl FidoOperations for HidTransport {
|
||||
let auth_key_agreement = self.get_key_agreement()?;
|
||||
|
||||
// 2. Generate Platform Key Pair (P-256)
|
||||
let rng = ring::rand::SystemRandom::new();
|
||||
let system_rng = ring::rand::SystemRandom::new();
|
||||
let platform_private_key =
|
||||
agreement::EphemeralPrivateKey::generate(&agreement::ECDH_P256, &rng)
|
||||
agreement::EphemeralPrivateKey::generate(&agreement::ECDH_P256, &system_rng)
|
||||
.map_err(|_| PFError::Device("Failed to generate platform ephemeral key".into()))?;
|
||||
let platform_public_key_bytes = platform_private_key
|
||||
.compute_public_key()
|
||||
.map_err(|_| PFError::Device("Failed to compute platform public key".into()))?;
|
||||
|
||||
// 3. Extract Authenticator Public Key
|
||||
let (auth_x, auth_y) = if let Value::Map(m) = &auth_key_agreement {
|
||||
let (auth_point_x, auth_point_y) = if let Value::Map(m) = &auth_key_agreement {
|
||||
let x = match m.get(&Value::Integer(-2)) {
|
||||
Some(Value::Bytes(b)) => b,
|
||||
_ => return Err(PFError::Device("Invalid KeyAgreement X coordinate".into())),
|
||||
@@ -860,8 +860,8 @@ impl FidoOperations for HidTransport {
|
||||
};
|
||||
|
||||
let mut auth_pub_key_bytes = vec![0x04];
|
||||
auth_pub_key_bytes.extend(auth_x);
|
||||
auth_pub_key_bytes.extend(auth_y);
|
||||
auth_pub_key_bytes.extend(auth_point_x);
|
||||
auth_pub_key_bytes.extend(auth_point_y);
|
||||
|
||||
let auth_unparsed_pub_key =
|
||||
agreement::UnparsedPublicKey::new(&agreement::ECDH_P256, auth_pub_key_bytes);
|
||||
@@ -943,17 +943,17 @@ impl FidoOperations for HidTransport {
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => {
|
||||
let err_str = e.to_string();
|
||||
log::error!("Failed to send changePin config: {}", err_str);
|
||||
if err_str.contains("0x31") {
|
||||
let error_string = e.to_string();
|
||||
log::error!("Failed to send changePin config: {}", error_string);
|
||||
if error_string.contains("0x31") {
|
||||
return Err(PFError::Device("Invalid current PIN (0x31). Please check that you entered the correct PIN.".into()));
|
||||
}
|
||||
if err_str.contains("0x32") {
|
||||
if error_string.contains("0x32") {
|
||||
return Err(PFError::Device(
|
||||
"PIN blocked (0x32). Device reset may be required.".into(),
|
||||
));
|
||||
}
|
||||
if err_str.contains("0x37") {
|
||||
if error_string.contains("0x37") {
|
||||
return Err(PFError::Device(
|
||||
"New PIN violates policy (e.g. too short).".into(),
|
||||
));
|
||||
@@ -1096,7 +1096,7 @@ impl FidoOperations for HidTransport {
|
||||
let mut payload = vec![CtapCommand::CredentialMgmt as u8];
|
||||
payload.extend(to_vec(&Value::Map(mgmt_map)).map_err(|e| PFError::Io(e.to_string()))?);
|
||||
|
||||
let resp = match self.send_cbor(CTAPHID_CBOR, &payload) {
|
||||
let response = match self.send_cbor(CTAPHID_CBOR, &payload) {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
if e.to_string().contains("0x2E") {
|
||||
@@ -1107,7 +1107,7 @@ impl FidoOperations for HidTransport {
|
||||
}
|
||||
};
|
||||
|
||||
let val: Value = from_slice(&resp).map_err(|e| PFError::Io(e.to_string()))?;
|
||||
let val: Value = from_slice(&response).map_err(|e| PFError::Io(e.to_string()))?;
|
||||
let mut total_rps = None;
|
||||
|
||||
if let Value::Map(m) = &val {
|
||||
@@ -1153,8 +1153,8 @@ impl FidoOperations for HidTransport {
|
||||
payload.extend(to_vec(&Value::Map(mgmt_map)).map_err(|e| PFError::Io(e.to_string()))?);
|
||||
|
||||
match self.send_cbor(CTAPHID_CBOR, &payload) {
|
||||
Ok(resp) => {
|
||||
let val: Value = from_slice(&resp).map_err(|e| PFError::Io(e.to_string()))?;
|
||||
Ok(rsp) => {
|
||||
let val: Value = from_slice(&rsp).map_err(|e| PFError::Io(e.to_string()))?;
|
||||
if let Value::Map(m) = val {
|
||||
let rp = m
|
||||
.get(&Value::Integer(CredentialMgmtResponseParam::Rp as i128))
|
||||
@@ -1252,7 +1252,7 @@ impl FidoOperations for HidTransport {
|
||||
let mut payload = vec![CtapCommand::CredentialMgmt as u8];
|
||||
payload.extend(to_vec(&Value::Map(mgmt_map)).map_err(|e| PFError::Io(e.to_string()))?);
|
||||
|
||||
let resp = match self.send_cbor(CTAPHID_CBOR, &payload) {
|
||||
let response = match self.send_cbor(CTAPHID_CBOR, &payload) {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
if e.to_string().contains("0x2E") {
|
||||
@@ -1262,7 +1262,7 @@ impl FidoOperations for HidTransport {
|
||||
}
|
||||
};
|
||||
|
||||
let val: Value = from_slice(&resp).map_err(|e| PFError::Io(e.to_string()))?;
|
||||
let val: Value = from_slice(&response).map_err(|e| PFError::Io(e.to_string()))?;
|
||||
let mut total_creds = None;
|
||||
|
||||
if let Value::Map(m) = &val {
|
||||
@@ -1321,8 +1321,8 @@ impl FidoOperations for HidTransport {
|
||||
payload.extend(to_vec(&Value::Map(mgmt_map)).map_err(|e| PFError::Io(e.to_string()))?);
|
||||
|
||||
match self.send_cbor(CTAPHID_CBOR, &payload) {
|
||||
Ok(resp) => {
|
||||
let val: Value = from_slice(&resp).map_err(|e| PFError::Io(e.to_string()))?;
|
||||
Ok(rsp) => {
|
||||
let val: Value = from_slice(&rsp).map_err(|e| PFError::Io(e.to_string()))?;
|
||||
if let Value::Map(m) = val {
|
||||
let user = m
|
||||
.get(&Value::Integer(CredentialMgmtResponseParam::User as i128))
|
||||
@@ -1571,13 +1571,13 @@ mod tests {
|
||||
inner_map.insert(Value::Integer(-2), Value::Bytes(vec![0xAA; 32])); // x
|
||||
inner_map.insert(Value::Integer(-3), Value::Bytes(vec![0xBB; 32])); // y
|
||||
|
||||
let mut resp_map = BTreeMap::new();
|
||||
resp_map.insert(
|
||||
let mut response_map = BTreeMap::new();
|
||||
response_map.insert(
|
||||
Value::Integer(ClientPinResponseParam::KeyAgreement as i128),
|
||||
Value::Map(inner_map),
|
||||
);
|
||||
|
||||
let val = Value::Map(resp_map);
|
||||
let val = Value::Map(response_map);
|
||||
|
||||
// This mimics the logic in get_key_agreement
|
||||
if let Value::Map(m) = val {
|
||||
|
||||
+18
-14
@@ -86,13 +86,13 @@ impl AnyFirmware {
|
||||
/// Construct an `AnyFirmware` from a known firmware type and version string.
|
||||
///
|
||||
/// LkOne and Unknown are treated as pico-fido variants.
|
||||
pub fn new(fw_type: FirmwareType, version: &str) -> Self {
|
||||
let ver = FirmwareVersion::parse(version).unwrap_or_default();
|
||||
match fw_type {
|
||||
FirmwareType::PicoFido => Self::PicoFido(PicoFidoFirmware::new(ver)),
|
||||
FirmwareType::RSKey => Self::RSKey(RSKeyFirmware::new(ver)),
|
||||
pub fn new(firmware_variant: FirmwareType, version: &str) -> Self {
|
||||
let firmware_version = FirmwareVersion::parse(version).unwrap_or_default();
|
||||
match firmware_variant {
|
||||
FirmwareType::PicoFido => Self::PicoFido(PicoFidoFirmware::new(firmware_version)),
|
||||
FirmwareType::RSKey => Self::RSKey(RSKeyFirmware::new(firmware_version)),
|
||||
FirmwareType::LkOne | FirmwareType::Unknown => {
|
||||
Self::PicoFido(PicoFidoFirmware::new(ver))
|
||||
Self::PicoFido(PicoFidoFirmware::new(firmware_version))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -101,15 +101,19 @@ impl AnyFirmware {
|
||||
///
|
||||
/// The flag is only meaningful for `FirmwareType::PicoFido`; other types
|
||||
/// ignore it.
|
||||
pub fn new_with_legacy(fw_type: FirmwareType, version: &str, has_legacy_vendor: bool) -> Self {
|
||||
let ver = FirmwareVersion::parse(version).unwrap_or_default();
|
||||
match fw_type {
|
||||
FirmwareType::PicoFido => {
|
||||
Self::PicoFido(PicoFidoFirmware::new(ver).with_legacy_vendor(has_legacy_vendor))
|
||||
}
|
||||
FirmwareType::RSKey => Self::RSKey(RSKeyFirmware::new(ver)),
|
||||
pub fn new_with_legacy(
|
||||
firmware_variant: FirmwareType,
|
||||
version: &str,
|
||||
has_legacy_vendor: bool,
|
||||
) -> Self {
|
||||
let firmware_version = FirmwareVersion::parse(version).unwrap_or_default();
|
||||
match firmware_variant {
|
||||
FirmwareType::PicoFido => Self::PicoFido(
|
||||
PicoFidoFirmware::new(firmware_version).with_legacy_vendor(has_legacy_vendor),
|
||||
),
|
||||
FirmwareType::RSKey => Self::RSKey(RSKeyFirmware::new(firmware_version)),
|
||||
FirmwareType::LkOne | FirmwareType::Unknown => {
|
||||
Self::PicoFido(PicoFidoFirmware::new(ver))
|
||||
Self::PicoFido(PicoFidoFirmware::new(firmware_version))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -326,8 +326,8 @@ impl PhyTag {
|
||||
///
|
||||
/// Returns `None` if the value doesn't match any known tag.
|
||||
/// Used when parsing device responses that contain raw tag bytes.
|
||||
pub fn from_u8(val: u8) -> Option<Self> {
|
||||
match val {
|
||||
pub fn from_u8(tag_value: u8) -> Option<Self> {
|
||||
match tag_value {
|
||||
0x00 => Some(Self::VidPid),
|
||||
0x04 => Some(Self::LedGpio),
|
||||
0x05 => Some(Self::LedBrightness),
|
||||
@@ -538,8 +538,8 @@ impl LedColor {
|
||||
/// Convert a raw u8 value to a LedColor enum variant.
|
||||
///
|
||||
/// Returns `None` if the value doesn't match any known color.
|
||||
pub fn from_u8(val: u8) -> Option<Self> {
|
||||
match val {
|
||||
pub fn from_u8(color_value: u8) -> Option<Self> {
|
||||
match color_value {
|
||||
0 => Some(Self::Off),
|
||||
1 => Some(Self::Red),
|
||||
2 => Some(Self::Green),
|
||||
|
||||
+71
-66
@@ -242,7 +242,7 @@ impl RescueOperations for PcscTransport {
|
||||
|
||||
// 2. Read Flash Info
|
||||
let mut rx_buf = [0; 256];
|
||||
let rx_flash = self.transmit(
|
||||
let flash_response = self.transmit(
|
||||
&[
|
||||
APDU_CLA_PROPRIETARY,
|
||||
RescueInstruction::Read as u8,
|
||||
@@ -253,21 +253,21 @@ impl RescueOperations for PcscTransport {
|
||||
&mut rx_buf,
|
||||
)?;
|
||||
|
||||
if !rx_flash.ends_with(&SW_SUCCESS) {
|
||||
if !flash_response.ends_with(&SW_SUCCESS) {
|
||||
return Err(PFError::Device("Failed to read flash".into()));
|
||||
}
|
||||
|
||||
let mut rdr = Cursor::new(&rx_flash[..rx_flash.len() - 2]);
|
||||
let _free = rdr.read_u32::<BigEndian>().unwrap_or(0);
|
||||
let used = rdr.read_u32::<BigEndian>().unwrap_or(0);
|
||||
let total = rdr.read_u32::<BigEndian>().unwrap_or(0);
|
||||
let mut cursor = Cursor::new(&flash_response[..flash_response.len() - 2]);
|
||||
let _free = cursor.read_u32::<BigEndian>().unwrap_or(0);
|
||||
let used = cursor.read_u32::<BigEndian>().unwrap_or(0);
|
||||
let total = cursor.read_u32::<BigEndian>().unwrap_or(0);
|
||||
|
||||
// NOTE: captured but currently unused variables
|
||||
let _nfiles = rdr.read_u32::<BigEndian>().unwrap_or(0);
|
||||
let _chip_size = rdr.read_u32::<BigEndian>().unwrap_or(0);
|
||||
let _nfiles = cursor.read_u32::<BigEndian>().unwrap_or(0);
|
||||
let _chip_size = cursor.read_u32::<BigEndian>().unwrap_or(0);
|
||||
|
||||
// --- Read Secure Boot Status ---
|
||||
let rx_secure = self.transmit(
|
||||
let secure_response = self.transmit(
|
||||
&[
|
||||
APDU_CLA_PROPRIETARY,
|
||||
RescueInstruction::Read as u8,
|
||||
@@ -278,13 +278,13 @@ impl RescueOperations for PcscTransport {
|
||||
&mut rx_buf,
|
||||
)?;
|
||||
|
||||
let (sb_enabled, sb_locked) = if rx_secure.ends_with(&[0x90, 0x00]) && rx_secure.len() >= 4
|
||||
{
|
||||
(rx_secure[0] != 0, rx_secure[1] != 0)
|
||||
} else {
|
||||
(false, false)
|
||||
}; // --- Read PHY Config ---
|
||||
let rx_phy = self.transmit(
|
||||
let (sb_enabled, sb_locked) =
|
||||
if secure_response.ends_with(&[0x90, 0x00]) && secure_response.len() >= 4 {
|
||||
(secure_response[0] != 0, secure_response[1] != 0)
|
||||
} else {
|
||||
(false, false)
|
||||
}; // --- Read PHY Config ---
|
||||
let phy_response = self.transmit(
|
||||
&[
|
||||
APDU_CLA_PROPRIETARY,
|
||||
RescueInstruction::Read as u8,
|
||||
@@ -295,61 +295,61 @@ impl RescueOperations for PcscTransport {
|
||||
&mut rx_buf,
|
||||
)?;
|
||||
|
||||
if !rx_phy.ends_with(&[0x90, 0x00]) {
|
||||
if !phy_response.ends_with(&[0x90, 0x00]) {
|
||||
return Err(PFError::Device("Failed to read config".into()));
|
||||
}
|
||||
|
||||
// Parse TLV
|
||||
let mut config = AppConfig::default();
|
||||
let data = &rx_phy[..rx_phy.len() - 2];
|
||||
let mut i = 0;
|
||||
while i < data.len() {
|
||||
if i + 2 > data.len() {
|
||||
let data = &phy_response[..phy_response.len() - 2];
|
||||
let mut offset = 0;
|
||||
while offset < data.len() {
|
||||
if offset + 2 > data.len() {
|
||||
break;
|
||||
}
|
||||
let tag_byte = data[i];
|
||||
let len = data[i + 1] as usize;
|
||||
i += 2;
|
||||
if i + len > data.len() {
|
||||
let tag_byte = data[offset];
|
||||
let field_len = data[offset + 1] as usize;
|
||||
offset += 2;
|
||||
if offset + field_len > data.len() {
|
||||
break;
|
||||
}
|
||||
let val = &data[i..i + len];
|
||||
let field_data = &data[offset..offset + field_len];
|
||||
|
||||
if let Some(tag) = PhyTag::from_u8(tag_byte) {
|
||||
match tag {
|
||||
PhyTag::VidPid => {
|
||||
if val.len() == 4 {
|
||||
let vid = u16::from_be_bytes([val[0], val[1]]);
|
||||
let pid = u16::from_be_bytes([val[2], val[3]]);
|
||||
if field_data.len() == 4 {
|
||||
let vid = u16::from_be_bytes([field_data[0], field_data[1]]);
|
||||
let pid = u16::from_be_bytes([field_data[2], field_data[3]]);
|
||||
config.vid = format!("{:04X}", vid);
|
||||
config.pid = format!("{:04X}", pid);
|
||||
}
|
||||
}
|
||||
PhyTag::LedGpio => {
|
||||
if !val.is_empty() {
|
||||
config.led_gpio = val[0];
|
||||
if !field_data.is_empty() {
|
||||
config.led_gpio = field_data[0];
|
||||
}
|
||||
}
|
||||
PhyTag::LedBrightness => {
|
||||
if !val.is_empty() {
|
||||
config.led_brightness = val[0];
|
||||
if !field_data.is_empty() {
|
||||
config.led_brightness = field_data[0];
|
||||
}
|
||||
}
|
||||
PhyTag::PresenceTimeout => {
|
||||
if !val.is_empty() {
|
||||
config.touch_timeout = val[0];
|
||||
if !field_data.is_empty() {
|
||||
config.touch_timeout = field_data[0];
|
||||
}
|
||||
}
|
||||
PhyTag::UsbProduct => {
|
||||
let s = std::str::from_utf8(val)
|
||||
let product_str = std::str::from_utf8(field_data)
|
||||
.unwrap_or("")
|
||||
.trim_matches(char::from(0));
|
||||
config.product_name = s.to_string();
|
||||
config.product_name = product_str.to_string();
|
||||
}
|
||||
PhyTag::Opts => {
|
||||
if val.len() >= 2 {
|
||||
let opts_val = u16::from_be_bytes([val[0], val[1]]);
|
||||
let opts = RescueOptions::from_bits_truncate(opts_val);
|
||||
if field_data.len() >= 2 {
|
||||
let options_raw = u16::from_be_bytes([field_data[0], field_data[1]]);
|
||||
let opts = RescueOptions::from_bits_truncate(options_raw);
|
||||
|
||||
config.led_dimmable = opts.contains(RescueOptions::LED_DIMMABLE);
|
||||
config.power_cycle_on_reset =
|
||||
@@ -358,36 +358,41 @@ impl RescueOperations for PcscTransport {
|
||||
}
|
||||
}
|
||||
PhyTag::Curves => {
|
||||
if val.len() == 4 {
|
||||
let curves_val = u32::from_be_bytes([val[0], val[1], val[2], val[3]]);
|
||||
config.raw_curves_mask = Some(curves_val);
|
||||
let curves = RescueCurves::from_bits_truncate(curves_val);
|
||||
if field_data.len() == 4 {
|
||||
let raw_curves_value = u32::from_be_bytes([
|
||||
field_data[0],
|
||||
field_data[1],
|
||||
field_data[2],
|
||||
field_data[3],
|
||||
]);
|
||||
config.raw_curves_mask = Some(raw_curves_value);
|
||||
let curves = RescueCurves::from_bits_truncate(raw_curves_value);
|
||||
config.enable_secp256k1 = curves.contains(RescueCurves::SECP256K1);
|
||||
}
|
||||
}
|
||||
PhyTag::LedDriver => {
|
||||
if !val.is_empty() {
|
||||
config.led_driver = Some(val[0]);
|
||||
if !field_data.is_empty() {
|
||||
config.led_driver = Some(field_data[0]);
|
||||
}
|
||||
}
|
||||
PhyTag::LedOrder => {
|
||||
if !val.is_empty() {
|
||||
config.led_order = Some(val[0]);
|
||||
if !field_data.is_empty() {
|
||||
config.led_order = Some(field_data[0]);
|
||||
}
|
||||
}
|
||||
PhyTag::LedNum => {
|
||||
if !val.is_empty() {
|
||||
config.led_num = Some(val[0]);
|
||||
if !field_data.is_empty() {
|
||||
config.led_num = Some(field_data[0]);
|
||||
}
|
||||
}
|
||||
PhyTag::EnabledUsbItf => {
|
||||
if !val.is_empty() {
|
||||
config.enabled_usb_itf = Some(val[0]);
|
||||
if !field_data.is_empty() {
|
||||
config.enabled_usb_itf = Some(field_data[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
i += len;
|
||||
offset += field_len;
|
||||
}
|
||||
|
||||
log::info!(
|
||||
@@ -779,34 +784,34 @@ impl RescueOperations for PcscTransport {
|
||||
};
|
||||
|
||||
let mut config = ManagementAppConfig::default();
|
||||
let mut i = 0;
|
||||
while i < tlv_data.len() {
|
||||
if i + 2 > tlv_data.len() {
|
||||
let mut offset = 0;
|
||||
while offset < tlv_data.len() {
|
||||
if offset + 2 > tlv_data.len() {
|
||||
break;
|
||||
}
|
||||
let tag = tlv_data[i];
|
||||
let len = tlv_data[i + 1] as usize;
|
||||
i += 2;
|
||||
if i + len > tlv_data.len() {
|
||||
let tag = tlv_data[offset];
|
||||
let field_len = tlv_data[offset + 1] as usize;
|
||||
offset += 2;
|
||||
if offset + field_len > tlv_data.len() {
|
||||
break;
|
||||
}
|
||||
let val = &tlv_data[i..i + len];
|
||||
let field_data = &tlv_data[offset..offset + field_len];
|
||||
match tag {
|
||||
MGMT_TAG_USB_SUPPORTED => {
|
||||
if val.len() >= 2 {
|
||||
config.usb_supported = u16::from_be_bytes([val[0], val[1]]);
|
||||
if field_data.len() >= 2 {
|
||||
config.usb_supported = u16::from_be_bytes([field_data[0], field_data[1]]);
|
||||
}
|
||||
}
|
||||
MGMT_TAG_USB_ENABLED => {
|
||||
if val.len() >= 2 {
|
||||
config.usb_enabled = u16::from_be_bytes([val[0], val[1]]);
|
||||
if field_data.len() >= 2 {
|
||||
config.usb_enabled = u16::from_be_bytes([field_data[0], field_data[1]]);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
log::trace!("Management TLV tag 0x{:02X} skipped", tag);
|
||||
}
|
||||
}
|
||||
i += len;
|
||||
offset += field_len;
|
||||
}
|
||||
|
||||
log::info!(
|
||||
|
||||
+51
-36
@@ -225,12 +225,15 @@ impl HidTransport {
|
||||
fn init_channel(device: &hidapi::HidDevice) -> Result<u32, PFError> {
|
||||
log::debug!("Initializing CTAPHID channel...");
|
||||
|
||||
let mut drain_buf = [0u8; HID_REPORT_SIZE];
|
||||
while let Ok(n) = device.read_timeout(&mut drain_buf[..], HID_READ_TIMEOUT_MS) {
|
||||
let mut stale_packet_buffer = [0u8; HID_REPORT_SIZE];
|
||||
while let Ok(n) = device.read_timeout(&mut stale_packet_buffer[..], HID_READ_TIMEOUT_MS) {
|
||||
if n == 0 {
|
||||
break;
|
||||
}
|
||||
log::trace!("Drained stale HID packet: {:02X?}", &drain_buf[0..16]);
|
||||
log::trace!(
|
||||
"Drained stale HID packet: {:02X?}",
|
||||
&stale_packet_buffer[0..16]
|
||||
);
|
||||
}
|
||||
|
||||
let mut nonce = [0u8; 8];
|
||||
@@ -253,24 +256,29 @@ impl HidTransport {
|
||||
// Read Response until we find our nonce
|
||||
let start = std::time::Instant::now();
|
||||
while start.elapsed() < Duration::from_secs(1) {
|
||||
let mut buf = [0u8; HID_REPORT_SIZE];
|
||||
let mut init_buf = [0u8; HID_REPORT_SIZE];
|
||||
if device
|
||||
.read_timeout(&mut buf[..], HID_INIT_READ_TIMEOUT_MS)
|
||||
.read_timeout(&mut init_buf[..], HID_INIT_READ_TIMEOUT_MS)
|
||||
.is_ok()
|
||||
{
|
||||
// Check if response matches our broadcast and nonce
|
||||
if buf[0..4] == CTAPHID_CID_BROADCAST.to_be_bytes()
|
||||
&& buf[4] == CTAPHID_INIT
|
||||
&& buf[7..15] == nonce
|
||||
if init_buf[0..4] == CTAPHID_CID_BROADCAST.to_be_bytes()
|
||||
&& init_buf[4] == CTAPHID_INIT
|
||||
&& init_buf[7..15] == nonce
|
||||
{
|
||||
// New CID is at bytes 16..20
|
||||
let new_cid = u32::from_be_bytes([buf[15], buf[16], buf[17], buf[18]]);
|
||||
let new_cid = u32::from_be_bytes([
|
||||
init_buf[15],
|
||||
init_buf[16],
|
||||
init_buf[17],
|
||||
init_buf[18],
|
||||
]);
|
||||
log::debug!("Channel negotiation successful. New CID: 0x{:08X}", new_cid);
|
||||
return Ok(new_cid);
|
||||
} else {
|
||||
log::trace!(
|
||||
"Received ignoreable HID packet during CID negotiation: {:02X?}",
|
||||
&buf[0..16]
|
||||
&init_buf[0..16]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -408,12 +416,15 @@ impl HidTransport {
|
||||
log::error!("Device sent empty payload response.");
|
||||
return Err(PFError::Device("Empty response".into()));
|
||||
}
|
||||
let status = response_data[0];
|
||||
if status != 0x00 {
|
||||
log::error!("FIDO Operation returned failure status: 0x{:02X}", status);
|
||||
let ctap_status_byte = response_data[0];
|
||||
if ctap_status_byte != 0x00 {
|
||||
log::error!(
|
||||
"FIDO Operation returned failure status: 0x{:02X}",
|
||||
ctap_status_byte
|
||||
);
|
||||
return Err(PFError::Device(format!(
|
||||
"FIDO Operation Failed with Status: 0x{:02X}",
|
||||
status
|
||||
ctap_status_byte
|
||||
)));
|
||||
}
|
||||
|
||||
@@ -436,18 +447,18 @@ impl HidTransport {
|
||||
fn read_hid_response(&self, cmd: u8, timeout_ms: i32) -> Result<Vec<u8>, PFError> {
|
||||
log::debug!("Waiting for response...");
|
||||
|
||||
let mut buf = [0u8; HID_REPORT_SIZE];
|
||||
let mut packet_buf = [0u8; HID_REPORT_SIZE];
|
||||
let mut response_data = Vec::new();
|
||||
let expected_len: usize;
|
||||
let mut read_len = 0;
|
||||
let mut last_seq = 0;
|
||||
|
||||
let start_time = std::time::Instant::now();
|
||||
let deadline_start = std::time::Instant::now();
|
||||
let timeout_duration = std::time::Duration::from_millis(timeout_ms as u64);
|
||||
|
||||
// 1. Read First Packet (Loop to handle Keepalives)
|
||||
// 1. Read First Packet (Keepalive Loop)
|
||||
loop {
|
||||
if start_time.elapsed() > timeout_duration {
|
||||
if deadline_start.elapsed() > timeout_duration {
|
||||
log::error!("Timeout waiting for device response (Keepalive limit exceeded)");
|
||||
return Err(PFError::Device(
|
||||
"Timeout waiting for device response (Keepalive limit exceeded)".into(),
|
||||
@@ -456,7 +467,7 @@ impl HidTransport {
|
||||
|
||||
if let Err(e) = self
|
||||
.device
|
||||
.read_timeout(&mut buf[..], HID_RESP_READ_TIMEOUT_MS)
|
||||
.read_timeout(&mut packet_buf[..], HID_RESP_READ_TIMEOUT_MS)
|
||||
{
|
||||
log::error!("Timeout reading response packet: {}", e);
|
||||
return Err(PFError::Io(format!(
|
||||
@@ -466,50 +477,52 @@ impl HidTransport {
|
||||
}
|
||||
|
||||
// Check CID mismatch
|
||||
if u32::from_be_bytes([buf[0], buf[1], buf[2], buf[3]]) != self.cid {
|
||||
if u32::from_be_bytes([packet_buf[0], packet_buf[1], packet_buf[2], packet_buf[3]])
|
||||
!= self.cid
|
||||
{
|
||||
log::warn!("Received packet from different CID, ignoring...");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check for KEEPALIVE (0xBB)
|
||||
if buf[4] == CTAPHID_KEEPALIVE {
|
||||
let status = buf[5]; // Keepalive status byte
|
||||
if packet_buf[4] == CTAPHID_KEEPALIVE {
|
||||
let keepalive_status = packet_buf[5];
|
||||
log::debug!(
|
||||
"Device sent KEEPALIVE (Status: 0x{:02X}), waiting...",
|
||||
status
|
||||
keepalive_status
|
||||
);
|
||||
continue; // Go back to start of loop and read again
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we are here, it's a real response
|
||||
break;
|
||||
}
|
||||
|
||||
if buf[4] == CTAPHID_ERROR {
|
||||
log::error!("Device returned CTAP Error code: 0x{:02X}", buf[5]);
|
||||
if packet_buf[4] == CTAPHID_ERROR {
|
||||
log::error!("Device returned CTAP Error code: 0x{:02X}", packet_buf[5]);
|
||||
return Err(PFError::Device(format!(
|
||||
"Device returned CTAP Error: 0x{:02X}",
|
||||
buf[5],
|
||||
packet_buf[5],
|
||||
)));
|
||||
} else {
|
||||
log::trace!("Packet received is not a CTAP Error");
|
||||
}
|
||||
|
||||
if buf[4] == cmd {
|
||||
expected_len = u16::from_be_bytes([buf[5], buf[6]]) as usize;
|
||||
if packet_buf[4] == cmd {
|
||||
expected_len = u16::from_be_bytes([packet_buf[5], packet_buf[6]]) as usize;
|
||||
let in_pkt = std::cmp::min(expected_len, HID_REPORT_SIZE - 7);
|
||||
response_data.extend_from_slice(&buf[7..7 + in_pkt]);
|
||||
response_data.extend_from_slice(&packet_buf[7..7 + in_pkt]);
|
||||
read_len += in_pkt;
|
||||
// log::trace!("Received Init Response. Expecting {} bytes total.", expected_len);
|
||||
} else {
|
||||
log::error!(
|
||||
"Unexpected command response: 0x{:02X} (Expected 0x{:02X})",
|
||||
buf[4],
|
||||
packet_buf[4],
|
||||
cmd
|
||||
);
|
||||
return Err(PFError::Device(format!(
|
||||
"Unexpected command response: 0x{:02X} (Expected 0x{:02X})",
|
||||
buf[4], cmd
|
||||
packet_buf[4], cmd
|
||||
)));
|
||||
}
|
||||
|
||||
@@ -517,7 +530,7 @@ impl HidTransport {
|
||||
while read_len < expected_len {
|
||||
if let Err(e) = self
|
||||
.device
|
||||
.read_timeout(&mut buf[..], HID_CONT_READ_TIMEOUT_MS)
|
||||
.read_timeout(&mut packet_buf[..], HID_CONT_READ_TIMEOUT_MS)
|
||||
{
|
||||
log::error!("Timeout reading continuation packet: {}", e);
|
||||
return Err(PFError::Io(format!(
|
||||
@@ -526,11 +539,13 @@ impl HidTransport {
|
||||
)));
|
||||
}
|
||||
|
||||
if u32::from_be_bytes([buf[0], buf[1], buf[2], buf[3]]) != self.cid {
|
||||
if u32::from_be_bytes([packet_buf[0], packet_buf[1], packet_buf[2], packet_buf[3]])
|
||||
!= self.cid
|
||||
{
|
||||
continue; // Ignore packets from other channels
|
||||
}
|
||||
|
||||
let seq = buf[4];
|
||||
let seq = packet_buf[4];
|
||||
if seq != last_seq {
|
||||
log::error!(
|
||||
"Sequence mismatch in response. Expected {}, got {}",
|
||||
@@ -542,7 +557,7 @@ impl HidTransport {
|
||||
last_seq += 1;
|
||||
|
||||
let in_pkt = std::cmp::min(expected_len - read_len, HID_REPORT_SIZE - 5);
|
||||
response_data.extend_from_slice(&buf[5..5 + in_pkt]);
|
||||
response_data.extend_from_slice(&packet_buf[5..5 + in_pkt]);
|
||||
read_len += in_pkt;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user