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:
Suyog Tandel
2026-07-09 21:48:12 +05:30
parent a43f840a1a
commit c24999d3a4
6 changed files with 300 additions and 268 deletions
+105 -97
View File
@@ -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
View File
@@ -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
View File
@@ -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))
}
}
}
+4 -4
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}