7 Commits

Author SHA1 Message Date
65ef574031 handle surplus options 2020-04-01 19:49:49 +02:00
9440271d90 fix warnings 2020-04-01 19:20:48 +02:00
822cebd6ff fix api leaking private type 2020-04-01 19:20:20 +02:00
4e14d265b8 implement make_credential_devices & rename feature 2020-04-01 17:58:53 +02:00
1202fed98d generic request_multiple_devices 2020-04-01 17:48:03 +02:00
501b28e0d9 added feature assert_devices 2020-03-31 21:10:33 +02:00
d4c9dd913f use builder pattern to expose all possible options 2020-03-30 22:59:37 +02:00
13 changed files with 33 additions and 1412 deletions

View File

@@ -1,24 +0,0 @@
kind: pipeline
name: default
steps:
- name: fmt
image: rust:1.43.0
commands:
- rustup component add rustfmt
- cargo fmt --all -- --check
- name: test
image: rust:1.43.0
commands:
- cargo test --all-features
- name: publish
image: rust:1.43.0
environment:
CARGO_REGISTRY_TOKEN:
from_secret: cargo_tkn
commands:
- grep -E 'version ?= ?"${DRONE_TAG}"' -i Cargo.toml || (printf "incorrect crate/tag version" && exit 1)
- cargo package --all-features
- cargo publish --all-features
when:
event: tag

View File

@@ -1,13 +1,12 @@
[package] [package]
name = "ctap_hmac" name = "ctap_hmac"
description = "A Rust implementation of the FIDO2 CTAP protocol, including the HMAC extension" description = "A Rust implementation of the FIDO2 CTAP protocol, including the HMAC extension"
version = "0.4.0" version = "0.3.0"
license = "Apache-2.0/MIT" license = "Apache-2.0/MIT"
homepage = "https://github.com/shimunn/ctap" homepage = "https://github.com/ArdaXi/ctap/pull/2"
repository = "https://github.com/shimunn/ctap" repository = "https://github.com/shimunn/ctap"
authors = ["Arda Xi <arda@ardaxi.com>", "shimun <shimun@shimun.net>"] authors = ["Arda Xi <arda@ardaxi.com>", "shimun <shimun@shimun.net>"]
edition = "2018" edition = "2018"
readme = "README.md"
[dependencies] [dependencies]
rand = "0.6" rand = "0.6"
@@ -23,10 +22,6 @@ rust-crypto = "0.2"
csv-core = "0.1.6" csv-core = "0.1.6"
derive_builder = "0.9.0" derive_builder = "0.9.0"
crossbeam = { version = "0.7.3", optional = true } crossbeam = { version = "0.7.3", optional = true }
serde_derive = "1.0.106"
serde = "1.0.106"
serde_cbor = "0.11.1"
serde_bytes = "0.11.3"
[dev-dependencies] [dev-dependencies]
crossbeam = "0.7.3" crossbeam = "0.7.3"
hex = "0.4.0" hex = "0.4.0"

View File

@@ -4,6 +4,7 @@ use crypto::digest::Digest;
use crypto::sha2::Sha256; use crypto::sha2::Sha256;
use ctap::extensions::hmac::HmacExtension; use ctap::extensions::hmac::HmacExtension;
use ctap::{FidoAssertionRequestBuilder, FidoCredential, FidoCredentialRequestBuilder}; use ctap::{FidoAssertionRequestBuilder, FidoCredential, FidoCredentialRequestBuilder};
use hex; use hex;
use std::env::args; use std::env::args;
use std::io::prelude::*; use std::io::prelude::*;

View File

@@ -1,4 +1,5 @@
extern crate ctap_hmac as ctap; extern crate ctap_hmac as ctap;
use ctap::{ use ctap::{
FidoAssertionRequestBuilder, FidoCredential, FidoCredentialRequestBuilder, FidoDevice, FidoAssertionRequestBuilder, FidoCredential, FidoCredentialRequestBuilder, FidoDevice,
FidoResult, FidoResult,
@@ -6,7 +7,6 @@ use ctap::{
use hex; use hex;
use std::env::args; use std::env::args;
use std::time::Duration;
const RP_ID: &str = "ctap_demo"; const RP_ID: &str = "ctap_demo";
@@ -24,7 +24,6 @@ fn main() -> ctap::FidoResult<()> {
FidoDevice::new(&h).and_then(|mut dev| { FidoDevice::new(&h).and_then(|mut dev| {
FidoCredentialRequestBuilder::default() FidoCredentialRequestBuilder::default()
.rp_id(RP_ID) .rp_id(RP_ID)
.user_name("test")
.build() .build()
.unwrap() .unwrap()
.make_credential(&mut dev) .make_credential(&mut dev)
@@ -42,8 +41,7 @@ fn main() -> ctap::FidoResult<()> {
.map(|handle| FidoDevice::new(&handle)) .map(|handle| FidoDevice::new(&handle))
.collect::<FidoResult<Vec<_>>>()?; .collect::<FidoResult<Vec<_>>>()?;
// run with --features request_multiple // run with --features request_multiple
let (cred, _) = let (cred, _) = ctap::get_assertion_devices(&req, devices.iter_mut())?;
ctap::get_assertion_devices(&req, devices.iter_mut(), Some(Duration::from_secs(10)))?;
println!("Success, got assertion for: {}", hex::encode(&cred.id)); println!("Success, got assertion for: {}", hex::encode(&cred.id));
Ok(()) Ok(())
} }

View File

@@ -597,45 +597,14 @@ impl CoseKey {
let mut cose_key = CoseKey::default(); let mut cose_key = CoseKey::default();
cose_key.algorithm = -7; cose_key.algorithm = -7;
for _ in 0..items { for _ in 0..items {
match generic.value()? { match generic.borrow_mut().i16()? {
Value::Text(value::Text::Text(text)) => match &text[..] { 0x01 => cose_key.key_type = generic.borrow_mut().u16()?,
"type" => { 0x02 => cose_key.algorithm = generic.borrow_mut().i32()?,
cose_key.key_type = match generic.value()? { key if key < 0 => {
Value::Text(value::Text::Text(type_)) if &type_ == "public-key" => 0u16, cose_key.parameters.insert(key, generic.value()?);
Value::U16(i) => i,
Value::U8(i) => i.into(),
_ => {
continue;
}
}
}
"alg" => cose_key.algorithm = generic.borrow_mut().i32()?,
_ => continue,
},
val @ Value::I8(_)
| val @ Value::I16(_)
| val @ Value::U16(_)
| val @ Value::U8(_) => {
let int_val = match val {
Value::I8(i) => i as i32,
Value::I16(i) => i as i32,
Value::U8(i) => i as i32,
Value::U16(i) => i as i32,
_ => unreachable!(),
};
match int_val {
0x01 => cose_key.key_type = generic.borrow_mut().u16()?,
0x02 => cose_key.algorithm = generic.borrow_mut().i32()?,
key if key < 0 => {
cose_key.parameters.insert(key as i16, generic.value()?);
}
unknown => {
(unknown, generic.value()?); // skip unknown parameter
}
}
} }
unknown => { _ => {
(unknown, generic.value()?); // skip unknown parameter generic.value()?; // skip unknown parameter
} }
} }
} }

View File

@@ -23,8 +23,6 @@ pub struct CborErrorCode(u8);
pub enum FidoErrorKind { pub enum FidoErrorKind {
#[fail(display = "Read/write error with device.")] #[fail(display = "Read/write error with device.")]
Io, Io,
#[fail(display = "Operation timed out")]
Timeout,
#[fail(display = "Error while reading packet from device.")] #[fail(display = "Error while reading packet from device.")]
ReadPacket, ReadPacket,
#[fail(display = "Error while writing packet to device.")] #[fail(display = "Error while writing packet to device.")]
@@ -49,7 +47,7 @@ pub enum FidoErrorKind {
DecryptPin, DecryptPin,
#[fail(display = "Failed to verify response signature.")] #[fail(display = "Failed to verify response signature.")]
VerifySignature, VerifySignature,
#[fail(display = "Failed to verify response signature.")] #[fail(display = "Supplied key has incorrect type.")]
KeyType, KeyType,
#[fail(display = "Device returned error: {}", _0)] #[fail(display = "Device returned error: {}", _0)]
CborError(CborErrorCode), CborError(CborErrorCode),

View File

@@ -1,4 +1,6 @@
use crate::{FidoAssertionRequest, FidoAssertionRequestBuilder, FidoCredentialRequest}; use crate::{
FidoAssertionRequest, FidoAssertionRequestBuilder, FidoCredentialRequest,
};
use crate::{FidoCredential, FidoDevice, FidoErrorKind, FidoResult}; use crate::{FidoCredential, FidoDevice, FidoErrorKind, FidoResult};
use cbor_codec::value::{Bytes, Int, Key, Text, Value}; use cbor_codec::value::{Bytes, Int, Key, Text, Value};
use cbor_codec::Encoder; use cbor_codec::Encoder;
@@ -11,6 +13,7 @@ use rust_crypto::sha2::Sha256;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::io::Cursor; use std::io::Cursor;
pub trait HmacExtension { pub trait HmacExtension {
fn extension_name() -> &'static str { fn extension_name() -> &'static str {
"hmac-secret" "hmac-secret"

View File

@@ -457,6 +457,7 @@ impl FidoDevice {
/// ///
/// This method will fail if a PIN is required but the device is not /// This method will fail if a PIN is required but the device is not
/// unlocked or if the device returns malformed data. /// unlocked or if the device returns malformed data.
pub fn get_assertion<'a, 'b>( pub fn get_assertion<'a, 'b>(
&mut self, &mut self,
assertion: &FidoAssertionRequest<'a, 'b>, assertion: &FidoAssertionRequest<'a, 'b>,

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +0,0 @@
use thiserror::Error;
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum ProtocolError {
CborEncode,
CborDecode { data: Vec<u8> },
}

View File

@@ -1,4 +0,0 @@
mod cbor;
mod error;
pub use self::cbor::*;
pub use self::error::*;

View File

@@ -1,9 +0,0 @@
use crate::protocol::{CborRequest, CborResponse};
pub trait CtapTransport {
type Error;
fn cbor<'a>(&mut self, command: &CborRequest<'a>) -> Result<CborResponse, Self::Error>;
}
pub enum CtapCommand {}

View File

@@ -7,8 +7,7 @@ use crate::{
use crossbeam::thread; use crossbeam::thread;
#[cfg(feature = "request_multiple")] #[cfg(feature = "request_multiple")]
use std::sync::mpsc::channel; use std::sync::mpsc::channel;
#[cfg(feature = "request_multiple")]
use std::time::Duration;
#[cfg(feature = "request_multiple")] #[cfg(feature = "request_multiple")]
pub fn request_multiple_devices< pub fn request_multiple_devices<
'a, 'a,
@@ -16,7 +15,6 @@ pub fn request_multiple_devices<
F: Fn(&mut FidoDevice) -> FidoResult<T> + 'a + Sync, F: Fn(&mut FidoDevice) -> FidoResult<T> + 'a + Sync,
>( >(
devices: impl Iterator<Item = (&'a mut FidoDevice, &'a F)>, devices: impl Iterator<Item = (&'a mut FidoDevice, &'a F)>,
timeout: Option<Duration>,
) -> FidoResult<T> { ) -> FidoResult<T> {
thread::scope(|scope| -> FidoResult<T> { thread::scope(|scope| -> FidoResult<T> {
let (tx, rx) = channel(); let (tx, rx) = channel();
@@ -28,39 +26,22 @@ pub fn request_multiple_devices<
Ok((cancel, thread_handle)) Ok((cancel, thread_handle))
}) })
.collect::<FidoResult<Vec<_>>>()?; .collect::<FidoResult<Vec<_>>>()?;
let mut err = None; let mut err = None;
let mut slept = Duration::from_millis(0); for res in rx.iter().take(handles.len()) {
let interval = Duration::from_millis(10); match res {
let mut received = 0usize; Ok(_) => {
let res = loop { for (mut cancel, join) in handles {
match timeout { // Canceling out of courtesy don't care if it fails
Some(t) if t < slept => { let _ = cancel.cancel();
break if let Some(cause) = err { let _ = join.join();
cause }
} else { return res;
Err(FidoErrorKind::Timeout.into())
};
} }
_ => (), e => err = Some(e),
} }
if timeout.map(|t| t < slept).unwrap_or(true) {}
if let Ok(msg) = rx.recv_timeout(interval) {
received += 1;
match msg {
e @ Err(_) if received == handles.len() => break e,
e @ Err(_) => err = Some(e),
res @ Ok(_) => break res,
}
} else {
slept += interval;
}
};
for (mut cancel, join) in handles {
// Canceling out of courtesy don't care if it fails
let _ = cancel.cancel();
let _ = join.join();
} }
res err.unwrap_or(Err(FidoErrorKind::DeviceUnsupported.into()))
}) })
.unwrap() .unwrap()
} }
@@ -70,10 +51,9 @@ pub fn request_multiple_devices<
pub fn get_assertion_devices<'a>( pub fn get_assertion_devices<'a>(
assertion_request: &'a FidoAssertionRequest, assertion_request: &'a FidoAssertionRequest,
devices: impl Iterator<Item = &'a mut FidoDevice>, devices: impl Iterator<Item = &'a mut FidoDevice>,
timeout: Option<Duration>,
) -> FidoResult<(&'a FidoCredential, AuthenticatorData)> { ) -> FidoResult<(&'a FidoCredential, AuthenticatorData)> {
let get_assertion = |device: &mut FidoDevice| device.get_assertion(assertion_request); let get_assertion = |device: &mut FidoDevice| device.get_assertion(assertion_request);
request_multiple_devices(devices.map(|device| (device, &get_assertion)), timeout) request_multiple_devices(devices.map(|device| (device, &get_assertion)))
} }
/// Will send the `credential_request` to all supplied `devices` and return either the first credential or the last error /// Will send the `credential_request` to all supplied `devices` and return either the first credential or the last error
@@ -81,8 +61,7 @@ pub fn get_assertion_devices<'a>(
pub fn make_credential_devices<'a>( pub fn make_credential_devices<'a>(
credential_request: &'a FidoCredentialRequest, credential_request: &'a FidoCredentialRequest,
devices: impl Iterator<Item = &'a mut FidoDevice>, devices: impl Iterator<Item = &'a mut FidoDevice>,
timeout: Option<Duration>,
) -> FidoResult<FidoCredential> { ) -> FidoResult<FidoCredential> {
let make_credential = |device: &mut FidoDevice| device.make_credential(credential_request); let make_credential = |device: &mut FidoDevice| device.make_credential(credential_request);
request_multiple_devices(devices.map(|device| (device, &make_credential)), timeout) request_multiple_devices(devices.map(|device| (device, &make_credential)))
} }