Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/devices/src/usb/backend/fido_backend/fido_guest.rs
5394 views
1
// Copyright 2023 The ChromiumOS Authors
2
// Use of this source code is governed by a BSD-style license that can be
3
// found in the LICENSE file.
4
5
use std::collections::VecDeque;
6
7
use base::error;
8
use usb_util::TransferBuffer;
9
10
use crate::usb::backend::fido_backend::constants;
11
use crate::usb::backend::fido_backend::error::Error;
12
use crate::usb::backend::fido_backend::error::Result;
13
use crate::usb::backend::fido_backend::poll_thread::PollTimer;
14
use crate::usb::backend::fido_backend::transfer::FidoTransfer;
15
16
/// `FidoGuestKey` is the struct representation of a virtual fido device as seen by the guest VM.
17
/// It takes care of bubbling up transactions from the host into the guest and show a
18
/// representation of the device's state into the guest.
19
pub struct FidoGuestKey {
20
/// Queue of packets already processed by the host that need to be sent to the guest.
21
pub pending_in_packets: VecDeque<[u8; constants::U2FHID_PACKET_SIZE]>,
22
/// HID Idle state of the security key.
23
pub idle: u8,
24
/// Timer used to poll to periodically send packets to pending USB transfers.
25
pub timer: PollTimer,
26
}
27
28
impl FidoGuestKey {
29
pub fn new() -> Result<Self> {
30
let timer = PollTimer::new(
31
"guest packet timer".to_string(),
32
std::time::Duration::from_nanos(constants::PACKET_POLL_RATE_NANOS),
33
)?;
34
Ok(FidoGuestKey {
35
pending_in_packets: VecDeque::with_capacity(constants::U2FHID_MAX_IN_PENDING),
36
idle: 1,
37
timer,
38
})
39
}
40
41
/// Resets the guest key representation, stopping the poll and clearing the packet queue.
42
pub fn reset(&mut self) {
43
self.pending_in_packets.clear();
44
if let Err(e) = self.timer.clear() {
45
error!("Unable to clear guest key timer, silently failing. {}", e);
46
}
47
}
48
49
/// Sends data to the guest by associating a given transfer to the oldest packet in the queue.
50
/// If the data from the host hasn't been read yet (the packet queue is empty), it returns the
51
/// same transfer back to the caller, unmodified.
52
pub fn return_data_to_guest(
53
&mut self,
54
transfer_opt: Option<FidoTransfer>,
55
) -> Result<Option<FidoTransfer>> {
56
// If this happens, it means we passed around an empty reference to a
57
// non existing transfer that was already cancelled and removed.
58
let mut transfer = transfer_opt.ok_or(Error::FidoTransferLost)?;
59
match self.pending_in_packets.pop_front() {
60
Some(packet) => {
61
transfer.buffer = TransferBuffer::Vector(packet.to_vec());
62
transfer.actual_length = packet.len();
63
transfer.complete_transfer();
64
Ok(None)
65
}
66
None => {
67
// Pending queue is empty, nothing to do so we return the original transfer without
68
// consuming it.
69
Ok(Some(transfer))
70
}
71
}
72
}
73
}
74
75
#[cfg(test)]
76
mod tests {
77
78
use std::sync::Arc;
79
80
use sync::Mutex;
81
use usb_util::TransferBuffer;
82
use usb_util::TransferStatus;
83
84
use crate::usb::backend::fido_backend::constants::U2FHID_PACKET_SIZE;
85
use crate::usb::backend::fido_backend::fido_guest::FidoGuestKey;
86
use crate::usb::backend::fido_backend::transfer::FidoTransfer;
87
use crate::usb::backend::transfer::BackendTransfer;
88
use crate::usb::backend::transfer::BackendTransferType;
89
90
#[test]
91
fn test_reset() {
92
let mut fido_key = FidoGuestKey::new().unwrap();
93
let fake_packet = [0; U2FHID_PACKET_SIZE];
94
95
fido_key.pending_in_packets.push_back(fake_packet);
96
assert_eq!(fido_key.pending_in_packets.len(), 1);
97
fido_key.reset();
98
assert_eq!(fido_key.pending_in_packets.len(), 0);
99
}
100
101
#[test]
102
fn test_return_data_to_guest_no_packet_retry() {
103
let mut fido_key = FidoGuestKey::new().unwrap();
104
let transfer_buffer = TransferBuffer::Vector(vec![0u8; U2FHID_PACKET_SIZE]);
105
let fake_transfer = FidoTransfer::new(1, transfer_buffer);
106
107
let returned_transfer = fido_key.return_data_to_guest(Some(fake_transfer)).unwrap();
108
assert!(returned_transfer.is_some());
109
}
110
111
#[test]
112
fn test_return_data_to_guest_success() {
113
let mut fido_key = FidoGuestKey::new().unwrap();
114
let fake_packet = [5; U2FHID_PACKET_SIZE];
115
let transfer_buffer = TransferBuffer::Vector(vec![0u8; U2FHID_PACKET_SIZE]);
116
let mut fake_transfer = FidoTransfer::new(1, transfer_buffer);
117
118
let callback_outer = Arc::new(Mutex::new(false));
119
let callback_inner = callback_outer.clone();
120
121
fake_transfer.set_callback(move |t: BackendTransferType| {
122
assert_eq!(t.actual_length(), U2FHID_PACKET_SIZE);
123
assert!(t.status() == TransferStatus::Completed);
124
*callback_inner.lock() = true;
125
});
126
fido_key.pending_in_packets.push_back(fake_packet);
127
128
let returned_transfer = fido_key.return_data_to_guest(Some(fake_transfer)).unwrap();
129
assert!(returned_transfer.is_none());
130
assert!(*callback_outer.lock());
131
}
132
}
133
134