use std::io::{Cursor, Read, Write};
use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt};
use crate::u2f::{
error::Error,
proto::{
constants::*,
raw_message::apdu::{ApduFrame, Request, Response},
},
};
pub struct RegisterRequest {
pub challenge: [u8; U2F_CHAL_SIZE],
pub application: [u8; U2F_APPID_SIZE],
}
pub struct RegisterResponse {
pub reserved: u8,
pub user_public_key: [u8; U2F_EC_POINT_SIZE],
pub key_handle_length: u8,
pub key_handle: String,
pub attestation_cert: Vec<u8>,
pub signature: Vec<u8>,
}
pub struct AuthenticateRequest {
pub control: u8,
pub challenge: [u8; U2F_CHAL_SIZE],
pub application: [u8; U2F_CHAL_SIZE],
pub key_h_len: u8,
pub key_handle: Vec<u8>,
}
pub struct AuthenticateResponse {
pub user_presence: u8,
pub counter: u32,
pub signature: Vec<u8>,
}
pub struct VersionRequest {}
pub struct VersionResponse {
pub version: String,
}
pub trait Message {
type Apdu: ApduFrame;
fn from_apdu(apdu: Self::Apdu) -> Result<Self, Error>
where
Self: Sized;
fn into_apdu(self) -> Result<Self::Apdu, Error>;
}
impl Message for RegisterRequest {
type Apdu = Request;
fn from_apdu(apdu: Self::Apdu) -> Result<Self, Error>
where
Self: Sized,
{
if apdu.command_mode != U2F_REGISTER {
return Err(Error::UnexpectedApdu(format!(
"Expecting Register Command Mode, got {}",
apdu.command_mode
)));
}
if apdu.data_len.filter(|l| *l == 64).is_none() {
return Err(Error::MalformedApdu);
}
apdu.data.ok_or(Error::MalformedApdu).and_then(|data| {
let mut cursor = Cursor::new(data);
let mut challenge = [0u8; 32];
cursor.read_exact(&mut challenge)?;
let mut application = [0u8; 32];
cursor.read_exact(&mut application)?;
Ok(RegisterRequest { challenge, application })
})
}
fn into_apdu(self) -> Result<Self::Apdu, Error> {
let RegisterRequest { challenge, application } = self;
let mut data = Vec::new();
data.extend_from_slice(&challenge);
data.extend_from_slice(&application);
Ok(Request {
class_byte: 0,
command_mode: U2F_REGISTER,
param_1: 0,
param_2: 0,
data_len: Some(64),
data: Some(data),
max_rsp_len: Some(MAX_RESPONSE_LEN_EXTENDED),
})
}
}
impl Message for RegisterResponse {
type Apdu = Response;
fn from_apdu(apdu: Self::Apdu) -> Result<Self, Error>
where
Self: Sized,
{
if apdu.status != U2F_SW_NO_ERROR {
return Err(apdu.status.into());
}
apdu.data.ok_or(Error::MalformedApdu).and_then(|data| {
let data_len = data.len();
let mut cursor = Cursor::new(data);
let reserved = cursor.read_u8()?;
if reserved != U2F_REGISTER_ID {
return Err(Error::MalformedApdu);
}
let mut user_public_key = [0u8; 65];
cursor.read_exact(&mut user_public_key)?;
let key_handle_length = cursor.read_u8()?;
let mut key_handle_bytes = vec![0u8; key_handle_length as usize];
cursor.read_exact(&mut key_handle_bytes[..])?;
let key_handle = String::from_utf8(key_handle_bytes)
.map_err(|e| Error::UnexpectedApdu(format!("Got error while parsing key_handle string: {:?}", e)))?;
let mut attestation_cert = vec![0u8; data_len - cursor.position() as usize];
cursor.read_exact(&mut attestation_cert[..])?;
let read_len_max = if attestation_cert.len() < (ASN1_MAX_FOLLOWING_LEN_BYTES + 2) {
attestation_cert.len()
} else {
ASN1_MAX_FOLLOWING_LEN_BYTES + 2
};
let cert_len = attestation_cert_length(&attestation_cert[0..read_len_max])?;
if cert_len > U2F_MAX_ATT_CERT_SIZE || cert_len > attestation_cert.len() {
return Err(Error::MalformedApdu);
}
let signature = attestation_cert.split_off(cert_len);
Ok(RegisterResponse {
reserved,
user_public_key,
key_handle_length,
key_handle,
attestation_cert,
signature,
})
})
}
fn into_apdu(self) -> Result<Self::Apdu, Error> {
let RegisterResponse {
reserved,
user_public_key,
key_handle_length,
key_handle,
attestation_cert,
signature,
} = self;
let mut data = Vec::new();
data.write_u8(reserved)?;
data.write_all(&user_public_key)?;
data.write_u8(key_handle_length)?;
data.write_all(key_handle.as_bytes())?;
data.write_all(&attestation_cert)?;
data.write_all(&signature)?;
Ok(Response {
data: Some(data),
status: U2F_SW_NO_ERROR,
})
}
}
impl Message for AuthenticateRequest {
type Apdu = Request;
fn from_apdu(apdu: Self::Apdu) -> Result<Self, Error>
where
Self: Sized,
{
if apdu.command_mode != U2F_AUTHENTICATE {
return Err(Error::UnexpectedApdu(format!(
"Expecting Version Command Mode, got {}",
apdu.command_mode
)));
}
let control = apdu.param_1;
match control {
U2F_AUTH_CHECK_ONLY | U2F_AUTH_ENFORCE | U2F_AUTH_DONT_ENFORCE => {}
_ => {
return Err(Error::MalformedApdu);
}
}
apdu.data.ok_or(Error::MalformedApdu).and_then(move |data| {
let mut cursor = Cursor::new(data);
let mut challenge = [0u8; 32];
cursor.read_exact(&mut challenge)?;
let mut application = [0u8; 32];
cursor.read_exact(&mut application)?;
let key_h_len = cursor.read_u8()?;
let mut key_handle = vec![0u8; key_h_len as usize];
cursor.read_exact(&mut key_handle[..])?;
Ok(AuthenticateRequest {
control,
challenge,
application,
key_h_len,
key_handle,
})
})
}
fn into_apdu(self) -> Result<Self::Apdu, Error> {
let AuthenticateRequest {
control,
challenge,
application,
key_h_len,
key_handle,
} = self;
let mut data = Vec::new();
data.write_all(&challenge)?;
data.write_all(&application)?;
data.write_u8(key_h_len)?;
data.write_all(&key_handle)?;
let data_len = data.len();
Ok(Request {
class_byte: 0,
command_mode: U2F_AUTHENTICATE,
param_1: control,
param_2: 0,
data_len: Some(data_len),
data: Some(data),
max_rsp_len: Some(MAX_RESPONSE_LEN_EXTENDED),
})
}
}
impl Message for AuthenticateResponse {
type Apdu = Response;
fn from_apdu(apdu: Self::Apdu) -> Result<Self, Error>
where
Self: Sized,
{
if apdu.status != U2F_SW_NO_ERROR {
return Err(apdu.status.into());
}
apdu.data.ok_or(Error::MalformedApdu).and_then(|data| {
let data_len = data.len();
let mut cursor = Cursor::new(data);
let user_presence = cursor.read_u8()?;
let counter = cursor.read_u32::<LittleEndian>()?;
let mut signature = vec![0u8; data_len - cursor.position() as usize];
cursor.read_exact(&mut signature[..])?;
Ok(AuthenticateResponse {
user_presence,
counter,
signature,
})
})
}
fn into_apdu(self) -> Result<Self::Apdu, Error> {
let AuthenticateResponse {
user_presence,
counter,
mut signature,
} = self;
let mut data = Vec::new();
data.write_u8(user_presence)?;
data.write_u32::<BigEndian>(counter)?;
data.append(&mut signature);
Ok(Response {
data: Some(data),
status: U2F_SW_NO_ERROR,
})
}
}
impl Message for VersionRequest {
type Apdu = Request;
fn from_apdu(apdu: Self::Apdu) -> Result<Self, Error>
where
Self: Sized,
{
if apdu.command_mode != U2F_VERSION {
return Err(Error::UnexpectedApdu(format!(
"Expecting Version Command Mode, got {}",
apdu.command_mode
)));
}
Ok(VersionRequest {})
}
fn into_apdu(self) -> Result<Self::Apdu, Error> {
Ok(Request {
class_byte: 0,
command_mode: U2F_VERSION,
param_1: 0,
param_2: 0,
data_len: None,
data: None,
max_rsp_len: Some(MAX_RESPONSE_LEN_EXTENDED),
})
}
}
impl Message for VersionResponse {
type Apdu = Response;
fn from_apdu(apdu: Self::Apdu) -> Result<Self, Error>
where
Self: Sized,
{
if apdu.status != U2F_SW_NO_ERROR {
return Err(apdu.status.into());
}
apdu.data.ok_or(Error::MalformedApdu).and_then(|data| {
Ok(VersionResponse {
version: String::from_utf8(data)
.map_err(|e| Error::UnexpectedApdu(format!("Got error while parsing version string: {:?}", e)))?,
})
})
}
fn into_apdu(self) -> Result<Self::Apdu, Error> {
Ok(Response {
data: Some(self.version.as_bytes().to_vec()),
status: U2F_SW_NO_ERROR,
})
}
}
pub mod apdu {
use std::io::{Cursor, Read, Write};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use crate::u2f::{
error::Error,
proto::constants::{MAX_RESPONSE_LEN_EXTENDED, MAX_RESPONSE_LEN_SHORT},
};
pub trait ApduFrame {
fn read_from(slice: &[u8]) -> Result<Self, Error>
where
Self: Sized;
fn write_to<W: Write>(self, writer: &mut W) -> Result<(), Error>;
fn get_frame_size(&self) -> usize;
}
#[derive(Clone)]
pub struct Request {
pub class_byte: u8,
pub command_mode: u8,
pub param_1: u8,
pub param_2: u8,
pub data_len: Option<usize>,
pub data: Option<Vec<u8>>,
pub max_rsp_len: Option<usize>,
}
impl ApduFrame for Request {
fn read_from(slice: &[u8]) -> Result<Self, Error>
where
Self: Sized,
{
let slice_len = slice.len();
let mut reader = Cursor::new(slice);
let class_byte = reader.read_u8()?;
let command_mode = reader.read_u8()?;
let param_1 = reader.read_u8()?;
let param_2 = reader.read_u8()?;
let mut data_len = None;
let mut data = None;
let mut max_rsp_len = None;
{
let remaining_len = slice_len - reader.position() as usize;
match remaining_len {
1 => {
let l_e = reader.read_u8()?;
if l_e == 0x00 {
max_rsp_len = Some(MAX_RESPONSE_LEN_SHORT)
} else {
max_rsp_len = Some(l_e as usize)
};
}
2 => {
let l_c = reader.read_u8()?;
if l_c != 0x01 {
return Err(Error::MalformedApdu);
}
data_len = Some(l_c as usize);
let mut data_vec = vec![0u8; l_c as usize];
reader.read_exact(&mut data_vec[..])?;
data = Some(data_vec);
}
3 => {
let b_1 = reader.read_u8()?;
if b_1 == 0x00 {
let l_e = reader.read_u16::<BigEndian>()?;
if l_e == 0x0000 {
max_rsp_len = Some(MAX_RESPONSE_LEN_EXTENDED);
} else {
max_rsp_len = Some(l_e as usize);
}
} else if b_1 == 0x01 {
let mut data_vec = vec![0u8; b_1 as usize];
reader.read_exact(&mut data_vec[..])?;
data_len = Some(b_1 as usize);
data = Some(data_vec);
let l_e = reader.read_u8()?;
if l_e == 0x00 {
max_rsp_len = Some(MAX_RESPONSE_LEN_SHORT)
} else {
max_rsp_len = Some(l_e as usize)
};
} else {
return Err(Error::MalformedApdu);
}
}
len if len > 3 => {
let b_1 = reader.read_u8()?;
if b_1 == 0x00 {
let l_c = reader.read_u16::<BigEndian>()?;
let mut data_vec = vec![0u8; l_c as usize];
reader.read_exact(&mut data_vec[..])?;
let l_e = reader.read_u16::<BigEndian>()?;
data_len = Some(l_c as usize);
data = Some(data_vec);
if l_e == 0x0000 {
max_rsp_len = Some(MAX_RESPONSE_LEN_EXTENDED);
} else {
max_rsp_len = Some(l_e as usize);
}
} else {
let mut data_vec = vec![0u8; b_1 as usize];
reader.read_exact(&mut data_vec[..])?;
data_len = Some(b_1 as usize);
data = Some(data_vec);
let l_e = reader.read_u8()?;
if l_e == 0x00 {
max_rsp_len = Some(MAX_RESPONSE_LEN_SHORT)
} else {
max_rsp_len = Some(l_e as usize)
};
}
}
_ => {}
}
}
Ok(Request {
class_byte,
command_mode,
param_1,
param_2,
data_len,
data,
max_rsp_len,
})
}
fn write_to<W: Write + WriteBytesExt>(self, writer: &mut W) -> Result<(), Error> {
let Request {
class_byte,
command_mode,
param_1,
param_2,
data_len,
data,
max_rsp_len,
} = self;
writer.write_u8(class_byte)?;
writer.write_u8(command_mode)?;
writer.write_u8(param_1)?;
writer.write_u8(param_2)?;
let mut l_e_offset = true;
if let Some((data_len, data)) = data_len.and_then(|l| data.map(move |d| (l, d))) {
l_e_offset = false;
writer.write_u8(0x00)?;
writer.write_u16::<BigEndian>(data_len as u16)?;
writer.write_all(&data[..])?;
}
if let Some(max_len) = max_rsp_len {
if l_e_offset {
writer.write_u8(0x00)?;
}
if max_len == MAX_RESPONSE_LEN_EXTENDED {
writer.write_u16::<BigEndian>(0x0000)?;
} else {
writer.write_u16::<BigEndian>(max_len as u16)?;
}
}
Ok(())
}
fn get_frame_size(&self) -> usize {
let mut len = 4;
let mut need_offset = false;
if let Some(ref data_len) = self.data_len {
if *data_len > 0 {
len += data_len + 2; need_offset = true;
}
}
if let Some(ref max_rsp_len) = self.max_rsp_len {
if *max_rsp_len > 0 {
len += 2;
need_offset = true;
}
}
if need_offset {
len += 1;
}
len
}
}
#[derive(Clone)]
pub struct Response {
pub data: Option<Vec<u8>>,
pub status: u16,
}
impl Response {
pub fn from_status(sw: u16) -> Self {
Response { data: None, status: sw }
}
}
impl ApduFrame for Response {
fn read_from(slice: &[u8]) -> Result<Self, Error>
where
Self: Sized,
{
let slice_len = slice.len();
if slice_len < 2 {
return Err(Error::MalformedApdu);
}
let rsp_data = &slice[0..slice_len - 2];
let status = (&slice[slice_len - 2..slice_len]).read_u16::<BigEndian>()?;
let data = if !rsp_data.is_empty() { Some(rsp_data.to_vec()) } else { None };
Ok(Response { data, status })
}
fn write_to<W: Write + WriteBytesExt>(self, writer: &mut W) -> Result<(), Error> {
let Response { data, status } = self;
if let Some(data) = data {
writer.write_all(&data[..])?;
}
Ok(writer.write_u16::<BigEndian>(status)?)
}
fn get_frame_size(&self) -> usize {
let mut len = 2; if let Some(ref data) = self.data {
len += data.len();
}
len
}
}
}
pub trait MessageFrame {
fn read_from<R: Read>(reader: &mut R) -> Result<Self, Error>
where
Self: Sized;
fn write_to<W: Write>(self, writer: &mut W) -> Result<(), Error>;
fn get_size(&self) -> usize;
}
pub fn attestation_cert_length(asn1: &[u8]) -> Result<usize, Error> {
if asn1.len() < 2 {
return Err(Error::AsnFormatError("Invalid data len".to_string()));
}
if asn1[0] != ASN1_SEQ_TYPE {
return Err(Error::AsnFormatError("Invalid type".to_string()));
}
let len = asn1[1];
if len & ASN1_DEFINITE_SHORT_MASK == 0 {
return Ok(len as usize);
}
let following_bytes = len & ASN1_DEFINITE_LONG_FOLLOWING_MASK; if following_bytes == 0 {
return Err(Error::AsnFormatError("Invalid ans len, expected definite long".to_string()));
}
if following_bytes as usize + 2 >= asn1.len() {
return Err(Error::AsnFormatError(
"Invalid ans len, too many following len bytes expected".to_string(),
));
}
let mut len: usize = 0;
for i in 0..following_bytes as usize {
len = len * 256 + (asn1[2 + i] as usize);
if len > U2F_MAX_ATT_CERT_SIZE {
return Err(Error::Other("Attestation certificate don't meet len requirements".to_string()));
}
}
len += following_bytes as usize;
Ok(len + 2) }