Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
name = "challenge_response"
version = "0.0.0-placeholder-version"
authors = ["Ashutosh Varma <[email protected]>", "louib <[email protected]>"]
edition = "2021"

description = "Perform HMAC-SHA1 and OTP challenges with YubiKey, OnlyKey and NitroKey, in pure Rust."
license = "MIT OR Apache-2.0"
Expand Down Expand Up @@ -33,13 +34,15 @@ aes = "0.8"
block-modes = "0.9"
hmac = "0.12"
sha-1 = "0.10"
tokio = { version = "1", features = ["rt", "macros", "time"] }
async-trait = "0.1"

[target.'cfg(windows)'.dependencies]
rusb = { version = "0.9" }

[target.'cfg(not(windows))'.dependencies]
rusb = { version = "0.9", optional = true }
nusb = { version = "0.1", optional = true }
nusb = { version = "0.2", optional = true }

[dev-dependencies]
hex = "0.4"
10 changes: 5 additions & 5 deletions src/configure.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use config::Command;
use hmacmode::HmacKey;
use otpmode::Aes128Key;
use sec::crc16;
use crate::config::Command;
use crate::hmacmode::HmacKey;
use crate::otpmode::Aes128Key;
use crate::sec::crc16;
use std;
use usb::{Frame, PAYLOAD_SIZE};
use crate::usb::{Frame, PAYLOAD_SIZE};

