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]
name = "ctap_hmac"
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"
homepage = "https://github.com/shimunn/ctap"
homepage = "https://github.com/ArdaXi/ctap/pull/2"
repository = "https://github.com/shimunn/ctap"
authors = ["Arda Xi <arda@ardaxi.com>", "shimun <shimun@shimun.net>"]
edition = "2018"
readme = "README.md"
[dependencies]
rand = "0.6"
@@ -23,10 +22,6 @@ rust-crypto = "0.2"
csv-core = "0.1.6"
derive_builder = "0.9.0"
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]
crossbeam = "0.7.3"
hex = "0.4.0"

View File

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

View File

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

View File

@@ -597,45 +597,14 @@ impl CoseKey {
let mut cose_key = CoseKey::default();
cose_key.algorithm = -7;
for _ in 0..items {
match generic.value()? {
Value::Text(value::Text::Text(text)) => match &text[..] {
"type" => {
cose_key.key_type = match generic.value()? {
Value::Text(value::Text::Text(type_)) if &type_ == "public-key" => 0u16,
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 {
match generic.borrow_mut().i16()? {
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()?);
cose_key.parameters.insert(key, 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 {
#[fail(display = "Read/write error with device.")]
Io,
#[fail(display = "Operation timed out")]
Timeout,
#[fail(display = "Error while reading packet from device.")]
ReadPacket,
#[fail(display = "Error while writing packet to device.")]
@@ -49,7 +47,7 @@ pub enum FidoErrorKind {
DecryptPin,
#[fail(display = "Failed to verify response signature.")]
VerifySignature,
#[fail(display = "Failed to verify response signature.")]
#[fail(display = "Supplied key has incorrect type.")]
KeyType,
#[fail(display = "Device returned error: {}", _0)]
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 cbor_codec::value::{Bytes, Int, Key, Text, Value};
use cbor_codec::Encoder;
@@ -11,6 +13,7 @@ use rust_crypto::sha2::Sha256;
use std::collections::BTreeMap;
use std::io::Cursor;
pub trait HmacExtension {
fn extension_name() -> &'static str {
"hmac-secret"

View File

@@ -457,6 +457,7 @@ impl FidoDevice {
///
/// This method will fail if a PIN is required but the device is not
/// unlocked or if the device returns malformed data.
pub fn get_assertion<'a, 'b>(
&mut self,
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;
#[cfg(feature = "request_multiple")]
use std::sync::mpsc::channel;
#[cfg(feature = "request_multiple")]
use std::time::Duration;
#[cfg(feature = "request_multiple")]
pub fn request_multiple_devices<
'a,
@@ -16,7 +15,6 @@ pub fn request_multiple_devices<
F: Fn(&mut FidoDevice) -> FidoResult<T> + 'a + Sync,
>(
devices: impl Iterator<Item = (&'a mut FidoDevice, &'a F)>,
timeout: Option<Duration>,
) -> FidoResult<T> {
thread::scope(|scope| -> FidoResult<T> {
let (tx, rx) = channel();
@@ -28,39 +26,22 @@ pub fn request_multiple_devices<
Ok((cancel, thread_handle))
})
.collect::<FidoResult<Vec<_>>>()?;
let mut err = None;
let mut slept = Duration::from_millis(0);
let interval = Duration::from_millis(10);
let mut received = 0usize;
let res = loop {
match timeout {
Some(t) if t < slept => {
break if let Some(cause) = err {
cause
} else {
Err(FidoErrorKind::Timeout.into())
};
}
_ => (),
}
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 res in rx.iter().take(handles.len()) {
match res {
Ok(_) => {
for (mut cancel, join) in handles {
// Canceling out of courtesy don't care if it fails
let _ = cancel.cancel();
let _ = join.join();
}
res
return res;
}
e => err = Some(e),
}
}
err.unwrap_or(Err(FidoErrorKind::DeviceUnsupported.into()))
})
.unwrap()
}
@@ -70,10 +51,9 @@ pub fn request_multiple_devices<
pub fn get_assertion_devices<'a>(
assertion_request: &'a FidoAssertionRequest,
devices: impl Iterator<Item = &'a mut FidoDevice>,
timeout: Option<Duration>,
) -> FidoResult<(&'a FidoCredential, AuthenticatorData)> {
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
@@ -81,8 +61,7 @@ pub fn get_assertion_devices<'a>(
pub fn make_credential_devices<'a>(
credential_request: &'a FidoCredentialRequest,
devices: impl Iterator<Item = &'a mut FidoDevice>,
timeout: Option<Duration>,
) -> FidoResult<FidoCredential> {
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)))
}