mirror of
https://github.com/trussed-dev/usbd-ccid.git
synced 2026-06-20 04:16:20 -07:00
Reorg where things live to remove dependencies on ARM
This commit is contained in:
committed by
Nicolas Stalder
parent
e74150c635
commit
a30962e1ee
@@ -17,7 +17,6 @@ logging = { package = "logging", path = "../logging",version = "0.1.0" }
|
||||
|
||||
apdu-dispatch = { path = "../apdu-dispatch" }
|
||||
iso7816 = { path = "../iso7816" }
|
||||
untrusted = "0.7.1"
|
||||
usb-device = { version = "0.2.3", features = ["control-buffer-256"] }
|
||||
|
||||
[features]
|
||||
|
||||
-345
@@ -1,345 +0,0 @@
|
||||
pub use heapless::{consts, ArrayLength, ByteBuf};
|
||||
|
||||
const CONSTRUCTED: u8 = 1 << 5;
|
||||
// const CONTEXT_SPECIFIC: u8 = 2 << 6;
|
||||
|
||||
/// ASN.1 Tags
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
#[repr(u8)]
|
||||
pub enum Tag {
|
||||
// Eoc = 0x00,
|
||||
// Boolean = 0x01,
|
||||
Integer = 0x02,
|
||||
// BitString = 0x03,
|
||||
// OctetString = 0x04,
|
||||
// Null = 0x05,
|
||||
// Oid = 0x06,
|
||||
Sequence = CONSTRUCTED | 0x10,
|
||||
// UtcTime = 0x17,
|
||||
// GeneralizedTime = 0x18,
|
||||
// ContextSpecificConstructed0 = CONTEXT_SPECIFIC | CONSTRUCTED | 0,
|
||||
// ContextSpecificConstructed1 = CONTEXT_SPECIFIC | CONSTRUCTED | 1,
|
||||
// ContextSpecificConstructed2 = CONTEXT_SPECIFIC | CONSTRUCTED | 2,
|
||||
// ContextSpecificConstructed3 = CONTEXT_SPECIFIC | CONSTRUCTED | 3,
|
||||
}
|
||||
|
||||
// impl From<Tag> for usize {
|
||||
// fn from(tag: Tag) -> Self {
|
||||
// tag as Self
|
||||
// }
|
||||
// }
|
||||
|
||||
// impl From<Tag> for u8 {
|
||||
// fn from(tag: Tag) -> Self {
|
||||
// tag as Self
|
||||
// }
|
||||
// }
|
||||
|
||||
// the only error is buffer overflow
|
||||
type Result = core::result::Result<(), ()>;
|
||||
|
||||
/// DER writer
|
||||
#[derive(Debug)]
|
||||
pub struct Der<N>(ByteBuf<N>)
|
||||
where
|
||||
N: ArrayLength<u8>;
|
||||
|
||||
impl<N: ArrayLength<u8>> Default for Der<N> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: ArrayLength<u8>> core::ops::Deref for Der<N> {
|
||||
type Target = ByteBuf<N>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: ArrayLength<u8>> core::ops::DerefMut for Der<N> {
|
||||
fn deref_mut(&mut self) -> &mut ByteBuf<N> {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: ArrayLength<u8>> Der<N> {
|
||||
/// Create a new `Der` structure that writes values to the given buffer
|
||||
pub fn new() -> Self {
|
||||
Der(ByteBuf::new())
|
||||
}
|
||||
|
||||
// // equivalent of method in std::io::Write
|
||||
// fn write_all(&mut self, data: &[u8]) -> Result {
|
||||
// self.0.extend_from_slice(data)
|
||||
// }
|
||||
|
||||
/// Return underlying buffer
|
||||
pub fn into_inner(self) -> ByteBuf<N> {
|
||||
self.0
|
||||
}
|
||||
|
||||
// https://docs.microsoft.com/en-us/windows/win32/seccertenroll/about-encoded-length-and-value-bytes
|
||||
fn write_length_field(&mut self, length: usize) -> Result {
|
||||
if length < 0x80 {
|
||||
// values under 128: write length directly as u8
|
||||
self.extend_from_slice(&[length as u8])
|
||||
} else {
|
||||
// values at least 128:
|
||||
// - write number of bytes needed as u8, setting bit 7
|
||||
// - write l as big-endian bytes representation, with minimal length
|
||||
|
||||
let mut repr = &length.to_be_bytes()[..];
|
||||
while repr[0] == 0 {
|
||||
repr = &repr[1..];
|
||||
}
|
||||
self.extend_from_slice(&[0x80 | repr.len() as u8])?;
|
||||
self.extend_from_slice(repr)
|
||||
}
|
||||
}
|
||||
|
||||
// // /// Write a `NULL` tag.
|
||||
// // pub fn null(&mut self) -> Result {
|
||||
// // self.0.extend_from_slice(&[Tag::Null as u8, 0])?;
|
||||
// // Ok(())
|
||||
// // }
|
||||
|
||||
// /// Write an arbitrary tag-length-value
|
||||
// pub fn raw_tlv(&mut self, tag: Tag, value: &[u8]) -> Result {
|
||||
// self.extend_from_slice(&[tag as u8])?;
|
||||
// self.write_length_field(value.len())?;
|
||||
// self.extend_from_slice(value)
|
||||
// }
|
||||
|
||||
/// Write an arbitrary tag-length-value
|
||||
pub fn raw_tlv(&mut self, tag: u8, value: &[u8]) -> Result {
|
||||
self.extend_from_slice(&[tag])?;
|
||||
self.write_length_field(value.len())?;
|
||||
self.extend_from_slice(value)
|
||||
}
|
||||
|
||||
/// Write an arbitrary tag-length-value with 2-byte tag
|
||||
/// NB: everything in ISO 7816 is big-endian
|
||||
pub fn raw_tlv2(&mut self, tag: u16, value: &[u8]) -> Result {
|
||||
self.extend_from_slice(&tag.to_be_bytes())?;
|
||||
self.write_length_field(value.len())?;
|
||||
self.extend_from_slice(value)
|
||||
}
|
||||
|
||||
///// Write the given input as integer.
|
||||
/////
|
||||
///// Assumes `input` is the big-endian representation of a non-negative `Integer`
|
||||
/////
|
||||
///// Not sure about good references, maybe:
|
||||
///// https://docs.microsoft.com/en-us/windows/win32/seccertenroll/about-integer
|
||||
/////
|
||||
///// From: https://docs.rs/ecdsa/0.3.0/src/ecdsa/convert.rs.html#205-219
|
||||
///// Compute ASN.1 DER encoded length for the provided scalar.
|
||||
///// The ASN.1 encoding is signed, so its leading bit must have value 0;
|
||||
///// it must also be of minimal length (so leading bytes of value 0 must be
|
||||
///// removed, except if that would contradict the rule about the sign bit).
|
||||
//pub fn non_negative_integer(&mut self, mut integer: &[u8]) -> Result {
|
||||
// self.extend_from_slice(&[Tag::Integer as u8])?;
|
||||
|
||||
// // strip leading zero bytes
|
||||
// while !integer.is_empty() && integer[0] == 0 {
|
||||
// integer = &integer[1..];
|
||||
// }
|
||||
|
||||
// if integer.is_empty() || integer[0] >= 0x80 {
|
||||
// self.write_length_field(integer.len() + 1)?;
|
||||
// self.extend_from_slice(&[0x00])?;
|
||||
// } else {
|
||||
// self.write_length_field(integer.len())?;
|
||||
// }
|
||||
|
||||
// self.extend_from_slice(integer)
|
||||
//}
|
||||
|
||||
/// Write a nested structure by passing in a handling function that writes
|
||||
/// the serialized intermediate structure.
|
||||
pub fn nested<F>(&mut self, tag: u8, f: F) -> Result
|
||||
where
|
||||
F: FnOnce(&mut Der<N>) -> Result,
|
||||
{
|
||||
let before = self.len();
|
||||
|
||||
// serialize the nested structure
|
||||
f(self)?;
|
||||
let written = self.len() - before;
|
||||
|
||||
// generate Tag-Length prefix
|
||||
// 1 for tag, 1 for length prefix, 4 or 8 for usize itself
|
||||
//
|
||||
// could try something like: type PrefixSize =<consts::U2 as core::ops::Add<consts::U8>>::Output;
|
||||
// but not couldn't find a consts::Usize type;
|
||||
type PrefixSize = consts::U12;
|
||||
let mut prefix = Der::<PrefixSize>::new();
|
||||
|
||||
// generate prefix consisting of "tag" and length of nested structure
|
||||
prefix.extend_from_slice(&[tag])?;
|
||||
prefix.write_length_field(written)?;
|
||||
|
||||
self.insert_slice_at(&prefix, before)
|
||||
}
|
||||
|
||||
/// Write a `SEQUENCE` by passing in a handling function that writes to an intermediate `Vec`
|
||||
/// before writing the whole sequence to `self`.
|
||||
pub fn sequence<F>(&mut self, f: F) -> Result
|
||||
where
|
||||
F: FnOnce(&mut Der<N>) -> Result,
|
||||
{
|
||||
self.nested(Tag::Sequence as u8, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
// #[test]
|
||||
// fn max_prefix() {
|
||||
// let mut u32_buf = [0u8; core::mem::size_of::<u32>() + 2];
|
||||
// let mut prefix = Der::new(&mut u32_buf);
|
||||
// prefix.0.extend_from_slice(&[0u8]).unwrap();
|
||||
// assert!(prefix.write_length_field(u32::max_value() as usize).is_ok());
|
||||
// assert_eq!([0u8, 132, 255, 255, 255, 255], prefix.as_ref());
|
||||
|
||||
// let mut u64_buf = [0u8; core::mem::size_of::<u64>() + 2];
|
||||
// let mut prefix = Der::new(&mut u64_buf);
|
||||
// prefix.0.extend_from_slice(&[0u8]).unwrap();
|
||||
// assert!(prefix.write_length_field(u64::max_value() as usize).is_ok());
|
||||
// assert_eq!([0, 136, 255, 255, 255, 255, 255, 255, 255, 255], prefix.as_ref());
|
||||
// }
|
||||
|
||||
#[test]
|
||||
fn write_asn1_der_ecdsa_signature() {
|
||||
let r = [
|
||||
167u8, 156, 58, 251, 253, 197, 176, 208, 165, 146, 155, 16, 217, 152, 192, 243, 206,
|
||||
76, 214, 207, 207, 180, 237, 8, 156, 160, 64, 32, 147, 82, 213, 158,
|
||||
];
|
||||
let s = [
|
||||
184, 156, 136, 100, 87, 142, 84, 61, 235, 27, 193, 223, 254, 97, 11, 111, 80, 37, 46,
|
||||
150, 121, 96, 165, 96, 65, 242, 211, 180, 175, 91, 158, 88,
|
||||
];
|
||||
// let mut buf = [0u8; 1024];
|
||||
let mut der = Der::<consts::U1024>::new();
|
||||
der.sequence(|der| {
|
||||
der.non_negative_integer(&r)?;
|
||||
der.non_negative_integer(&s)
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
#[rustfmt::skip]
|
||||
let expected = [
|
||||
48u8, 70,
|
||||
2, 33,
|
||||
0, 167, 156, 58, 251, 253, 197, 176, 208, 165, 146, 155, 16, 217, 152,
|
||||
192, 243, 206, 76, 214, 207, 207, 180, 237, 8, 156, 160, 64, 32, 147, 82, 213, 158,
|
||||
2, 33,
|
||||
0, 184, 156, 136, 100, 87, 142, 84, 61, 235, 27, 193, 223, 254, 97, 11, 111, 80,
|
||||
37, 46, 150, 121, 96, 165, 96, 65, 242, 211, 180, 175, 91, 158, 88,
|
||||
];
|
||||
assert_eq!(der.len(), expected.len());
|
||||
assert_eq!(
|
||||
ByteBuf::<consts::U72>::from_slice(&der).unwrap(),
|
||||
ByteBuf::<consts::U72>::from_slice(&expected).unwrap(),
|
||||
);
|
||||
// assert_eq!(&got[..32], &expected[..32]);
|
||||
// assert_eq!(&got[32..64], &expected[32..64]);
|
||||
// assert_eq!(&got[64..], &expected[64..]);
|
||||
}
|
||||
}
|
||||
|
||||
//// let mut der = Der::new(&mut buf);
|
||||
//// der.sequence(|der| {
|
||||
//// der.positive_integer(n)?;
|
||||
//// der.positive_integer(e)
|
||||
//// })
|
||||
//// .unwrap();
|
||||
|
||||
//// /// Write an `OBJECT IDENTIFIER`.
|
||||
//// pub fn oid(&mut self, input: &[u8]) -> Result<()> {
|
||||
//// self.writer.0.extend_from_slice(&[Tag::Oid as u8])?;
|
||||
//// self.write_length_field(input.len())?;
|
||||
//// self.writer.0.extend_from_slice(&input)?;
|
||||
//// Ok(())
|
||||
//// }
|
||||
|
||||
//// /// Write raw bytes to `self`. This does not calculate length or apply. This should only be used
|
||||
//// /// when you know you are dealing with bytes that are already DER encoded.
|
||||
//// pub fn raw(&mut self, input: &[u8]) -> Result<()> {
|
||||
//// Ok(self.writer.0.extend_from_slice(input)?)
|
||||
//// }
|
||||
|
||||
//// /// Write a `BIT STRING`.
|
||||
//// pub fn bit_string(&mut self, unused_bits: u8, bit_string: &[u8]) -> Result<()> {
|
||||
//// self.writer.0.extend_from_slice(&[Tag::BitString as u8])?;
|
||||
//// self.write_length_field(bit_string.len() + 1)?;
|
||||
//// self.writer.0.extend_from_slice(&[unused_bits])?;
|
||||
//// self.writer.0.extend_from_slice(&bit_string)?;
|
||||
//// Ok(())
|
||||
//// }
|
||||
|
||||
//// /// Write an `OCTET STRING`.
|
||||
//// pub fn octet_string(&mut self, octet_string: &[u8]) -> Result<()> {
|
||||
//// self.writer.0.extend_from_slice(&[Tag::OctetString as u8])?;
|
||||
//// self.write_length_field(octet_string.len())?;
|
||||
//// self.writer.0.extend_from_slice(&octet_string)?;
|
||||
//// Ok(())
|
||||
//// }
|
||||
//// }
|
||||
|
||||
//// #[cfg(test)]
|
||||
//// mod test {
|
||||
//// use super::*;
|
||||
//// use untrusted::Input;
|
||||
//// use Error;
|
||||
|
||||
//// static RSA_2048_PKCS1: &'static [u8] = include_bytes!("../tests/rsa-2048.pkcs1.der");
|
||||
|
||||
//// #[test]
|
||||
//// fn write_pkcs1() {
|
||||
//// let input = Input::from(RSA_2048_PKCS1);
|
||||
//// let (n, e) = input
|
||||
//// .read_all(Error::Read, |input| {
|
||||
//// der::nested(input, Tag::Sequence, |input| {
|
||||
//// let n = der::positive_integer(input)?;
|
||||
//// let e = der::positive_integer(input)?;
|
||||
//// Ok((n.as_slice_less_safe(), e.as_slice_less_safe()))
|
||||
//// })
|
||||
//// })
|
||||
//// .unwrap();
|
||||
|
||||
//// let mut buf = Vec::new();
|
||||
//// {
|
||||
//// let mut der = Der::new(&mut buf);
|
||||
//// der.sequence(|der| {
|
||||
//// der.positive_integer(n)?;
|
||||
//// der.positive_integer(e)
|
||||
//// })
|
||||
//// .unwrap();
|
||||
//// }
|
||||
|
||||
//// assert_eq!(buf.as_slice(), RSA_2048_PKCS1);
|
||||
//// }
|
||||
|
||||
//// #[test]
|
||||
//// fn write_octet_string() {
|
||||
//// let mut buf = Vec::new();
|
||||
//// {
|
||||
//// let mut der = Der::new(&mut buf);
|
||||
//// der.octet_string(&[]).unwrap();
|
||||
//// }
|
||||
|
||||
//// assert_eq!(&buf, &[0x04, 0x00]);
|
||||
|
||||
//// let mut buf = Vec::new();
|
||||
//// {
|
||||
//// let mut der = Der::new(&mut buf);
|
||||
//// der.octet_string(&[0x0a, 0x0b, 0x0c]).unwrap();
|
||||
//// }
|
||||
|
||||
//// assert_eq!(&buf, &[0x04, 0x03, 0x0a, 0x0b, 0x0c]);
|
||||
//// }
|
||||
//// }
|
||||
-85
@@ -1,85 +0,0 @@
|
||||
pub use untrusted::{Input, Reader};
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum Error {
|
||||
HighTagNumberForm,
|
||||
LongLengthNotSupported,
|
||||
NonCanonical,
|
||||
Read,
|
||||
UnexpectedEnd,
|
||||
WrongTag,
|
||||
WrongValue,
|
||||
}
|
||||
|
||||
pub type Result<T> = core::result::Result<T, Error>;
|
||||
|
||||
impl From<untrusted::EndOfInput> for Error {
|
||||
fn from(_: untrusted::EndOfInput) -> Error {
|
||||
Error::UnexpectedEnd
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the value of the given tag and apply a decoding function to it.
|
||||
pub fn nested<'a, F, R>(input: &mut Reader<'a>, tag: u8, decoder: F) -> Result<R>
|
||||
where
|
||||
F: FnOnce(&mut untrusted::Reader<'a>) -> Result<R>,
|
||||
{
|
||||
let inner = expect_tag_and_get_value(input, tag)?;
|
||||
inner.read_all(Error::Read, decoder)
|
||||
}
|
||||
|
||||
/// Read a tag and return it's value. Errors when the expect and actual tag do not match.
|
||||
pub fn expect_tag_and_get_value<'a>(input: &mut Reader<'a>, tag: u8) -> Result<Input<'a>> {
|
||||
let (actual_tag, inner) = read_tag_and_get_value(input)?;
|
||||
if usize::from(tag) != usize::from(actual_tag) {
|
||||
return Err(Error::WrongTag);
|
||||
}
|
||||
Ok(inner)
|
||||
}
|
||||
|
||||
/// Read a tag and its value. Errors when the expected and actual tag and values do not match.
|
||||
pub fn expect_tag_and_value<'a>(input: &mut Reader<'a>, tag: u8, value: &[u8]) -> Result<()> {
|
||||
let (actual_tag, inner) = read_tag_and_get_value(input)?;
|
||||
if usize::from(tag) != usize::from(actual_tag) {
|
||||
return Err(Error::WrongTag);
|
||||
}
|
||||
if value != inner.as_slice_less_safe() {
|
||||
return Err(Error::WrongValue);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Read the next tag, and return it and its value.
|
||||
pub fn read_tag_and_get_value<'a>(input: &mut Reader<'a>) -> Result<(u8, Input<'a>)> {
|
||||
let tag = input.read_byte()?;
|
||||
if (tag & 0x1F) == 0x1F {
|
||||
return Err(Error::HighTagNumberForm);
|
||||
}
|
||||
|
||||
// If the high order bit of the first byte is set to zero then the length
|
||||
// is encoded in the seven remaining bits of that byte. Otherwise, those
|
||||
// seven bits represent the number of bytes used to encode the length.
|
||||
let length = match input.read_byte()? {
|
||||
n if (n & 0x80) == 0 => usize::from(n),
|
||||
0x81 => {
|
||||
let second_byte = input.read_byte()?;
|
||||
if second_byte < 128 {
|
||||
return Err(Error::NonCanonical);
|
||||
}
|
||||
usize::from(second_byte)
|
||||
}
|
||||
0x82 => {
|
||||
let second_byte = usize::from(input.read_byte()?);
|
||||
let third_byte = usize::from(input.read_byte()?);
|
||||
let combined = (second_byte << 8) | third_byte;
|
||||
if combined < 256 {
|
||||
return Err(Error::NonCanonical);
|
||||
}
|
||||
combined
|
||||
}
|
||||
_ => return Err(Error::LongLengthNotSupported),
|
||||
};
|
||||
|
||||
let inner = input.read_bytes(length)?;
|
||||
Ok((tag, inner))
|
||||
}
|
||||
@@ -5,10 +5,6 @@
|
||||
|
||||
pub mod constants;
|
||||
pub mod class;
|
||||
// writer
|
||||
pub mod der;
|
||||
// parser
|
||||
pub mod derp;
|
||||
pub mod pipe;
|
||||
pub mod types;
|
||||
|
||||
|
||||
-445
@@ -1,445 +0,0 @@
|
||||
use core::convert::TryFrom;
|
||||
|
||||
use cortex_m_semihosting::hprintln;
|
||||
use heapless::consts;
|
||||
|
||||
use crate::{
|
||||
der::Der,
|
||||
types::{
|
||||
apdu,
|
||||
},
|
||||
constants::*,
|
||||
types::{
|
||||
MessageBuffer,
|
||||
},
|
||||
};
|
||||
|
||||
fn write_apdu(sw1: u8, sw2: u8, data: &[u8], buffer: &mut MessageBuffer) {
|
||||
let l = data.len();
|
||||
buffer.clear();
|
||||
|
||||
buffer.extend_from_slice(data).unwrap();
|
||||
buffer.push(sw1).unwrap();
|
||||
buffer.push(sw2).unwrap();
|
||||
}
|
||||
|
||||
// top nibble of first byte is "category", here "A" = International
|
||||
// this category has 5 byte "registered application provider identifier"
|
||||
// (international RID, the other 9 nibbles is between 0x0 and 0x9).
|
||||
pub const NIST_RID: &[u8; 5] = &[0xa0, 0x00, 0x00, 0x03, 0x08];
|
||||
pub const YUBICO_RID: &[u8; 5] = &[0xa0, 0x00, 0x00, 0x05, 0x27];
|
||||
// temp, until our application is through
|
||||
pub const SOLOKEYS_RID: &[u8; 5] = &[0xa0, 0x00, 0x06, 0x06, 0x06];
|
||||
|
||||
pub const PIV_APP: [u8; 4] = [0x00, 0x00, 0x10, 0x00];
|
||||
pub const PIV_VERSION: [u8; 2] = [0x01, 0x00];
|
||||
pub const PIV_PIX: [u8; 6] = [0x00, 0x00, 0x10, 0x00, 0x01, 0x00];
|
||||
|
||||
pub const PIV_TRUNCATED_AID: [u8; 9]
|
||||
= [0xa0, 0x00, 0x00, 0x03, 0x08, 0x00, 0x00, 0x10, 0x00];
|
||||
pub const PIV_AID: [u8; 11]
|
||||
= [0xa0, 0x00, 0x00, 0x03, 0x08, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00];
|
||||
|
||||
// https://git.io/JfWuD
|
||||
pub const YUBICO_OTP_PIX: &[u8; 3] = &[0x20, 0x01, 0x01];
|
||||
pub const YUBICO_OTP_AID: &[u8; 8] = &[0xa0, 0x00, 0x00, 0x05, 0x27, 0x20, 0x01, 0x01];
|
||||
// they use it to "deauthenticate user PIN and mgmt key": https://git.io/JfWgN
|
||||
pub const YUBICO_MGMT_PIX: &[u8; 3] = &[0x47, 0x11, 0x17];
|
||||
pub const YUBICO_MGMT_AID: &[u8; 8] = &[0xa0, 0x00, 0x00, 0x05, 0x27, 0x20, 0x01, 0x01];
|
||||
|
||||
// https://git.io/JfW28
|
||||
// const (
|
||||
// // https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-78-4.pdf#page=17
|
||||
// algTag = 0x80
|
||||
// alg3DES = 0x03
|
||||
// algRSA1024 = 0x06
|
||||
// algRSA2048 = 0x07
|
||||
// algECCP256 = 0x11
|
||||
// algECCP384 = 0x14
|
||||
|
||||
// // https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-78-4.pdf#page=16
|
||||
// keyAuthentication = 0x9a
|
||||
// keyCardManagement = 0x9b
|
||||
// keySignature = 0x9c
|
||||
// keyKeyManagement = 0x9d
|
||||
// keyCardAuthentication = 0x9e
|
||||
// keyAttestation = 0xf9
|
||||
|
||||
// insVerify = 0x20
|
||||
// insChangeReference = 0x24
|
||||
// insResetRetry = 0x2c
|
||||
// insGenerateAsymmetric = 0x47
|
||||
// insAuthenticate = 0x87
|
||||
// insGetData = 0xcb
|
||||
// insPutData = 0xdb
|
||||
// insSelectApplication = 0xa4
|
||||
// insGetResponseAPDU = 0xc0
|
||||
|
||||
// // https://github.com/Yubico/yubico-piv-tool/blob/yubico-piv-tool-1.7.0/lib/ykpiv.h#L656
|
||||
// insGetSerial = 0xf8
|
||||
// insAttest = 0xf9
|
||||
// insSetPINRetries = 0xfa
|
||||
// insReset = 0xfb
|
||||
// insGetVersion = 0xfd
|
||||
// insImportKey = 0xfe
|
||||
// insSetMGMKey = 0xff
|
||||
// )
|
||||
|
||||
pub const OK: &[u8; 2] = &[0x90, 0x00];
|
||||
|
||||
// pub const SELECT: (u8, u8, u8, u8, usize) = (
|
||||
pub const SELECT: (u8, u8, u8, u8) = (
|
||||
0x00, // interindustry, channel 0, no chain, no secure messaging,
|
||||
0xa4, // SELECT
|
||||
// p1
|
||||
0x04, // data is DF name, may be AID, possibly right-truncated
|
||||
// p2: i think this is dummy here
|
||||
0x00, // b2, b1 zero means "file occurence": first/only occurence,
|
||||
// b4, b3 zero means "file control information": return FCI template
|
||||
// 256,
|
||||
);
|
||||
|
||||
//
|
||||
// See SP 800-73 Part 1, Table 7
|
||||
// for list of all objects and minimum container capacity
|
||||
// - CCC: 287
|
||||
// - CHUID: 2916
|
||||
// - discovery: 19
|
||||
// - key history: 256
|
||||
// - x5c: 1905B
|
||||
// - etc.
|
||||
//
|
||||
// pub const GET_DATA: (u8, u8, u8, u8, usize) = (
|
||||
pub const GET_DATA: (u8, u8, u8, u8) = (
|
||||
0x00, // as before, would be 0x0C for secure messaging
|
||||
0xCB, // GET DATA. There's also `CA`, setting bit 1 here
|
||||
// means (7816-4, sec. 5.1.2): use BER-TLV, as opposed
|
||||
// to "no indication provided".
|
||||
// P1, P2: 7816-4, sec. 7.4.1: bit 1 of INS set => P1,P2 identifies
|
||||
// a file. And 0x3FFF identifies current DF
|
||||
0x3F,
|
||||
0xFF,
|
||||
// 256,
|
||||
);
|
||||
|
||||
// SW (SP 800-73 Part 1, Table 6)
|
||||
// == == == == == == == == == == ==
|
||||
// 61, xx success, more response data bytes
|
||||
//
|
||||
// 63, 00 verification failed
|
||||
// 63, Cx verification failed, x furtehr retries or resets
|
||||
//
|
||||
// 68, 82 secure messaging not supported
|
||||
//
|
||||
// 69, 82 security status not satisfied
|
||||
// 69, 83 authn method blocked
|
||||
// : (more secure messaging stuff)
|
||||
//
|
||||
// 6A, 80 incorrect parameter in command data field
|
||||
// 6A, 81 function not supported
|
||||
// 6A, 82 data object not found ( = NOT FOUND for files, e.g. certificate, e.g. after GET-DATA)
|
||||
// 6A, 84 not enough memory
|
||||
// 6A, 86 incorrect parameter in P1/P2
|
||||
// 6A, 88 reference(d) data not found ( = NOT FOUND for keys, e.g. global PIN, e.g. after VERIFY)
|
||||
//
|
||||
// 90, 00 SUCCESS!
|
||||
// == == == == == == == == == == ==
|
||||
|
||||
pub fn fake_piv(command: &mut MessageBuffer) {
|
||||
let apdu = match apdu::Apdu::try_from(command.as_ref()) {
|
||||
Ok(apdu) => apdu,
|
||||
Err(_) => {
|
||||
invalid_apdu(command);
|
||||
return;
|
||||
}
|
||||
};
|
||||
if apdu.ins() != crate::types::packet::CommandType::GetSlotStatus as u8 {
|
||||
hprintln!("{}, {}", crate::types::packet::CommandType::GetSlotStatus as u8, apdu.ins()).ok();
|
||||
hprintln!(":: {:?}", &apdu).ok();
|
||||
}
|
||||
|
||||
let (cla, ins, p1, p2, le) = (*&apdu.cla(), apdu.ins(), apdu.p1(), apdu.p2(), apdu.le());
|
||||
|
||||
// match (cla, ins, p1, p2, le) {
|
||||
match (cla, ins, p1, p2) {
|
||||
// `piv-tool -n` sends SELECT for 'A0 00 00 00 01 01', with Le = 0 (?!)
|
||||
// we need to handle this one
|
||||
SELECT => {
|
||||
// pub const PIV_AID: [u8; 11]
|
||||
// = [0xa0, 0x00, 0x00, 0x03, 0x08, // 0x00, 0x00, 0x10, 0x00, 0x01, 0x00];
|
||||
//
|
||||
// 05808693 APDU: 00 A4 04 00 05 A0 00 00 03 08
|
||||
hprintln!("got SELECT").ok();
|
||||
let is_nist_rid = apdu.data() == &NIST_RID[..];
|
||||
let is_piv = apdu.data() == &PIV_AID;
|
||||
let is_trunc_piv = apdu.data() == &PIV_TRUNCATED_AID;
|
||||
let is_pivish = is_piv || is_trunc_piv || is_nist_rid;
|
||||
let is_yubico = apdu.data() == YUBICO_OTP_AID;
|
||||
|
||||
if is_pivish {
|
||||
hprintln!("for PIV").ok();
|
||||
select(command);
|
||||
} else if is_yubico {
|
||||
hprintln!("for Yubico").ok();
|
||||
command.clear();
|
||||
command.extend_from_slice(&[0x04, 0x03, 0x04, 0x01, 0x05, 0x00, 0x05, 0x0F, 0x00, 0x00]);
|
||||
command.extend_from_slice(OK);
|
||||
} else {
|
||||
panic!("unknown AID {:?}", &apdu.data());
|
||||
}
|
||||
}
|
||||
|
||||
// https://git.io/JfWaX
|
||||
// YKPIV_OBJ_AUTHENTICATION 0x5fc105 /* cert for 9a key */
|
||||
// YKPIV_OBJ_ATTESTATION 0x5fff01 <-- custom Yubico thing
|
||||
GET_DATA => {
|
||||
todo!();
|
||||
}
|
||||
|
||||
// This is what we get from `piv-agent`
|
||||
// raw APDU: 00 87 11 9A 26 7C 24 82 00 81 20 E6 57 78 FC E5 C5 D8 03 4F EA C9 17 27 D5 8A 40 54 5F BC 05 BC 6A CD 37 85 3B F5 E4 E2 A9 33 F2
|
||||
//
|
||||
// APDU: 00 87 11 9A 26
|
||||
// 7C 24
|
||||
// // 82 = response, empty = "request for request"
|
||||
// 82 00
|
||||
// // 81 = challenge, length 0x20 = 32 bytes
|
||||
// 81 20
|
||||
// E6 57 78 FC E5 C5 D8 03 4F EA C9 17 27 D5 8A 40 54 5F BC 05 BC 6A CD 37 85 3B F5 E4 E2 A9 33 F2
|
||||
//
|
||||
// reponse length = 76 bytes
|
||||
// SW: 7C 4A 82 48 30 46 02 21 00 C2 E4 D8 7E B4 4A F1 A7 71 DC F8 69 5C F5 CA BD 9A 71 C9 4F 16 FB B6 FF FF CC E2 1E D2 49 BE C8 02 21 00 BE 63 44 F3 33 CD D9 4E 1C CB 52 43 EB 1D 78 11 0E A2 AB E0 5A 3E A3 93 58 6C F0 82 28 E1 A2 B1
|
||||
// 90 00
|
||||
// GENERAL AUTHENTICATE => {
|
||||
(0x00, 0x87, 0x11, 0x9a) => {
|
||||
// P1 = alg = 0x11 = P256
|
||||
// P2 = key = 0x9a = authentication (w/PIN)
|
||||
|
||||
}
|
||||
|
||||
// getVersion in Yubico's AID
|
||||
(0x00, 0xfd, 0x00, 0x00) => {
|
||||
command.clear();
|
||||
// command.extend_from_slice(&[0x04, 0x03, 0x04]);
|
||||
command.extend_from_slice(&[0x06, 0x06, 0x06]);
|
||||
command.extend(OK);
|
||||
}
|
||||
// getSerial in Yubico's AID
|
||||
(0x00, 0x01, 0x10, 0x00) => {
|
||||
command.clear();
|
||||
// make one up :)
|
||||
command.extend_from_slice(&[0x00, 0x52, 0xf7, 0x43]);
|
||||
command.extend(OK);
|
||||
}
|
||||
// getSerial in Yubico's AID (alternate version)
|
||||
(0x00, 0xf8, 0x00, 0x00) => {
|
||||
command.clear();
|
||||
// make one up :)
|
||||
command.extend_from_slice(&[0x00, 0x52, 0xf7, 0x43]);
|
||||
command.extend(OK);
|
||||
}
|
||||
// Yubico getAttestation command (attn for, here,
|
||||
(0x00, 0xf9, 0x9a, 0x00) => {
|
||||
todo!();
|
||||
}
|
||||
|
||||
// VERIFY => {
|
||||
(0x00, 0x20, 0x00, 0x80) => {
|
||||
// P2 = 0x80 = PIV card application PIN
|
||||
|
||||
// APDU: 00 20 00 80 00
|
||||
// SW: 63 C3
|
||||
// APDU: 00 20 00 80 00
|
||||
// SW: 63 C3
|
||||
// APDU: 00 20 00 80 08 31 32 33 34 FF FF FF FF
|
||||
// SW: 63 C2
|
||||
match apdu.data().len() {
|
||||
// case of missing data: used to read out retries
|
||||
// - '63 CX' => X retries
|
||||
// - '90 00' => no PIN set
|
||||
0 => {
|
||||
command.clear();
|
||||
// 0x63 = verification failed, Cx => x = remaining tries
|
||||
command.extend_from_slice(&[0x63, 0xC3]).unwrap();
|
||||
}
|
||||
// shorter PINs are padded
|
||||
8 => {
|
||||
// PIN "1234"
|
||||
if apdu.data() == [0x31, 0x32, 0x33, 0x34, 0xff, 0xff, 0xff, 0xff] {
|
||||
command.clear();
|
||||
command.extend_from_slice(OK).unwrap();
|
||||
} else {
|
||||
command.clear();
|
||||
// TODO: decrement PIN retries (here we "set" it to 2)
|
||||
command.extend_from_slice(&[0x63, 0xc2]).unwrap();
|
||||
// if retries = 0, then return '69 83'
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
command.clear();
|
||||
// "incorrect parameter in command data field"
|
||||
command.extend_from_slice(&[0x6a, 0x80]).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
// 00000156 APDU: 00 A4 04 00 05 A0 00 00 03 08
|
||||
// 00001032 SW: 61 11 4F 06 00 00 10 00 01 00 79 07 4F 05 A0 00 00 03 08 90 00
|
||||
//
|
||||
// 00009280 APDU: 00 A4 04 00 05 A0 00 00 03 08
|
||||
// 00001095 SW: 61 11 4F 06 00 00 10 00 01 00 79 07 4F 05 A0 00 00 03 08 90 00
|
||||
//
|
||||
// 00000117 APDU: 00 FD 00 00 00
|
||||
// 00001057 SW: 04 03 04 90 00
|
||||
//
|
||||
// 00000152 APDU: 00 A4 04 00 08 A0 00 00 05 27 20 01 01
|
||||
// 00001154 SW: 04 03 04 01 05 00 05 0F 00 00 90 00
|
||||
//
|
||||
// 00000112 APDU: 00 01 10 00 00
|
||||
// 00001010 SW: 00 52 F7 43 90 00
|
||||
//
|
||||
// 00000102 APDU: 00 A4 04 00 05 A0 00 00 03 08
|
||||
// 00001426 SW: 61 11 4F 06 00 00 10 00 01 00 79 07 4F 05 A0 00 00 03 08 90 00
|
||||
_ => {
|
||||
panic!("unhandled APDU (0x{:x}, 0x{:x}, 0x{:x}, 0x{:x}, {}), !",
|
||||
cla, ins, p1, p2, le);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn invalid_apdu(command: &mut MessageBuffer) {
|
||||
command.clear();
|
||||
// figure out what the correct error status words are
|
||||
command.extend_from_slice(&[0x6a, 0x82]);
|
||||
|
||||
}
|
||||
|
||||
// calling `yubikey readers`, response from NEO OTP+U2F+CCID
|
||||
// 05808693 APDU: 00 A4 04 00 05 A0 00 00 03 08
|
||||
// 00011103 SW: 61 11 4F 06 00 00 10 00 01 00 79 07 4F 05 A0 00 00 03 08 90 00
|
||||
// 00000145 APDU: 00 FD 00 00 00
|
||||
// 00005749 SW: 01 00 04 90 00
|
||||
// 00000131 APDU: 00 A4 04 00 08 A0 00 00 05 27 20 01 01
|
||||
// 00013940 SW: 03 04 01 01 85 07 06 0F 00 00 90 00
|
||||
// 00008731 APDU: 00 01 10 00 00
|
||||
// 00008949 SW: 00 60 E8 4B 90 00
|
||||
// 00000090 APDU: 00 A4 04 00 05 A0 00 00 03 08
|
||||
// 00008148 SW: 61 11 4F 06 00 00 10 00 01 00 79 07 4F 05 A0 00 00 03 08 90 00
|
||||
// 00039651 APDU: 00 A4 04 00 05 A0 00 00 03 08
|
||||
// 00008103 SW: 61 11 4F 06 00 00 10 00 01 00 79 07 4F 05 A0 00 00 03 08 90 00
|
||||
// 00000086 APDU: 00 FD 00 00 00
|
||||
// 00006044 SW: 01 00 04 90 00
|
||||
// 00000101 APDU: 00 A4 04 00 08 A0 00 00 05 27 20 01 01
|
||||
// 00009155 SW: 03 04 01 01 85 07 06 0F 00 00 90 00
|
||||
// 00000228 APDU: 00 01 10 00 00
|
||||
// 00005829 SW: 00 60 E8 4B 90 00
|
||||
// 00000094 APDU: 00 A4 04 00 05 A0 00 00 03 08
|
||||
// 00008128 SW: 61 11 4F 06 00 00 10 00 01 00 79 07 4F 05 A0 00 00 03 08 90 00
|
||||
//
|
||||
//
|
||||
// 00003001 readerfactory.c:376:RFAddReader() Yubico YubiKey FIDO+CCID init failed.
|
||||
//
|
||||
// 03510021 APDU: 00 A4 04 00 05 A0 00 00 03 08
|
||||
// 00001106 SW: 61 11 4F 06 00 00 10 00 01 00 79 07 4F 05 A0 00 00 03 08 90 00
|
||||
// --> 90 00 = OK
|
||||
//
|
||||
// 00000104 APDU: 00 FD 00 00 00
|
||||
// 00000949 SW: 04 03 04 90 00
|
||||
// --> ?!?! what is this `FD` command?!
|
||||
//
|
||||
// 00000141 APDU: 00 A4 04 00 08 A0 00 00 05 27 20 01 01
|
||||
// 00001183 SW: 04 03 04 01 05 00 05 0F 00 00 90 00
|
||||
//
|
||||
// 00000489 APDU: 00 01 10 00 00
|
||||
// 00000950 SW: 00 52 F7 43 90 00
|
||||
// --> ?!?! what is this `01` command?!
|
||||
//
|
||||
// 00000156 APDU: 00 A4 04 00 05 A0 00 00 03 08
|
||||
// 00001032 SW: 61 11 4F 06 00 00 10 00 01 00 79 07 4F 05 A0 00 00 03 08 90 00
|
||||
//
|
||||
// 00009280 APDU: 00 A4 04 00 05 A0 00 00 03 08
|
||||
// 00001095 SW: 61 11 4F 06 00 00 10 00 01 00 79 07 4F 05 A0 00 00 03 08 90 00
|
||||
//
|
||||
// 00000117 APDU: 00 FD 00 00 00
|
||||
// 00001057 SW: 04 03 04 90 00
|
||||
//
|
||||
// 00000152 APDU: 00 A4 04 00 08 A0 00 00 05 27 20 01 01
|
||||
// 00001154 SW: 04 03 04 01 05 00 05 0F 00 00 90 00
|
||||
//
|
||||
// 00000112 APDU: 00 01 10 00 00
|
||||
// 00001010 SW: 00 52 F7 43 90 00
|
||||
//
|
||||
// 00000102 APDU: 00 A4 04 00 05 A0 00 00 03 08
|
||||
// 00001426 SW: 61 11 4F 06 00 00 10 00 01 00 79 07 4F 05 A0 00 00 03 08 90 00
|
||||
|
||||
fn select(command: &mut MessageBuffer) {
|
||||
let mut der: Der<consts::U256> = Default::default();
|
||||
der.nested(0x61, |der| {
|
||||
// Application identifier of application:
|
||||
// -> PIX (without RID, with version)
|
||||
der.raw_tlv(0x4f, &PIV_PIX)?;
|
||||
|
||||
// Coexistent tag allocation authority
|
||||
der.nested(0x79, |der| {
|
||||
// Application identifier
|
||||
der.raw_tlv(0x4f, &NIST_RID[..])
|
||||
// })?;
|
||||
})
|
||||
|
||||
// Application label (optional)
|
||||
// der.raw_tlv(0x50, ...);
|
||||
|
||||
// URL to spec of app (optional)
|
||||
// der.raw_tlv(0x5f50, ...);
|
||||
|
||||
//// Cryptographic algorithms supported
|
||||
//// Conditionally mandatory if only a suubset of SP 800-78
|
||||
//// algorithms are supported.
|
||||
////
|
||||
//// We do intend to leave out RSA!
|
||||
//der.nested(0xac, |der| {
|
||||
// // one entry per algorithm
|
||||
// // der.raw_tlv(0x80, ...[SP800-78, Table 6-2]
|
||||
|
||||
// // 0x07: RSA2048
|
||||
// der.raw_tlv(0x80, &[0x07])?;
|
||||
|
||||
// // // 0x08: AES128-ECB
|
||||
// // der.raw_tlv(0x80, &[0x08])?;
|
||||
|
||||
// // // 0x0A: AES192-ECB
|
||||
// // der.raw_tlv(0x80, &[0x0a])?;
|
||||
|
||||
// // 0x0C: AES256-ECB
|
||||
// der.raw_tlv(0x80, &[0x0c])?;
|
||||
|
||||
// // 0x11: P256
|
||||
// der.raw_tlv(0x80, &[0x11])?;
|
||||
|
||||
// // // 0x14: P384
|
||||
// // der.raw_tlv(0x80, &[0x10])?;
|
||||
|
||||
// // 25519 (this is not part of the spec! idea is to
|
||||
// // use `0xC0...0xCF` to map "custom" algorithms, behind
|
||||
// // a Cargo feature)
|
||||
// // der.raw_tlv(0x80, &[0xc0])?;
|
||||
|
||||
// // // 0x27: Cipher Suite 2 (secure messaging w/P256)
|
||||
// // der.raw_tlv(0x80, &[0x27])?;
|
||||
|
||||
// // // 0x2E: Cipher Suite 7 (secure messaging w/P384)
|
||||
// // der.raw_tlv(0x80, &[0x2e])?;
|
||||
|
||||
// // object identifier ("its value is set to 0x00")
|
||||
// der.raw_tlv(0x07, &[0x00])
|
||||
//})
|
||||
}).unwrap();
|
||||
|
||||
command.clear();
|
||||
command.extend_from_slice(&der).unwrap();
|
||||
|
||||
// // application not found
|
||||
// command.extend_from_slice(&[0x6a, 0x80]).unwrap();
|
||||
// successful exectuion
|
||||
command.extend_from_slice(&[0x90, 0x00]).unwrap();
|
||||
hprintln!("prepared command: {:?}", &command).ok();
|
||||
}
|
||||
Reference in New Issue
Block a user