use crate::ssh::PrivateKeyKind;
use crate::ssh::PublicKeyKind;
use crate::utils::format_signature_for_ssh;
use crate::PrivateKey;
use ctap_hid_fido2::{fidokey::GetAssertionArgsBuilder, Cfg, FidoKeyHid, HidParam};
pub fn sign_with_private_key(private_key: &PrivateKey, challenge: &[u8]) -> Option<Vec<u8>> {
let (handle, pin, device_path): (&Vec<u8>, _, _) = match &private_key.kind {
PrivateKeyKind::EcdsaSk(key) => (
key.handle.as_ref(),
key.pin.as_ref(),
key.device_path.as_ref(),
),
PrivateKeyKind::Ed25519Sk(key) => (
key.handle.as_ref(),
key.pin.as_ref(),
key.device_path.as_ref(),
),
_ => return None,
};
let sk_application = match &private_key.pubkey.kind {
PublicKeyKind::Ed25519(key) => key.sk_application.as_ref(),
PublicKeyKind::Ecdsa(key) => key.sk_application.as_ref(),
_ => return None,
};
let sk_application = match sk_application {
Some(sk) => sk,
None => return None
};
let device = if let Some(path) = &device_path {
FidoKeyHid::new(&[HidParam::Path(path.to_string())], &Cfg::init())
} else {
let fido_devices: Vec<HidParam> = ctap_hid_fido2::get_fidokey_devices()
.into_iter()
.map(|x| x.param)
.collect();
FidoKeyHid::new(&fido_devices, &Cfg::init())
};
let device = device.ok()?;
let args = GetAssertionArgsBuilder::new(&sk_application, &challenge).credential_id(handle);
let args = if let Some(pin) = pin {
args.pin(pin)
} else {
args.without_pin_and_uv()
};
let mut assert = device.get_assertion_with_args(&args.build()).ok()?;
let assert = assert.pop()?;
let signature = &assert.signature;
let mut format = format_signature_for_ssh(&private_key.pubkey, &signature)?;
format.push(assert.flags.as_u8());
format.extend_from_slice(&assert.sign_count.to_be_bytes());
Some(format)
}