const FIXED_SIZE: usize = 16;
const UID_SIZE: usize = 6;
Expand Down
28 changes: 28 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#[cfg(any(feature = "rusb", target_os = "windows"))]
use rusb::Error as usbError;
#[cfg(all(feature = "nusb", not(target_os = "windows")))]
use nusb::{Error as nusbError, transfer::TransferError as nusbTransferError};
use std::error;
use std::fmt;
use std::io::Error as ioError;
Expand All @@ -9,6 +11,10 @@ pub enum ChallengeResponseError {
IOError(ioError),
#[cfg(any(feature = "rusb", target_os = "windows"))]
UsbError(usbError),
#[cfg(all(feature = "nusb", not(target_os = "windows")))]
NusbError(nusbError),
#[cfg(all(feature = "nusb", not(target_os = "windows")))]
NusbTransferError(nusbTransferError),
CommandNotSupported,
DeviceNotFound,
OpenDeviceError,
Expand All @@ -25,6 +31,10 @@ impl fmt::Display for ChallengeResponseError {
ChallengeResponseError::IOError(ref err) => write!(f, "IO error: {}", err),
#[cfg(any(feature = "rusb", target_os = "windows"))]
ChallengeResponseError::UsbError(ref err) => write!(f, "USB error: {}", err),
#[cfg(all(feature = "nusb", not(target_os = "windows")))]
ChallengeResponseError::NusbError(ref err) => write!(f, "NUSB error: {}", err),
#[cfg(all(feature = "nusb", not(target_os = "windows")))]
ChallengeResponseError::NusbTransferError(ref err) => write!(f, "NUSB transfer error: {}", err),
ChallengeResponseError::DeviceNotFound => write!(f, "Device not found"),
ChallengeResponseError::OpenDeviceError => write!(f, "Can not open device"),
ChallengeResponseError::CommandNotSupported => write!(f, "Command Not Supported"),
Expand All @@ -42,6 +52,10 @@ impl error::Error for ChallengeResponseError {
match *self {
#[cfg(any(feature = "rusb", target_os = "windows"))]
ChallengeResponseError::UsbError(ref err) => Some(err),
#[cfg(all(feature = "nusb", not(target_os = "windows")))]
ChallengeResponseError::NusbError(ref err) => Some(err),
#[cfg(all(feature = "nusb", not(target_os = "windows")))]
ChallengeResponseError::NusbTransferError(ref err) => Some(err),
_ => None,
}
}
Expand All @@ -59,3 +73,17 @@ impl From<usbError> for ChallengeResponseError {
ChallengeResponseError::UsbError(err)
}
}

#[cfg(all(feature = "nusb", not(target_os = "windows")))]
impl From<nusbError> for ChallengeResponseError {
fn from(err: nusbError) -> ChallengeResponseError {
ChallengeResponseError::NusbError(err)
}
}

#[cfg(all(feature = "nusb", not(target_os = "windows")))]
impl From<nusbTransferError> for ChallengeResponseError {
fn from(err: nusbTransferError) -> ChallengeResponseError {
ChallengeResponseError::NusbTransferError(err)
}
}
2 changes: 1 addition & 1 deletion src/hmacmode.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use rand::Rng;
use sec::hmac_sha1;
use crate::sec::hmac_sha1;
use std;

/// Size of the secret used by the HMAC algorithm
Expand Down
91 changes: 52 additions & 39 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@ compile_error!("Either the rusb or nusb feature must be enabled for this crate")

#[cfg(all(feature = "nusb", not(feature = "rusb"), not(target_os = "windows")))]
extern crate nusb;
#[cfg(all(feature = "nusb", not(feature = "rusb"), not(target_os = "windows")))]
extern crate tokio;
#[cfg(any(feature = "rusb", target_os = "windows"))]
extern crate rusb;

#[macro_use]
extern crate structure;

extern crate aes;
extern crate async_trait;
extern crate block_modes;
extern crate hmac;
extern crate rand;
Expand Down Expand Up @@ -54,50 +57,55 @@ impl ChallengeResponse {
Ok(ChallengeResponse { backend })
}

pub fn find_device(&mut self) -> Result<Device> {
self.backend.find_device()
pub async fn find_device(&mut self) -> Result<Device> {
self.backend.find_device().await
}

pub fn find_device_from_serial(&mut self, serial: u32) -> Result<Device> {
self.backend.find_device_from_serial(serial)
pub async fn find_device_from_serial(&mut self, serial: u32) -> Result<Device> {
self.backend.find_device_from_serial(serial).await
}

pub fn find_all_devices(&mut self) -> Result<Vec<Device>> {
self.backend.find_all_devices()
pub async fn find_all_devices(&mut self) -> Result<Vec<Device>> {
self.backend.find_all_devices().await
}

pub fn read_serial_number(&mut self, conf: Config) -> Result<u32> {
pub async fn read_serial_number(&mut self, conf: Config) -> Result<u32> {
self.backend
.read_serial_from_device(conf.device.bus_id, conf.device.address_id)
.await
}

pub fn write_config(&mut self, conf: Config, device_config: &mut DeviceModeConfig) -> Result<()> {
pub async fn write_config(&mut self, conf: Config, device_config: &mut DeviceModeConfig) -> Result<()> {
let d = device_config.to_frame(conf.command);
let mut buf = [0; usb::STATUS_UPDATE_PAYLOAD_SIZE];

let (mut handle, interfaces) = self
.backend
.open_device(conf.device.bus_id, conf.device.address_id)?;
.open_device(conf.device.bus_id, conf.device.address_id)
.await?;

self.backend
.wait(&mut handle, |f| !f.contains(Flags::SLOT_WRITE_FLAG), &mut buf)?;
.wait(&mut handle, |f| !f.contains(Flags::SLOT_WRITE_FLAG), &mut buf)
.await?;

// TODO: Should check version number.

self.backend.write_frame(&mut handle, &d)?;
self.backend.write_frame(&mut handle, &d).await?;
self.backend
.wait(&mut handle, |f| !f.contains(Flags::SLOT_WRITE_FLAG), &mut buf)?;
self.backend.close_device(handle, interfaces)?;
.wait(&mut handle, |f| !f.contains(Flags::SLOT_WRITE_FLAG), &mut buf)
.await?;
self.backend.close_device(handle, interfaces).await?;

Ok(())
}

pub fn challenge_response_hmac(&mut self, chall: &[u8], conf: Config) -> Result<Hmac> {
pub async fn challenge_response_hmac(&mut self, chall: &[u8], conf: Config) -> Result<Hmac> {
let mut hmac = Hmac([0; 20]);

let (mut handle, interfaces) = self
.backend
.open_device(conf.device.bus_id, conf.device.address_id)?;
.open_device(conf.device.bus_id, conf.device.address_id)
.await?;

let mut challenge = [0; CHALLENGE_SIZE];

Expand All @@ -113,18 +121,20 @@ impl ChallengeResponse {
(&mut challenge[..chall.len()]).copy_from_slice(chall);
let d = Frame::new(challenge, command);
let mut buf = [0; usb::STATUS_UPDATE_PAYLOAD_SIZE];
self.backend.wait(
&mut handle,
|f| !f.contains(usb::Flags::SLOT_WRITE_FLAG),
&mut buf,
)?;
self.backend
.wait(
&mut handle,
|f| !f.contains(usb::Flags::SLOT_WRITE_FLAG),
&mut buf,
)
.await?;

self.backend.write_frame(&mut handle, &d)?;
self.backend.write_frame(&mut handle, &d).await?;

// Read the response.
let mut response = [0; usb::RESPONSE_SIZE];
self.backend.read_response(&mut handle, &mut response)?;
self.backend.close_device(handle, interfaces)?;
self.backend.read_response(&mut handle, &mut response).await?;
self.backend.close_device(handle, interfaces).await?;

// Check response.
if crc16(&response[..22]) != CRC_RESIDUAL_OK {
Expand All @@ -136,14 +146,15 @@ impl ChallengeResponse {
Ok(hmac)
}

pub fn challenge_response_otp(&mut self, chall: &[u8], conf: Config) -> Result<Aes128Block> {
pub async fn challenge_response_otp(&mut self, chall: &[u8], conf: Config) -> Result<Aes128Block> {
let mut block = Aes128Block {
block: GenericArray::clone_from_slice(&[0; 16]),
};

let (mut handle, interfaces) = self
.backend
.open_device(conf.device.bus_id, conf.device.address_id)?;
.open_device(conf.device.bus_id, conf.device.address_id)
.await?;

let mut challenge = [0; CHALLENGE_SIZE];

Expand All @@ -156,17 +167,19 @@ impl ChallengeResponse {
let d = Frame::new(challenge, command);
let mut buf = [0; usb::STATUS_UPDATE_PAYLOAD_SIZE];

self.backend.wait(
&mut handle,
|f| !f.contains(usb::Flags::SLOT_WRITE_FLAG),
&mut buf,
)?;
self.backend
.wait(
&mut handle,
|f| !f.contains(usb::Flags::SLOT_WRITE_FLAG),
&mut buf,
)
.await?;

self.backend.write_frame(&mut handle, &d)?;
self.backend.write_frame(&mut handle, &d).await?;

let mut response = [0; usb::RESPONSE_SIZE];
self.backend.read_response(&mut handle, &mut response)?;
self.backend.close_device(handle, interfaces)?;
self.backend.read_response(&mut handle, &mut response).await?;
self.backend.close_device(handle, interfaces).await?;

// Check response.
if crc16(&response[..18]) != CRC_RESIDUAL_OK {
Expand All @@ -183,8 +196,8 @@ impl ChallengeResponse {
mod tests {
use super::*;

#[test]
fn test_find_device() {
#[tokio::test]
async fn test_find_device() {
let mut cr_client = match ChallengeResponse::new() {
Ok(c) => c,
Err(e) => {
Expand All @@ -193,13 +206,13 @@ mod tests {
}
};

if let Err(e) = cr_client.find_device() {
if let Err(e) = cr_client.find_device().await {
assert!(matches!(e, ChallengeResponseError::DeviceNotFound));
};
}

#[test]
fn test_find_all_devices() {
#[tokio::test]
async fn test_find_all_devices() {
let mut cr_client = match ChallengeResponse::new() {
Ok(c) => c,
Err(e) => {
Expand All @@ -208,7 +221,7 @@ mod tests {
}
};

if let Err(e) = cr_client.find_all_devices() {
if let Err(e) = cr_client.find_all_devices().await {
assert!(matches!(e, ChallengeResponseError::DeviceNotFound));
};
}
Expand Down
Loading
Loading