mirror of
https://github.com/librekeys/picoforge.git
synced 2026-07-28 08:01:19 -07:00
@@ -15,6 +15,7 @@ let
|
||||
webkitgtk_4_1
|
||||
pcsclite
|
||||
hidapi
|
||||
mesa
|
||||
];
|
||||
|
||||
packages = with pkgs; [
|
||||
@@ -58,6 +59,10 @@ pkgs.mkShell {
|
||||
export LD_LIBRARY_PATH=${pkgs.lib.makeLibraryPath libraries}:$LD_LIBRARY_PATH
|
||||
export XDG_DATA_DIRS=$GSETTINGS_SCHEMAS_PATH:$XDG_DATA_DIRS
|
||||
|
||||
# Try to uncomment the following lines if you encounter EGL_BAD_PARAMETER errors:
|
||||
# export LIBGL_ALWAYS_SOFTWARE=1
|
||||
# export WEBKIT_DISABLE_COMPOSITING_MODE=1
|
||||
|
||||
echo "Nix development environment loaded!"
|
||||
echo "Available tools: rustc, cargo, deno, node, tauri"
|
||||
'';
|
||||
|
||||
@@ -372,4 +372,94 @@ impl HidTransport {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Send authenticatorConfig command to set minimum PIN length.
|
||||
///
|
||||
/// This bypasses the ctap-hid-fido2 library which has a bug where it sends
|
||||
/// CBOR map keys out of order (0x01, 0x03, 0x04, 0x02) instead of the required
|
||||
/// ascending order (0x01, 0x02, 0x03, 0x04). The pico-fido firmware strictly
|
||||
/// enforces canonical CBOR ordering per CTAP2 spec.
|
||||
pub fn send_config_set_min_pin_length(
|
||||
&self,
|
||||
pin_token: &[u8],
|
||||
new_min_pin_length: u8,
|
||||
) -> Result<(), PFError> {
|
||||
log::debug!(
|
||||
"Sending setMinPINLength config command (new length: {})...",
|
||||
new_min_pin_length
|
||||
);
|
||||
|
||||
// Build subCommandParams (Key 0x02): { 0x01: newMinPINLength }
|
||||
let mut sub_params_map = BTreeMap::new();
|
||||
sub_params_map.insert(
|
||||
Value::Integer(ConfigSubCommandParam::NewMinPinLength as i128),
|
||||
Value::Integer(new_min_pin_length as i128),
|
||||
);
|
||||
let sub_params = Value::Map(sub_params_map);
|
||||
let sub_params_bytes = to_vec(&sub_params).map_err(|e| PFError::Io(e.to_string()))?;
|
||||
|
||||
// Build HMAC message for signing
|
||||
// Per FIDO 2.1 spec: authenticate(pinUvAuthToken, 32×0xff || 0x0d || uint8(subCommand) || subCommandParams)
|
||||
let mut message = vec![0xff; 32];
|
||||
message.push(CtapCommand::Config as u8); // 0x0d
|
||||
message.push(ConfigSubCommand::SetMinPinLength as u8); // 0x03
|
||||
message.extend(&sub_params_bytes);
|
||||
|
||||
// Sign using provided PIN token (Protocol 1 uses HMAC-SHA256, first 16 bytes)
|
||||
use ring::hmac;
|
||||
let hmac_key = hmac::Key::new(hmac::HMAC_SHA256, pin_token);
|
||||
let sig = hmac::sign(&hmac_key, &message);
|
||||
let pin_auth = sig.as_ref()[0..16].to_vec();
|
||||
|
||||
// Build full authenticatorConfig map with keys in ASCENDING ORDER
|
||||
// This is critical - the firmware parser rejects out-of-order keys with CTAP2_ERR_INVALID_CBOR
|
||||
let mut config_map = BTreeMap::new();
|
||||
config_map.insert(
|
||||
Value::Integer(ConfigParam::SubCommand as i128), // 0x01
|
||||
Value::Integer(ConfigSubCommand::SetMinPinLength as i128), // 0x03
|
||||
);
|
||||
config_map.insert(
|
||||
Value::Integer(ConfigParam::SubCommandParams as i128), // 0x02
|
||||
sub_params,
|
||||
);
|
||||
config_map.insert(
|
||||
Value::Integer(ConfigParam::PinUvAuthProtocol as i128), // 0x03
|
||||
Value::Integer(1), // PIN protocol version 1
|
||||
);
|
||||
config_map.insert(
|
||||
Value::Integer(ConfigParam::PinUvAuthParam as i128), // 0x04
|
||||
Value::Bytes(pin_auth),
|
||||
);
|
||||
|
||||
let config_payload_cbor =
|
||||
to_vec(&Value::Map(config_map)).map_err(|e| PFError::Io(e.to_string()))?;
|
||||
|
||||
// Prepend CTAP command byte
|
||||
let mut payload = vec![CtapCommand::Config as u8];
|
||||
payload.extend(config_payload_cbor);
|
||||
|
||||
// Send via HID
|
||||
match self.send_cbor(CTAPHID_CBOR, &payload) {
|
||||
Ok(_) => {
|
||||
log::info!(
|
||||
"Successfully set minimum PIN length to {}",
|
||||
new_min_pin_length
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => {
|
||||
let err_str = e.to_string();
|
||||
log::error!("Failed to send setMinPINLength config: {}", err_str);
|
||||
|
||||
// Check for PIN policy violation (0x37) - cannot decrease min PIN length
|
||||
if err_str.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()
|
||||
));
|
||||
}
|
||||
|
||||
Err(PFError::Device(format!("setMinPINLength failed: {}", e)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,13 +79,39 @@ pub(crate) fn set_min_pin_length(
|
||||
current_pin: String,
|
||||
min_pin_length: u8,
|
||||
) -> Result<String, String> {
|
||||
let cfg = Cfg::init();
|
||||
let device = FidoKeyHidFactory::create(&cfg)
|
||||
.map_err(|e| format!("Failed to connect to FIDO device: {:?}", e))?;
|
||||
log::info!("Starting set_min_pin_length (custom implementation)...");
|
||||
|
||||
device
|
||||
.set_min_pin_length(min_pin_length, Some(¤t_pin))
|
||||
.map_err(|e| format!("Failed to set minimum PIN length: {:?}", e))?;
|
||||
// 1. Obtain PIN token using the library handle
|
||||
let pin_token = {
|
||||
let cfg = Cfg::init();
|
||||
let device = FidoKeyHidFactory::create(&cfg)
|
||||
.map_err(|e| format!("Could not connect to FIDO device: {:?}", e))?;
|
||||
|
||||
use ctap_hid_fido2::fidokey::pin::Permission;
|
||||
// Obtain a token with AuthenticatorConfiguration permission (CTAP 2.1)
|
||||
match device.get_pinuv_auth_token_with_permission(
|
||||
¤t_pin,
|
||||
Permission::AuthenticatorConfiguration,
|
||||
) {
|
||||
Ok(token) => {
|
||||
log::debug!("Successfully obtained PIN token with ACFG permission.");
|
||||
token.key
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("Failed to get PIN token with ACFG permission: {:?}", e);
|
||||
return Err(format!("Failed to obtain PIN token: {:?}", e));
|
||||
}
|
||||
}
|
||||
// Library handle 'device' is dropped here, closing the HID session.
|
||||
};
|
||||
|
||||
// 2. Open custom HidTransport and send command using the token because ctap-hid-fido2 has a bug where it sends CBOR map keys out of order (0x01, 0x03, 0x04, 0x02) instead of the required ascending order (0x01, 0x02, 0x03, 0x04). The pico-fido firmware strictly requires ascending order.
|
||||
let transport =
|
||||
HidTransport::open().map_err(|e| format!("Could not open HID transport: {}", e))?;
|
||||
|
||||
transport
|
||||
.send_config_set_min_pin_length(&pin_token, min_pin_length)
|
||||
.map_err(|e| format!("Failed to set minimum PIN length: {}", e))?;
|
||||
|
||||
Ok(format!(
|
||||
"Minimum PIN length successfully set to {}",
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
<AlertDialog.Cancel onclick={() => (configState.minPinDialogOpen = false)}
|
||||
>Cancel</AlertDialog.Cancel
|
||||
>
|
||||
<AlertDialog.Action onclick={configState.handleMinPinChange}
|
||||
<AlertDialog.Action onclick={() => configState.handleMinPinChange()}
|
||||
>Update</AlertDialog.Action
|
||||
>
|
||||
</AlertDialog.Footer>
|
||||
|
||||
@@ -49,14 +49,14 @@
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between p-4 border rounded-lg opacity-60">
|
||||
<div class="flex items-center justify-between p-4 border rounded-lg">
|
||||
<div class="space-y-1">
|
||||
<p class="font-medium">Minimum PIN Length</p>
|
||||
<p class="text-sm text-muted-foreground">
|
||||
Current: {device.fidoInfo?.minPinLength || 4} characters
|
||||
</p>
|
||||
</div>
|
||||
<Button variant="outline" disabled={true}>Update Minimum Length</Button>
|
||||
<Button variant="outline" disabled={!device.fidoInfo?.options?.clientPin} onclick={() => state.openMinPinDialog()}>Update Minimum Length</Button>
|
||||
</div>
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
|
||||
Reference in New Issue
Block a user