You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
186 lines
6.6 KiB
Rust
186 lines
6.6 KiB
Rust
use ctap2_proto::{prelude::*, Ctap2_2Authenticator};
|
|
use ctaphid::types::Command;
|
|
|
|
pub struct Device<D: ctaphid::HidDevice>(ctaphid::Device<D>);
|
|
|
|
impl<D: ctaphid::HidDevice> Device<D> {
|
|
fn send_raw(&self, command: Command, bytes: &[u8]) -> Result<Vec<u8>, ctaphid::error::Error> {
|
|
self.0.ctap2(command.into(), bytes)
|
|
}
|
|
|
|
fn send<Req, Res>(&self, req: Req) -> Result<Res, ctaphid::error::Error>
|
|
where
|
|
Req: serde::Serialize,
|
|
Res: for<'de> serde::Deserialize<'de>,
|
|
{
|
|
let command = Command::Cbor;
|
|
let mut data: Vec<u8> = Vec::new();
|
|
ciborium::ser::into_writer(&req, &mut data).map_err(|e| match e {
|
|
ciborium::ser::Error::Io(_) => ctaphid::error::RequestError::IncompleteWrite,
|
|
ciborium::ser::Error::Value(desc) => {
|
|
ctaphid::error::RequestError::PacketSendingFailed(desc.into())
|
|
}
|
|
})?;
|
|
|
|
let response = self.0.ctap2(command.into(), &data)?;
|
|
|
|
match ciborium::de::from_reader(response.as_slice()) {
|
|
Ok(res) => Ok(res),
|
|
Err(e) => match e {
|
|
ciborium::de::Error::Io(_) => todo!(),
|
|
ciborium::de::Error::Syntax(_) => todo!(),
|
|
ciborium::de::Error::Semantic(_, _) => todo!(),
|
|
ciborium::de::Error::RecursionLimitExceeded => todo!(),
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<D> Ctap2_2Authenticator for Device<D>
|
|
where
|
|
D: ctaphid::HidDevice,
|
|
{
|
|
fn make_credential(&mut self, request: make::Request) -> Result<make::Response, make::Error> {
|
|
// TODO: How the heck am i supposed to handle errors???
|
|
self.send(&request).map_err(|e| match e {
|
|
ctaphid::error::Error::CommandError(e) => match e {
|
|
ctaphid::error::CommandError::CborError(_) => todo!(),
|
|
ctaphid::error::CommandError::InvalidPingData => todo!(),
|
|
ctaphid::error::CommandError::NotSupported(_) => todo!(),
|
|
},
|
|
ctaphid::error::Error::RequestError(e) => match e {
|
|
ctaphid::error::RequestError::IncompleteWrite => todo!(),
|
|
ctaphid::error::RequestError::MessageFragmentationFailed(_) => todo!(),
|
|
ctaphid::error::RequestError::PacketSendingFailed(_) => todo!(),
|
|
ctaphid::error::RequestError::PacketSerializationFailed(_) => todo!(),
|
|
},
|
|
ctaphid::error::Error::ResponseError(e) => match e {
|
|
ctaphid::error::ResponseError::CommandFailed(_) => todo!(),
|
|
ctaphid::error::ResponseError::MessageDefragmentationFailed(_) => todo!(),
|
|
ctaphid::error::ResponseError::PacketParsingFailed(_) => todo!(),
|
|
ctaphid::error::ResponseError::PacketReceivingFailed(_) => todo!(),
|
|
ctaphid::error::ResponseError::Timeout => todo!(),
|
|
ctaphid::error::ResponseError::MissingErrorCode => todo!(),
|
|
ctaphid::error::ResponseError::UnexpectedCommand { expected, actual } => todo!(),
|
|
ctaphid::error::ResponseError::UnexpectedKeepAlive(_) => todo!(),
|
|
ctaphid::error::ResponseError::UnexpectedResponseData(_) => todo!(),
|
|
},
|
|
})
|
|
}
|
|
|
|
fn get_assertion(request: get::Request) -> Result<get::Response, get::Error> {
|
|
todo!()
|
|
}
|
|
|
|
fn get_info(&self) -> device::Info {
|
|
let info = self.send_raw(Command::Cbor, &[1u8, 4]).unwrap();
|
|
println!("info: {info:#?}");
|
|
let info: device::Info =
|
|
ciborium::de::from_reader::<device::Info, _>(info.as_slice()).unwrap();
|
|
info
|
|
}
|
|
|
|
fn client_pin(request: client_pin::Request) -> Result<client_pin::Response, client_pin::Error> {
|
|
todo!()
|
|
}
|
|
|
|
fn reset() -> Result<(), reset::Error> {
|
|
todo!()
|
|
}
|
|
|
|
fn selection() -> Result<(), ctap2_proto::authenticator::selection::Error> {
|
|
todo!()
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use std::ffi::CStr;
|
|
|
|
use ctap2_proto::Ctap2_2Authenticator;
|
|
use ctaphid::{
|
|
error::{RequestError, ResponseError},
|
|
types::Command,
|
|
};
|
|
struct HidDevice(hidapi::HidDevice);
|
|
#[derive(Debug)]
|
|
struct HidDeviceInfoMy(hidapi::DeviceInfo);
|
|
|
|
impl ctaphid::HidDeviceInfo for HidDeviceInfoMy {
|
|
fn vendor_id(&self) -> u16 {
|
|
hidapi::DeviceInfo::vendor_id(&self.0)
|
|
}
|
|
|
|
fn product_id(&self) -> u16 {
|
|
hidapi::DeviceInfo::product_id(&self.0)
|
|
}
|
|
|
|
fn path(&self) -> std::borrow::Cow<'_, str> {
|
|
let cstr: &CStr = hidapi::DeviceInfo::path(&self.0);
|
|
let s = cstr.to_str().unwrap();
|
|
std::borrow::Cow::from(s)
|
|
}
|
|
}
|
|
|
|
impl ctaphid::HidDevice for HidDevice {
|
|
type Info = HidDeviceInfoMy;
|
|
|
|
fn send(&self, data: &[u8]) -> Result<(), ctaphid::error::RequestError> {
|
|
println!("sending bytes: {data:#?}");
|
|
hidapi::HidDevice::write(&self.0, data)
|
|
.map_err(|e| RequestError::PacketSendingFailed(e.into()))?;
|
|
Ok(())
|
|
}
|
|
|
|
fn receive<'a>(
|
|
&self,
|
|
buffer: &'a mut [u8],
|
|
timeout: Option<std::time::Duration>,
|
|
) -> Result<&'a [u8], ctaphid::error::ResponseError> {
|
|
println!("reading bytes");
|
|
let duration = if let Some(timeout) = timeout {
|
|
i32::try_from(timeout.as_millis())
|
|
.map_err(|err| ResponseError::PacketReceivingFailed(err.into()))?
|
|
} else {
|
|
-1
|
|
};
|
|
let n = self
|
|
.0
|
|
.read_timeout(buffer, duration)
|
|
.map_err(|err| ResponseError::PacketReceivingFailed(err.into()))?;
|
|
if n == buffer.len() {
|
|
Ok(&buffer[1..n])
|
|
} else if n == 0 {
|
|
Err(ResponseError::Timeout)
|
|
} else {
|
|
Ok(&buffer[..n])
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn get_info() {
|
|
let hidapi = hidapi::HidApi::new().unwrap();
|
|
let devices = hidapi.device_list();
|
|
for device_info in devices {
|
|
let hid_device = hidapi::DeviceInfo::open_device(&device_info, &hidapi);
|
|
let hid_device = match hid_device {
|
|
Ok(hid_device) => hid_device,
|
|
Err(e) => {
|
|
println!("error: {e:#?}");
|
|
continue;
|
|
}
|
|
};
|
|
let device = HidDevice(hid_device);
|
|
let device =
|
|
ctaphid::Device::new(device, HidDeviceInfoMy(device_info.to_owned())).unwrap();
|
|
let device = super::Device(device);
|
|
println!("info: {:#?}", device.0.ctap2(Command::Cbor.into(), &[]));
|
|
}
|
|
assert!(false);
|
|
}
|
|
|
|
#[test]
|
|
fn quickstart() {}
|
|
}
|