mirror of
https://github.com/trussed-dev/piv-authenticator.git
synced 2026-06-20 04:16:15 -07:00
Add Put DATA parsing support
This commit is contained in:
+41
-7
@@ -11,6 +11,8 @@ use core::convert::{TryFrom, TryInto};
|
||||
// use flexiber::Decodable;
|
||||
use iso7816::{Instruction, Status};
|
||||
|
||||
use crate::container::Container;
|
||||
|
||||
use crate::state::TouchPolicy;
|
||||
pub use crate::{
|
||||
container::{
|
||||
@@ -57,7 +59,7 @@ pub enum Command<'l> {
|
||||
/// In particular, this can also decrypt or similar.
|
||||
GeneralAuthenticate(GeneralAuthenticate),
|
||||
/// Store a data object / container.
|
||||
PutData(PutData),
|
||||
PutData(PutData<'l>),
|
||||
GenerateAsymmetric(AsymmetricKeyReference),
|
||||
|
||||
/* Yubico commands */
|
||||
@@ -111,8 +113,7 @@ impl TryFrom<&[u8]> for GetData {
|
||||
if tagged_slice.tag() != flexiber::Tag::application(0x1C) {
|
||||
return Err(Status::IncorrectDataParameter);
|
||||
}
|
||||
let container: containers::Container = containers::Tag::new(tagged_slice.as_bytes())
|
||||
.try_into()
|
||||
let container = containers::Container::try_from(tagged_slice.as_bytes())
|
||||
.map_err(|_| Status::IncorrectDataParameter)?;
|
||||
|
||||
info!("request to GetData for container {:?}", container);
|
||||
@@ -246,12 +247,45 @@ pub struct AuthenticateArguments<'l> {
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub struct PutData {}
|
||||
pub enum PutData<'data> {
|
||||
DiscoveryObject(&'data [u8]),
|
||||
BitGroupTemplate(&'data [u8]),
|
||||
Any(Container, &'data [u8]),
|
||||
}
|
||||
|
||||
impl TryFrom<&[u8]> for PutData {
|
||||
impl<'data> TryFrom<&'data [u8]> for PutData<'data> {
|
||||
type Error = Status;
|
||||
fn try_from(_data: &[u8]) -> Result<Self, Self::Error> {
|
||||
todo!();
|
||||
fn try_from(data: &'data [u8]) -> Result<Self, Self::Error> {
|
||||
use crate::tlv::take_do;
|
||||
let (tag, inner, rem) = take_do(data).ok_or_else(|| {
|
||||
warn!("Failed to parse PUT DATA: {:02x?}", data);
|
||||
Status::IncorrectDataParameter
|
||||
})?;
|
||||
if matches!(tag, 0x7E | 0x7F61) && !rem.is_empty() {
|
||||
warn!("Empty remainder expected, got: {:02x?}", rem);
|
||||
}
|
||||
|
||||
let container: Container = match tag {
|
||||
0x7E => return Ok(PutData::DiscoveryObject(inner)),
|
||||
0x7F61 => return Ok(PutData::BitGroupTemplate(inner)),
|
||||
0x5C => Container::try_from(inner).map_err(|_| Status::IncorrectDataParameter)?,
|
||||
_ => return Err(Status::IncorrectDataParameter),
|
||||
};
|
||||
|
||||
let (tag, inner, rem) = take_do(data).ok_or_else(|| {
|
||||
warn!("Failed to parse PUT DATA's second field: {:02x?}", data);
|
||||
Status::IncorrectDataParameter
|
||||
})?;
|
||||
|
||||
if !rem.is_empty() {
|
||||
warn!("Empty second remainder expected, got: {:02x?}", rem);
|
||||
}
|
||||
|
||||
if tag != 0x53 {
|
||||
warn!("Expected 0x53 tag, got: 0x{:02x?}", rem);
|
||||
}
|
||||
|
||||
Ok(PutData::Any(container, inner))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-37
@@ -74,13 +74,6 @@ macro_rules! enum_subset {
|
||||
|
||||
pub(crate) use enum_subset;
|
||||
|
||||
pub struct Tag<'a>(&'a [u8]);
|
||||
impl<'a> Tag<'a> {
|
||||
pub fn new(slice: &'a [u8]) -> Self {
|
||||
Self(slice)
|
||||
}
|
||||
}
|
||||
|
||||
/// Security condition for the use of a given key.
|
||||
pub enum SecurityCondition {
|
||||
Pin,
|
||||
@@ -257,33 +250,6 @@ pub enum Container {
|
||||
PairingCodeReferenceDataContainer,
|
||||
}
|
||||
|
||||
pub struct ContainerId(u16);
|
||||
|
||||
impl From<Container> for ContainerId {
|
||||
fn from(container: Container) -> Self {
|
||||
use Container::*;
|
||||
Self(match container {
|
||||
CardCapabilityContainer => 0xDB00,
|
||||
CardHolderUniqueIdentifier => 0x3000,
|
||||
X509CertificateFor9A => 0x0101,
|
||||
CardholderFingerprints => 0x6010,
|
||||
SecurityObject => 0x9000,
|
||||
CardholderFacialImage => 0x6030,
|
||||
X509CertificateFor9E => 0x0500,
|
||||
X509CertificateFor9C => 0x0100,
|
||||
X509CertificateFor9D => 0x0102,
|
||||
PrintedInformation => 0x3001,
|
||||
DiscoveryObject => 0x6050,
|
||||
KeyHistoryObject => 0x6060,
|
||||
RetiredX509Certificate(RetiredIndex(i)) => 0x1000u16 + i as u16,
|
||||
CardholderIrisImages => 0x1015,
|
||||
BiometricInformationTemplatesGroupTemplate => 0x1016,
|
||||
SecureMessagingCertificateSigner => 0x1017,
|
||||
PairingCodeReferenceDataContainer => 0x1018,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// these are just the "contact" rules, need to model "contactless" also
|
||||
pub enum ReadAccessRule {
|
||||
Always,
|
||||
@@ -326,11 +292,11 @@ pub enum ReadAccessRule {
|
||||
// }
|
||||
// }
|
||||
|
||||
impl TryFrom<Tag<'_>> for Container {
|
||||
impl TryFrom<&[u8]> for Container {
|
||||
type Error = ();
|
||||
fn try_from(tag: Tag<'_>) -> Result<Self, ()> {
|
||||
fn try_from(tag: &[u8]) -> Result<Self, ()> {
|
||||
use Container::*;
|
||||
Ok(match tag.0 {
|
||||
Ok(match tag {
|
||||
hex!("5FC107") => CardCapabilityContainer,
|
||||
hex!("5FC102") => CardHolderUniqueIdentifier,
|
||||
hex!("5FC105") => X509CertificateFor9A,
|
||||
|
||||
@@ -24,6 +24,7 @@ mod dispatch;
|
||||
pub mod piv_types;
|
||||
mod reply;
|
||||
pub mod state;
|
||||
mod tlv;
|
||||
|
||||
pub use piv_types::{AsymmetricAlgorithms, Pin, Puk};
|
||||
|
||||
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
// Copyright (C) 2022 Nitrokey GmbH
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
//! Utilities for dealing with TLV (Tag-Length-Value) encoded data
|
||||
|
||||
pub fn get_do<'input>(tag_path: &[u16], data: &'input [u8]) -> Option<&'input [u8]> {
|
||||
let mut to_ret = data;
|
||||
let mut remainder = data;
|
||||
for tag in tag_path {
|
||||
loop {
|
||||
let (cur_tag, cur_value, cur_remainder) = take_do(remainder)?;
|
||||
remainder = cur_remainder;
|
||||
if *tag == cur_tag {
|
||||
to_ret = cur_value;
|
||||
remainder = cur_value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(to_ret)
|
||||
}
|
||||
|
||||
/// Returns (tag, data, remainder)
|
||||
pub fn take_do(data: &[u8]) -> Option<(u16, &[u8], &[u8])> {
|
||||
let (tag, remainder) = take_tag(data)?;
|
||||
let (len, remainder) = take_len(remainder)?;
|
||||
if remainder.len() < len {
|
||||
warn!("Tried to parse TLV with data length shorter that the length data");
|
||||
None
|
||||
} else {
|
||||
let (value, remainder) = remainder.split_at(len);
|
||||
Some((tag, value, remainder))
|
||||
}
|
||||
}
|
||||
|
||||
// See
|
||||
// https://www.emvco.com/wp-content/uploads/2017/05/EMV_v4.3_Book_3_Application_Specification_20120607062110791.pdf
|
||||
// Annex B1
|
||||
fn take_tag(data: &[u8]) -> Option<(u16, &[u8])> {
|
||||
let b1 = *data.first()?;
|
||||
if (b1 & 0x1f) == 0x1f {
|
||||
let b2 = *data.get(1)?;
|
||||
|
||||
if (b2 & 0b10000000) != 0 {
|
||||
// OpenPGP doesn't have any DO with a tag longer than 2 bytes
|
||||
warn!("Got a tag larger than 2 bytes: {data:x?}");
|
||||
return None;
|
||||
}
|
||||
Some((u16::from_be_bytes([b1, b2]), &data[2..]))
|
||||
} else {
|
||||
Some((u16::from_be_bytes([0, b1]), &data[1..]))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn take_len(data: &[u8]) -> Option<(usize, &[u8])> {
|
||||
let l1 = *data.first()?;
|
||||
if l1 <= 0x7F {
|
||||
Some((l1 as usize, &data[1..]))
|
||||
} else if l1 == 0x81 {
|
||||
Some((*data.get(1)? as usize, &data[2..]))
|
||||
} else {
|
||||
if l1 != 0x82 {
|
||||
warn!(
|
||||
"Got an unexpected length tag: {l1:x}, data: {:x?}",
|
||||
&data[..3]
|
||||
);
|
||||
return None;
|
||||
}
|
||||
let l2 = *data.get(1)?;
|
||||
let l3 = *data.get(2)?;
|
||||
let len = u16::from_be_bytes([l2, l3]) as usize;
|
||||
Some((len as usize, &data[3..]))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use hex_literal::hex;
|
||||
use test_log::test;
|
||||
|
||||
#[test]
|
||||
fn dos() {
|
||||
assert_eq!(
|
||||
get_do(&[0x02], &hex!("02 02 1DB9 02 02 1DB9")),
|
||||
Some(hex!("1DB9").as_slice())
|
||||
);
|
||||
assert_eq!(
|
||||
get_do(&[0xA6, 0x7F49, 0x86], &hex!("A6 26 7F49 23 86 21 04 2525252525252525252525252525252525252525252525252525252525252525")),
|
||||
Some(hex!("04 2525252525252525252525252525252525252525252525252525252525252525").as_slice())
|
||||
);
|
||||
|
||||
// Multiple nested
|
||||
assert_eq!(
|
||||
get_do(&[0xA6, 0x7F49, 0x86], &hex!("A6 2A 02 02 DEAD 7F49 23 86 21 04 2525252525252525252525252525252525252525252525252525252525252525")),
|
||||
Some(hex!("04 2525252525252525252525252525252525252525252525252525252525252525").as_slice())
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user