-
Notifications
You must be signed in to change notification settings - Fork 289
/
main.rs
291 lines (263 loc) · 9.77 KB
/
main.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
// Copyright 2019-2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(not(feature = "std"), no_main)]
extern crate alloc;
extern crate arrayref;
extern crate byteorder;
#[cfg(feature = "std")]
extern crate core;
extern crate lang_items;
use core::convert::TryFrom;
#[cfg(feature = "debug_ctap")]
use core::fmt::Write;
#[cfg(feature = "with_ctap1")]
use ctap2::env::tock::blink_leds;
use ctap2::env::tock::{switch_off_leds, wink_leds, TockEnv};
#[cfg(feature = "with_ctap1")]
use libtock_buttons::Buttons;
#[cfg(feature = "debug_ctap")]
use libtock_console::Console;
#[cfg(feature = "debug_ctap")]
use libtock_console::ConsoleWriter;
use libtock_drivers::result::FlexUnwrap;
use libtock_drivers::timer::Duration;
use libtock_drivers::usb_ctap_hid;
#[cfg(not(feature = "std"))]
use libtock_runtime::{set_main, stack_size, TockSyscalls};
#[cfg(feature = "std")]
use libtock_unittest::fake;
use opensk::api::clock::Clock;
use opensk::api::connection::UsbEndpoint;
use opensk::ctap::hid::HidPacketIterator;
use opensk::ctap::KEEPALIVE_DELAY_MS;
use opensk::env::Env;
use opensk::Transport;
#[cfg(not(feature = "std"))]
stack_size! {0x4000}
#[cfg(not(feature = "std"))]
set_main! {main}
const SEND_TIMEOUT_MS: Duration<isize> = Duration::from_ms(1000);
const KEEPALIVE_DELAY_MS_TOCK: Duration<isize> = Duration::from_ms(KEEPALIVE_DELAY_MS as isize);
#[cfg(not(feature = "vendor_hid"))]
const NUM_ENDPOINTS: usize = 1;
#[cfg(feature = "vendor_hid")]
const NUM_ENDPOINTS: usize = 2;
// The reply/replies that are queued for each endpoint.
struct EndpointReply {
endpoint: UsbEndpoint,
reply: HidPacketIterator,
}
#[cfg(feature = "std")]
type SyscallImplementation = fake::Syscalls;
#[cfg(not(feature = "std"))]
type SyscallImplementation = TockSyscalls;
impl EndpointReply {
pub fn new(endpoint: UsbEndpoint) -> Self {
EndpointReply {
endpoint,
reply: HidPacketIterator::none(),
}
}
}
// A single packet to send.
struct SendPacket {
endpoint: UsbEndpoint,
packet: [u8; 64],
}
struct EndpointReplies {
replies: [EndpointReply; NUM_ENDPOINTS],
}
impl EndpointReplies {
pub fn new() -> Self {
EndpointReplies {
replies: [
EndpointReply::new(UsbEndpoint::MainHid),
#[cfg(feature = "vendor_hid")]
EndpointReply::new(UsbEndpoint::VendorHid),
],
}
}
pub fn next_packet(&mut self) -> Option<SendPacket> {
for ep in self.replies.iter_mut() {
if let Some(packet) = ep.reply.next() {
return Some(SendPacket {
endpoint: ep.endpoint,
packet,
});
}
}
None
}
pub fn clear(&mut self, endpoint: UsbEndpoint) {
for ep in self.replies.iter_mut() {
if ep.endpoint == endpoint {
*ep = EndpointReply::new(endpoint);
break;
}
}
}
}
fn main() {
#[cfg(feature = "debug_ctap")]
let mut writer = Console::<SyscallImplementation>::writer();
#[cfg(feature = "debug_ctap")]
{
writeln!(writer, "Hello world from OpenSK!").ok().unwrap();
}
// Setup USB driver.
if !usb_ctap_hid::UsbCtapHid::<SyscallImplementation>::setup() {
panic!("Cannot setup USB driver");
}
let env = TockEnv::<SyscallImplementation>::default();
let mut ctap = opensk::Ctap::new(env);
let mut led_counter = 0;
let mut led_blink_timer =
<<TockEnv<SyscallImplementation> as Env>::Clock as Clock>::Timer::default();
let mut replies = EndpointReplies::new();
// Main loop. If CTAP1 is used, we register button presses for U2F while receiving and waiting.
// The way TockOS and apps currently interact, callbacks need a yield syscall to execute,
// making consistent blinking patterns and sending keepalives harder.
#[cfg(feature = "debug_ctap")]
writeln!(writer, "Entering main ctap loop").unwrap();
loop {
#[cfg(feature = "with_ctap1")]
let num_buttons = Buttons::<SyscallImplementation>::count().ok().unwrap();
// Variable for use in both the send_and_maybe_recv and recv cases.
let mut usb_endpoint: Option<UsbEndpoint> = None;
let mut pkt_request = [0; 64];
if let Some(packet) = replies.next_packet() {
match usb_ctap_hid::UsbCtapHid::<SyscallImplementation>::send(
&packet.packet,
SEND_TIMEOUT_MS,
packet.endpoint as u32,
)
.flex_unwrap()
{
usb_ctap_hid::SendOrRecvStatus::Sent => {
#[cfg(feature = "debug_ctap")]
print_packet_notice::<SyscallImplementation>(
"Sent packet",
ctap.env().clock().timestamp_us(),
&mut writer,
);
}
usb_ctap_hid::SendOrRecvStatus::Timeout => {
#[cfg(feature = "debug_ctap")]
print_packet_notice::<SyscallImplementation>(
"Timeout while sending packet",
ctap.env().clock().timestamp_us(),
&mut writer,
);
// The client is unresponsive, so we discard all pending packets.
replies.clear(packet.endpoint);
}
_ => panic!("Unexpected status on USB transmission"),
};
} else {
usb_endpoint =
match usb_ctap_hid::UsbCtapHid::<SyscallImplementation>::recv_with_timeout(
&mut pkt_request,
KEEPALIVE_DELAY_MS_TOCK,
)
.flex_unwrap()
{
usb_ctap_hid::SendOrRecvStatus::Received(endpoint) => {
#[cfg(feature = "debug_ctap")]
print_packet_notice::<SyscallImplementation>(
"Received packet",
ctap.env().clock().timestamp_us(),
&mut writer,
);
UsbEndpoint::try_from(endpoint as usize).ok()
}
usb_ctap_hid::SendOrRecvStatus::Sent => {
panic!("Returned transmit status on receive")
}
usb_ctap_hid::SendOrRecvStatus::Timeout => None,
};
}
#[cfg(feature = "with_ctap1")]
{
let button_touched = (0..num_buttons).any(Buttons::<SyscallImplementation>::is_pressed);
if button_touched {
ctap.u2f_grant_user_presence();
}
}
// This call is making sure that even for long inactivity, wrapping clock values
// don't cause problems with timers.
ctap.env().clock().tickle();
if let Some(endpoint) = usb_endpoint {
let transport = match endpoint {
UsbEndpoint::MainHid => Transport::MainHid,
#[cfg(feature = "vendor_hid")]
UsbEndpoint::VendorHid => Transport::VendorHid,
};
let reply = ctap.process_hid_packet(&pkt_request, transport);
if reply.has_data() {
// Update endpoint with the reply.
for ep in replies.replies.iter_mut() {
if ep.endpoint == endpoint {
if ep.reply.has_data() {
#[cfg(feature = "debug_ctap")]
writeln!(
Console::<SyscallImplementation>::writer(),
"Warning overwriting existing reply for endpoint {}",
endpoint as usize
)
.unwrap();
}
ep.reply = reply;
break;
}
}
}
}
if ctap.env().clock().is_elapsed(&led_blink_timer) {
// Loops quickly when waiting for U2F user presence, so the next LED blink
// state is only set if enough time has elapsed.
led_counter += 1;
led_blink_timer = ctap.env().clock().make_timer(KEEPALIVE_DELAY_MS)
}
if ctap.should_wink() {
wink_leds::<SyscallImplementation>(led_counter);
} else {
#[cfg(not(feature = "with_ctap1"))]
switch_off_leds::<SyscallImplementation>();
#[cfg(feature = "with_ctap1")]
if ctap.u2f_needs_user_presence() {
// Flash the LEDs with an almost regular pattern. The inaccuracy comes from
// delay caused by processing and sending of packets.
blink_leds::<SyscallImplementation>(led_counter);
} else {
switch_off_leds::<SyscallImplementation>();
}
}
}
}
#[cfg(feature = "debug_ctap")]
fn print_packet_notice<S: libtock_platform::Syscalls>(
notice_text: &str,
now_us: usize,
writer: &mut ConsoleWriter<S>,
) {
writeln!(
writer,
"{} at {}.{:06} s",
notice_text,
now_us / 1_000_000,
now_us % 1_000_000
)
.unwrap();
}