Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit66c54bf

Browse files
committed
Add NFC support
1 parentf567991 commit66c54bf

File tree

7 files changed

+341
-1
lines changed

7 files changed

+341
-1
lines changed

‎Cargo.toml‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ maintenance = { status = "actively-developed" }
1717
binding-recompile = ["bindgen"]
1818
webdriver = ["base64","bytes","warp","tokio","serde","serde_json"]
1919

20+
nfc = ["pcsc"]
21+
2022
[target.'cfg(target_os="linux")'.dependencies]
2123
libudev ="^0.2"
2224

@@ -51,6 +53,7 @@ serde = { version = "1.0", optional = true, features = ["derive"] }
5153
serde_json = {version ="1.0",optional =true }
5254
bytes = {version ="0.5",optional =true,features = ["serde"] }
5355
base64 = {version ="^0.10",optional =true }
56+
pcsc = {version ="2",optional =true }
5457

5558
[dev-dependencies]
5659
sha2 ="^0.8.2"

‎examples/main.rs‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ fn main() {
4242
opts.optflag("x","no-u2f-usb-hid","do not enable u2f-usb-hid platforms");
4343
#[cfg(feature ="webdriver")]
4444
opts.optflag("w","webdriver","enable WebDriver virtual bus");
45+
#[cfg(feature ="nfc")]
46+
opts.optflag("n","no-nfc","do not enable u2f-nfc platform");
4547

4648
opts.optflag("h","help","print this help menu").optopt(
4749
"t",
@@ -74,6 +76,13 @@ fn main() {
7476
}
7577
}
7678

79+
#[cfg(feature ="nfc")]
80+
{
81+
if !matches.opt_present("no-nfc"){
82+
manager.add_u2f_nfc_transports();
83+
}
84+
}
85+
7786
let timeout_ms =match matches.opt_get_default::<u64>("timeout",15){
7887
Ok(timeout_s) =>{
7988
println!("Using {}s as the timeout",&timeout_s);

‎src/authenticatorservice.rs‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ impl AuthenticatorService {
7171
/// Add any detected platform transports
7272
pubfnadd_detected_transports(&mutself){
7373
self.add_u2f_usb_hid_platform_transports();
74+
75+
#[cfg(feature ="nfc")]
76+
self.add_u2f_nfc_transports();
7477
}
7578

7679
fnadd_transport(&mutself,boxed_token:Box<dynAuthenticatorTransport +Send>){
@@ -95,6 +98,11 @@ impl AuthenticatorService {
9598
}
9699
}
97100

101+
#[cfg(feature ="nfc")]
102+
pubfnadd_u2f_nfc_transports(&mutself){
103+
self.add_transport(Box::new(crate::NFCManager::new()));
104+
}
105+
98106
pubfnregister(
99107
&mutself,
100108
flags:crate::RegisterFlags,

‎src/consts.rs‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,3 +78,12 @@ pub const SW_NO_ERROR: [u8; 2] = [0x90, 0x00];
7878
pubconstSW_CONDITIONS_NOT_SATISFIED:[u8;2] =[0x69,0x85];
7979
pubconstSW_WRONG_DATA:[u8;2] =[0x6A,0x80];
8080
pubconstSW_WRONG_LENGTH:[u8;2] =[0x67,0x00];
81+
82+
// U2F-over-NFC constants
83+
// TODO: naming, some are also ISO 7816-4 ??
84+
// The're mostly APDU-specific?
85+
pubconstU2F_AID:[u8;8] =[0xA0,0x00,0x00,0x06,0x47,0x2F,0x00,0x01];
86+
pubconstU2F_SELECT_FILE:u8 =0xA4;
87+
pubconstU2F_SELECT_DIRECT:u8 =0x04;
88+
pubconstU2F_GET_RESPONSE:u8 =0xC0;
89+
pubconstU2F_MORE_DATA:u8 =0x61;

‎src/lib.rs‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,13 @@ pub mod platform;
5252
#[path ="stub/mod.rs"]
5353
pubmod platform;
5454

55+
#[cfg(feature ="nfc")]
56+
externcrate pcsc;
57+
#[cfg(feature ="nfc")]
58+
mod nfc;
59+
#[cfg(feature ="nfc")]
60+
pubusecrate::nfc::NFCManager;
61+
5562
externcrate libc;
5663
#[macro_use]
5764
externcrate log;

‎src/nfc.rs‎

Lines changed: 302 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,302 @@
1+
/* This Source Code Form is subject to the terms of the Mozilla Public
2+
* License, v. 2.0. If a copy of the MPL was not distributed with this
3+
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4+
5+
use pcsc::*;
6+
use runloop::RunLoop;
7+
use std::collections::HashMap;
8+
use std::ffi::CString;
9+
use std::io;
10+
use std::option::Option;
11+
use std::sync::mpsc::Sender;
12+
use std::sync::{Arc,Mutex};
13+
use std::thread;
14+
use std::time::Duration;
15+
16+
usecrate::apdu::*;
17+
usecrate::consts::*;
18+
usecrate::errors;
19+
usecrate::util::{io_err, trace_hex};
20+
21+
usecrate::authenticatorservice::AuthenticatorTransport;
22+
usecrate::statecallback::StateCallback;
23+
usecrate::statemachine::StateMachine;
24+
usecrate::u2ftypes::{U2FDeviceInfo,U2FInfoQueryable};
25+
26+
fnsendrecv(card:&mutCard,send:&[u8]) -> io::Result<Vec<u8>>{
27+
trace_hex("NFC send", send);
28+
letmut rapdu_buf =[0;MAX_BUFFER_SIZE];
29+
match card.transmit(send,&mut rapdu_buf){
30+
Ok(rapdu) =>{
31+
trace_hex("NFC recv", rapdu);
32+
Ok(rapdu.to_vec())
33+
}
34+
Err(err) =>{
35+
trace!("NFC error: {}", err);
36+
let s =format!("{}", err);
37+
Err(io_err(&s))
38+
}
39+
}
40+
}
41+
42+
implAPDUDeviceforCard{
43+
fninit_apdu(&mutself) -> io::Result<()>{
44+
let out =APDU::serialize_short(U2F_SELECT_FILE,U2F_SELECT_DIRECT,&U2F_AID)?;
45+
let ret =sendrecv(self,&out)?;
46+
let(_, status) =APDU::deserialize(ret)?;
47+
apdu_status_to_result(status,())
48+
}
49+
50+
fnsend_apdu(&mutself,cmd:u8,p1:u8,send:&[u8]) -> io::Result<(Vec<u8>,[u8;2])>{
51+
// Some devices, such as the Yubikey 4, freak out if an APDU which _would_ fit a short
52+
// command is sent as an extended command. This means we must use short, even though
53+
// that means chaining the responses together.
54+
// The whole response would have fit in an extended reply, but it seems we can't have nice
55+
// things.
56+
letmut data:Vec<u8> =Vec::new();
57+
58+
let out =APDU::serialize_short(cmd, p1, send)?;
59+
let ret =sendrecv(self,&out)?;
60+
let(mut more,[s1, s2]) =APDU::deserialize(ret)?;
61+
data.append(&mut more);
62+
63+
if s1 !=U2F_MORE_DATA{
64+
returnOk((data,[s1, s2]));
65+
}
66+
67+
loop{
68+
let out =APDU::serialize_short(U2F_GET_RESPONSE,0x00,&[])?;
69+
let ret =sendrecv(self,&out)?;
70+
let(mut more,[s1, s2]) =APDU::deserialize(ret)?;
71+
data.append(&mut more);
72+
if s1 !=U2F_MORE_DATA{
73+
returnOk((data,[s1, s2]));
74+
}
75+
}
76+
}
77+
}
78+
79+
implU2FInfoQueryableforCard{
80+
fnget_device_info(&self) ->U2FDeviceInfo{
81+
// TODO: actuall return something sane here!
82+
let vendor =String::from("Unknown Vendor");
83+
let product =String::from("Unknown Device");
84+
85+
U2FDeviceInfo{
86+
vendor_name: vendor.as_bytes().to_vec(),
87+
device_name: product.as_bytes().to_vec(),
88+
version_interface:0,
89+
version_major:0,
90+
version_minor:0,
91+
version_build:0,
92+
cap_flags:0,
93+
}
94+
}
95+
}
96+
97+
pubstructNFCManager{
98+
run_loop:Option<RunLoop>,
99+
}
100+
101+
implNFCManager{
102+
fnrun<E,F>(&mutself,timeout:u64,fatal_error:E,f:F) ->crate::Result<()>
103+
where
104+
E:Fn() +Sync +Send +'static,
105+
F:Fn(&mutCard,&dynFn() ->bool) +Sync +Send +Clone +'static,
106+
{
107+
ifself.run_loop.is_some(){
108+
returnErr(errors::AuthenticatorError::InternalError(String::from(
109+
"nfc run loop is already in use",
110+
)));
111+
}
112+
113+
let ctx =
114+
Context::establish(Scope::User).map_err(|_| errors::AuthenticatorError::Platform)?;
115+
116+
let rl =RunLoop::new_with_timeout(
117+
move |alive|{
118+
letmut child_loops:HashMap<CString,RunLoop> =HashMap::new();
119+
120+
letmut readers_buf =[0;2048];
121+
// We _could_ insert `ReaderState::new(PNP_NOTIFICATION(), State::UNAWARE)`
122+
// to be reminded of reader insertion/removal,
123+
// but this is not guaranteed to be supported
124+
// and we need to poll anyways.
125+
letmut reader_states:Vec<ReaderState> =Vec::new();
126+
127+
whilealive(){
128+
// Add new readers.
129+
let names =match ctx.list_readers(&mut readers_buf){
130+
Ok(n) => n,
131+
Err(_) =>{
132+
fatal_error();
133+
break;
134+
}
135+
};
136+
for namein names{
137+
if !reader_states.iter().any(|reader| reader.name() == name){
138+
debug!("Adding reader {:?}", name);
139+
reader_states.push(ReaderState::new(name,State::UNAWARE));
140+
}
141+
}
142+
143+
// Remove dead readers.
144+
fnis_dead(reader:&ReaderState) ->bool{
145+
reader
146+
.event_state()
147+
.intersects(State::UNKNOWN |State::IGNORE)
148+
}
149+
for readerin&reader_states{
150+
ifis_dead(reader){
151+
debug!("Removing reader {:?}", reader.name());
152+
}
153+
}
154+
reader_states.retain(|reader| !is_dead(reader));
155+
156+
// Let backend know that we know about the reader state.
157+
// Otherwise it will keep trying to update us.
158+
for rsin&mut reader_states{
159+
rs.sync_current_state();
160+
}
161+
162+
if reader_states.len() ==0{
163+
// No readers available. This means that `get_status_change` will return
164+
// immediately without any work, causing a busy-loop.
165+
// Let's wait for a bit and look for a new reader.
166+
thread::sleep(Duration::from_millis(500));
167+
continue;
168+
}
169+
170+
// This call is blocking, so we must give it _some_ timeout in order for
171+
// the `alive()` check to work.
172+
let timeout =Duration::from_millis(100);
173+
ifletErr(e) = ctx.get_status_change(timeout,&mut reader_states){
174+
if e ==Error::Timeout{
175+
continue;
176+
}
177+
fatal_error();
178+
break;
179+
}
180+
181+
for readerin&mut reader_states{
182+
let state = reader.event_state();
183+
let name =CString::from(reader.name());
184+
185+
trace!("Reader {:?}: state {:?}", name, state);
186+
187+
// TODO: this will keep spamming yubikeys with usb auth attempts???
188+
// probably not harmful, but is there a way to avoid it?
189+
if state.contains(State::PRESENT) && !state.contains(State::EXCLUSIVE){
190+
letmut card =
191+
match ctx.connect(&name,ShareMode::Shared,Protocols::ANY){
192+
Ok(card) => card,
193+
_ =>continue,
194+
};
195+
196+
let my_f = f.clone();
197+
let cl =RunLoop::new(move |alive|{
198+
ifalive(){
199+
my_f(&mut card, alive);
200+
}
201+
});
202+
203+
ifletOk(x) = cl{
204+
child_loops.insert(name, x);
205+
}
206+
}else{
207+
ifletSome(cl) = child_loops.remove(&name){
208+
cl.cancel();
209+
}
210+
}
211+
}
212+
}
213+
214+
for(_, child)in child_loops{
215+
child.cancel();
216+
}
217+
},
218+
timeout,
219+
)
220+
.map_err(|_| errors::AuthenticatorError::Platform)?;
221+
self.run_loop =Some(rl);
222+
Ok(())
223+
}
224+
225+
pubfnnew() ->Self{
226+
Self{run_loop:None}
227+
}
228+
229+
fnstop(&mutself){
230+
ifletSome(rl) =&self.run_loop{
231+
rl.cancel();
232+
self.run_loop =None;
233+
}
234+
}
235+
}
236+
237+
implDropforNFCManager{
238+
fndrop(&mutself){
239+
self.stop();
240+
}
241+
}
242+
243+
implAuthenticatorTransportforNFCManager{
244+
fnregister(
245+
&mutself,
246+
flags:crate::RegisterFlags,
247+
timeout:u64,
248+
challenge:Vec<u8>,
249+
application:crate::AppId,
250+
key_handles:Vec<crate::KeyHandle>,
251+
status:Sender<crate::StatusUpdate>,
252+
callback:StateCallback<crate::Result<crate::RegisterResult>>,
253+
) ->crate::Result<()>{
254+
let status_mutex =Arc::new(Mutex::new(status));
255+
let cbc = callback.clone();
256+
let err =move || cbc.call(Err(errors::AuthenticatorError::Platform));
257+
self.run(timeout, err,move |card, alive|{
258+
StateMachine::register(
259+
card,
260+
flags,
261+
&challenge,
262+
&application,
263+
&key_handles,
264+
&status_mutex,
265+
&callback,
266+
alive,
267+
);
268+
})
269+
}
270+
271+
fnsign(
272+
&mutself,
273+
flags:crate::SignFlags,
274+
timeout:u64,
275+
challenge:Vec<u8>,
276+
app_ids:Vec<crate::AppId>,
277+
key_handles:Vec<crate::KeyHandle>,
278+
status:Sender<crate::StatusUpdate>,
279+
callback:StateCallback<crate::Result<crate::SignResult>>,
280+
) ->crate::Result<()>{
281+
let status_mutex =Arc::new(Mutex::new(status));
282+
let cbc = callback.clone();
283+
let err =move || cbc.call(Err(errors::AuthenticatorError::Platform));
284+
self.run(timeout, err,move |card, alive|{
285+
StateMachine::sign(
286+
card,
287+
flags,
288+
&challenge,
289+
&app_ids,
290+
&key_handles,
291+
&status_mutex,
292+
&callback,
293+
alive,
294+
);
295+
})
296+
}
297+
298+
fncancel(&mutself) ->crate::Result<()>{
299+
self.stop();
300+
Ok(())
301+
}
302+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp