usesuper::TestDevice; usecrate::consts::{HIDCmd, CID_BROADCAST}; usecrate::ctap2::commands::{CommandError, RequestCtap1, RequestCtap2, Retryable, StatusCode}; usecrate::transport::errors::{ApduErrorStatus, HIDError}; usecrate::transport::{FidoDevice, FidoDeviceIO, FidoProtocol}; usecrate::u2ftypes::{U2FDeviceInfo, U2FHIDCont, U2FHIDInit, U2FHIDInitResp}; usecrate::util::io_err; use rand::{thread_rng, RngCore}; use std::cmp::Eq; use std::fmt; use std::hash::Hash; use std::io; use std::io::{Read, Write}; use std::thread; use std::time::Duration;
// Open device, verify that it is indeed a CTAP device and potentially read initial values fn new(parameters: Self::BuildParameters) -> Result<Self, (HIDError, Self::Id)>; fn id(&self) -> Self::Id;
// Send Init to broadcast address to create a new channel self.set_cid(CID_BROADCAST); let (cmd, raw) = HIDDevice::sendrecv(self, HIDCmd::Init, &nonce, &|| true)?; if cmd != HIDCmd::Init { return Err(HIDError::DeviceError);
}
let rsp = U2FHIDInitResp::read(&raw, &nonce)?; // Set the new Channel ID self.set_cid(rsp.cid);
let vendor = self
.get_property("Manufacturer")
.unwrap_or_else(|_| String::from("Unknown Vendor")); let product = self
.get_property("Product")
.unwrap_or_else(|_| String::from("Unknown Device"));
// A CTAPHID host SHALL accept a response size that is longer than the // anticipated size to allow for future extensions of the protocol, yet // maintaining backwards compatibility. Future versions will maintain // the response structure of the current version, but additional fields // may be added.
Ok(())
}
fn sendrecv(
&mutself,
cmd: HIDCmd,
send: &[u8],
keep_alive: &dynFn() -> bool,
) -> io::Result<(HIDCmd, Vec<u8>)> { self.u2f_write(cmd.into(), send)?;
debug!("sent to Device {:?} cmd={:?}: {:?}", self.id(), cmd, send); loop { let (cmd, data) = self.u2f_read()?; if cmd != HIDCmd::Keepalive {
debug!( "got from Device {:?} status={:?}: {:?}", self.id(),
cmd,
data
); return Ok((cmd, data));
} // The authenticator might send us HIDCmd::Keepalive messages indefinitely, e.g. if // it's waiting for user presence. The keep_alive function is used to cancel the // transaction. if !keep_alive() { break;
}
}
// If this is a CTAP2 device we can tell the authenticator to cancel the transaction on its // side as well. There's nothing to do for U2F/CTAP1 devices. ifself.get_protocol() == FidoProtocol::CTAP2 { self.u2f_write(u8::from(HIDCmd::Cancel), &[])?;
} // For CTAP2 devices we expect to read // (HIDCmd::Cbor, [CTAP2_ERR_KEEPALIVE_CANCEL]) // for U2F/CTAP1 we expect to read // (HIDCmd::Keepalive, [status]). self.u2f_read()
}
fn u2f_read(&mutself) -> io::Result<(HIDCmd, Vec<u8>)> { // Now we read. This happens in 2 chunks: The initial packet, which has // the size we expect overall, then continuation packets, which will // fill in data until we have everything. let (cmd, data) = { let (cmd, mut data) = U2FHIDInit::read(self)?;
while keep_alive() { // sendrecv will not block with a CTAP1 device let (cmd, mut data) = self.sendrecv(HIDCmd::Msg, &data, &|| true)?; if cmd == HIDCmd::Msg { if data.len() < 2 { return Err(io_err("Unexpected Response: shorter than expected").into());
} let split_at = data.len() - 2; let status = data.split_off(split_at); // This will bubble up error if status != no error let status = ApduErrorStatus::from([status[0], status[1]]);
match msg.handle_response_ctap1(self, status, &data, &add_info) {
Ok(out) => return Ok(out),
Err(Retryable::Retry) => { // sleep 100ms then loop again // TODO(baloo): meh, use tokio instead?
thread::sleep(Duration::from_millis(100));
}
Err(Retryable::Error(e)) => return Err(e),
}
} else { return Err(HIDError::UnexpectedCmd(cmd.into()));
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.