Have authnr not directly depend on apdu-dispatch

This commit is contained in:
Nicolas Stalder
2021-06-05 19:43:30 +02:00
committed by Nicolas Stalder
parent 9d59e891c7
commit d9857cef35
4 changed files with 94 additions and 66 deletions
+1 -1
View File
@@ -29,7 +29,7 @@ littlefs2 = "0.2.1"
rand_core = { version = "0.5.1", features = ["getrandom"] }
[features]
default = ["applet"]
default = []
applet = ["apdu-dispatch"]
strict-pin = []
+24 -24
View File
@@ -6,8 +6,8 @@
use core::convert::{TryFrom, TryInto};
// use flexiber::Decodable;
use heapless::ArrayLength;
use iso7816::{Instruction, Status};
use apdu_dispatch::{Command as IsoCommand, command::Data};
pub use crate::{container as containers, piv_types, Pin, Puk};
@@ -43,7 +43,7 @@ impl<'l> Command<'l> {
/// Core method, constructs a PIV command, if the iso7816::Command is valid.
///
/// Inherent method re-exposing the `TryFrom` implementation.
pub fn try_from(command: &'l IsoCommand) -> Result<Self, Status> {
pub fn try_from<C: ArrayLength<u8>>(command: &'l iso7816::Command<C>) -> Result<Self, Status> {
command.try_into()
}
}
@@ -54,12 +54,12 @@ pub struct Select<'l> {
pub aid: &'l [u8],
}
impl<'l> TryFrom<&'l Data> for Select<'l> {
impl<'l> TryFrom<&'l [u8]> for Select<'l> {
type Error = Status;
/// We allow ourselves the option of answering to more than just the official PIV AID.
/// For instance, to offer additional functionality, under our own RID.
fn try_from(data: &'l Data) -> Result<Self, Self::Error> {
Ok(match data.as_slice() {
fn try_from(data: &'l [u8]) -> Result<Self, Self::Error> {
Ok(match data {
crate::constants::PIV_AID => Self { aid: data },
_ => return Err(Status::NotFound),
})
@@ -70,9 +70,9 @@ impl<'l> TryFrom<&'l Data> for Select<'l> {
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct GetData(containers::Container);
impl TryFrom<&Data> for GetData {
impl TryFrom<&[u8]> for GetData {
type Error = Status;
fn try_from(data: &Data) -> Result<Self, Self::Error> {
fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
let mut decoder = flexiber::Decoder::new(data);
let tagged_slice: flexiber::TaggedSlice = decoder.decode().map_err(|_| Status::IncorrectDataParameter)?;
if tagged_slice.tag() != flexiber::Tag::application(0x1C) {
@@ -135,7 +135,7 @@ impl TryFrom<u8> for VerifyLogout {
pub struct VerifyArguments<'l> {
pub key_reference: VerifyKeyReference,
pub logout: VerifyLogout,
pub data: &'l Data
pub data: &'l [u8],
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
@@ -161,7 +161,7 @@ impl TryFrom<VerifyArguments<'_>> for Verify {
}
Ok(match (logout.0, data.len()) {
(false, 0) => Verify::Status(key_reference),
(false, 8) => Verify::Login(VerifyLogin::PivPin(data.as_slice().try_into().map_err(|_| Status::IncorrectDataParameter)?)),
(false, 8) => Verify::Login(VerifyLogin::PivPin(data.try_into().map_err(|_| Status::IncorrectDataParameter)?)),
(false, _) => return Err(Status::IncorrectDataParameter),
(true, 0) => Verify::Logout(key_reference),
(true, _) => return Err(Status::IncorrectDataParameter),
@@ -192,7 +192,7 @@ impl TryFrom<u8> for ChangeReferenceKeyReference {
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct ChangeReferenceArguments<'l> {
pub key_reference: ChangeReferenceKeyReference,
pub data: &'l Data
pub data: &'l [u8],
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
@@ -232,9 +232,9 @@ pub struct ResetPinRetries {
pub puk: [u8; 8],
}
impl TryFrom<&Data> for ResetPinRetries {
impl TryFrom<&[u8]> for ResetPinRetries {
type Error = Status;
fn try_from(data: &Data) -> Result<Self, Self::Error> {
fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
if data.len() != 16 {
return Err(Status::IncorrectDataParameter);
}
@@ -317,7 +317,7 @@ pub struct AuthenticateArguments<'l> {
/// this is passed through as-is.
pub unparsed_algorithm: u8,
pub key_reference: AuthenticateKeyReference,
pub data: &'l Data
pub data: &'l [u8],
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
@@ -326,7 +326,7 @@ pub enum Authenticate {
impl TryFrom<AuthenticateArguments<'_>> for Authenticate {
type Error = Status;
fn try_from(arguments: AuthenticateArguments<'_>) -> Result<Self, Self::Error> {
fn try_from(_arguments: AuthenticateArguments<'_>) -> Result<Self, Self::Error> {
todo!();
}
}
@@ -335,9 +335,9 @@ impl TryFrom<AuthenticateArguments<'_>> for Authenticate {
pub struct PutData {
}
impl TryFrom<&Data> for PutData {
impl TryFrom<&[u8]> for PutData {
type Error = Status;
fn try_from(data: &Data) -> Result<Self, Self::Error> {
fn try_from(_data: &[u8]) -> Result<Self, Self::Error> {
todo!();
}
}
@@ -369,7 +369,7 @@ impl TryFrom<u8> for GenerateAsymmetricKeyReference {
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct GenerateAsymmetricArguments<'l> {
pub key_reference: GenerateAsymmetricKeyReference,
pub data: &'l Data
pub data: &'l [u8],
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
@@ -378,12 +378,12 @@ pub enum GenerateAsymmetric {
impl TryFrom<GenerateAsymmetricArguments<'_>> for GenerateAsymmetric {
type Error = Status;
fn try_from(arguments: GenerateAsymmetricArguments<'_>) -> Result<Self, Self::Error> {
fn try_from(_arguments: GenerateAsymmetricArguments<'_>) -> Result<Self, Self::Error> {
todo!();
}
}
impl<'l> TryFrom<&'l IsoCommand> for Command<'l> {
impl<'l, C: ArrayLength<u8>> TryFrom<&'l iso7816::Command<C>> for Command<'l> {
type Error = Status;
/// The first layer of unraveling the iso7816::Command onion.
///
@@ -391,7 +391,7 @@ impl<'l> TryFrom<&'l IsoCommand> for Command<'l> {
/// in the "Command Syntax" boxes of NIST SP 800-73-4, and return early errors.
///
/// The individual piv::Command TryFroms then further interpret these validated parameters.
fn try_from(command: &'l IsoCommand) -> Result<Self, Self::Error> {
fn try_from(command: &'l iso7816::Command<C>) -> Result<Self, Self::Error> {
let (class, instruction, p1, p2) = (command.class(), command.instruction(), command.p1, command.p2);
let data = command.data();
@@ -408,11 +408,11 @@ impl<'l> TryFrom<&'l IsoCommand> for Command<'l> {
Ok(match (class.into_inner(), instruction, p1, p2) {
(0x00, Instruction::Select, 0x04, 0x00) => {
Self::Select(Select::try_from(data)?)
Self::Select(Select::try_from(data.as_slice())?)
}
(0x00, Instruction::GetData, 0x3F, 0xFF) => {
Self::GetData(GetData::try_from(data)?.0)
Self::GetData(GetData::try_from(data.as_slice())?.0)
}
(0x00, Instruction::Verify, p1, p2) => {
@@ -427,7 +427,7 @@ impl<'l> TryFrom<&'l IsoCommand> for Command<'l> {
}
(0x00, Instruction::ResetRetryCounter, 0x00, 0x80) => {
Self::ResetPinRetries(ResetPinRetries::try_from(data)?)
Self::ResetPinRetries(ResetPinRetries::try_from(data.as_slice())?)
}
(0x00, Instruction::GeneralAuthenticate, p1, p2) => {
@@ -437,7 +437,7 @@ impl<'l> TryFrom<&'l IsoCommand> for Command<'l> {
}
(0x00, Instruction::PutData, 0x3F, 0xFF) => {
Self::PutData(PutData::try_from(data)?)
Self::PutData(PutData::try_from(data.as_slice())?)
}
(0x00, Instruction::GenerateAsymmetricKeyPair, 0x00, p2) => {
+63 -33
View File
@@ -19,8 +19,8 @@ pub use piv_types::{Pin, Puk};
use core::convert::TryInto;
use flexiber::EncodableHeapless;
use iso7816::Status;
use apdu_dispatch::{Command as IsoCommand, response};
use heapless::ArrayLength;
use iso7816::{Data, Status};
use trussed::client;
use trussed::{syscall, try_syscall};
@@ -28,15 +28,23 @@ use constants::*;
pub type Result = iso7816::Result<()>;
pub struct Authenticator<T>
/// PIV authenticator Trussed app.
///
/// The `C` parameter is necessary, as PIV includes command sequences,
/// where we need to store the previous command, so we need to know how
/// much space to allocate.
pub struct Authenticator<C, T>
where
C: ArrayLength<u8>,
{
state: state::State,
state: state::State<C>,
trussed: T,
// trussed: RefCell<Trussed>,
}
impl<T> Authenticator<T>
impl<C, T> Authenticator<C, T>
where
C: ArrayLength<u8>,
T: client::Client + client::Ed255 + client::Tdes,
{
pub fn new(
@@ -59,7 +67,11 @@ where
pub fn deselect(&mut self) {
}
pub fn select(&mut self, _apdu: &IsoCommand, reply: &mut response::Data) -> Result {
pub fn select<R>(&mut self, _apdu: &iso7816::Command<C>, reply: &mut Data<R>) -> Result
where
R: ArrayLength<u8>,
{
use piv_types::Algorithms::*;
info_now!("selecting PIV maybe");
@@ -79,8 +91,12 @@ where
Ok(())
}
pub fn respond(&mut self, command: &IsoCommand, reply: &mut response::Data) -> Result {
info_now!("PIV responding to {:?}", command);
pub fn respond<R>(&mut self, command: &iso7816::Command<C>, reply: &mut Data<R>) -> Result
where
R: ArrayLength<u8>,
{
// need to implement Debug on iso7816::Command
// info_now!("PIV responding to {:?}", command);
let last_or_only = command.class().chain().last_or_only();
// TODO: avoid owned copy?
@@ -100,7 +116,7 @@ where
None => {
if last_or_only {
// IsoCommand
// iso7816::Command<C>
command.clone()
} else {
self.state.runtime.chained_command = Some(command.clone());
@@ -216,7 +232,7 @@ where
return Ok(Default::default());
}
// pub fn old_respond(&mut self, command: &IsoCommand, reply: &mut response::Data) -> Result {
// pub fn old_respond(&mut self, command: &iso7816::Command<C>, reply: &mut Data<R>) -> Result {
// // TEMP
// // blocking::dbg!(self.state.persistent(&mut self.trussed).timestamp(&mut self.trussed));
@@ -246,7 +262,7 @@ where
// None => {
// if last_or_only {
// // IsoCommand
// // iso7816::Command<C>
// command.clone()
// } else {
// self.state.runtime.chained_command = Some(command.clone());
@@ -325,7 +341,10 @@ where
// - 9000, 61XX for success
// - 6982 security status
// - 6A80, 6A86 for data, P1/P2 issue
fn general_authenticate(&mut self, command: &IsoCommand, reply: &mut response::Data) -> Result {
fn general_authenticate<R>(&mut self, command: &iso7816::Command<C>, reply: &mut Data<R>) -> Result
where
R: ArrayLength<u8>,
{
// For "SSH", we need implement A.4.2 in SP-800-73-4 Part 2, ECDSA signatures
//
@@ -435,7 +454,10 @@ where
Ok(())
}
fn request_for_challenge(&mut self, command: &IsoCommand, remaining_data: &[u8], reply: &mut response::Data) -> Result {
fn request_for_challenge<R>(&mut self, command: &iso7816::Command<C>, remaining_data: &[u8], reply: &mut Data<R>) -> Result
where
R: ArrayLength<u8>,
{
// - data is of the form
// 00 87 03 9B 16 7C 14 80 08 99 6D 71 40 E7 05 DF 7F 81 08 6E EF 9C 02 00 69 73 E8
// - remaining data contains <decrypted challenge> 81 08 <encrypted counter challenge>
@@ -486,7 +508,10 @@ where
Ok(())
}
fn request_for_witness(&mut self, command: &IsoCommand, remaining_data: &[u8], reply: &mut response::Data) -> Result {
fn request_for_witness<R>(&mut self, command: &iso7816::Command<C>, remaining_data: &[u8], reply: &mut Data<R>) -> Result
where
R: ArrayLength<u8>,
{
// invariants: parsed data was '7C L1 80 00' + remaining_data
if command.p1 != 0x03 || command.p2 != 0x9b {
@@ -661,7 +686,10 @@ where
// }
//}
fn generate_asymmetric_keypair(&mut self, command: &IsoCommand, reply: &mut response::Data) -> Result {
fn generate_asymmetric_keypair<R>(&mut self, command: &iso7816::Command<C>, reply: &mut Data<R>) -> Result
where
R: ArrayLength<u8>,
{
if !self.state.runtime.app_security_status.management_verified {
return Err(Status::SecurityStatusNotSatisfied);
}
@@ -784,7 +812,7 @@ where
Ok(())
}
fn put_data(&mut self, command: &IsoCommand) -> Result {
fn put_data(&mut self, command: &iso7816::Command<C>) -> Result {
info_now!("PutData");
if command.p1 != 0x3f || command.p2 != 0xff {
return Err(Status::IncorrectP1OrP2Parameter);
@@ -878,7 +906,10 @@ where
// }
// todo!();
fn get_data(&mut self, container: container::Container, reply: &mut response::Data) -> Result {
fn get_data<R>(&mut self, container: container::Container, reply: &mut Data<R>) -> Result
where
R: ArrayLength<u8>,
{
// TODO: check security status, else return Status::SecurityStatusNotSatisfied
@@ -888,8 +919,7 @@ where
match container {
Container::DiscoveryObject => {
// Err(Status::InstructionNotSupportedOrInvalid)
let data = response::Data::try_from_slice(DISCOVERY_OBJECT).unwrap();
reply.extend_from_slice(&data).ok();
reply.extend_from_slice(DISCOVERY_OBJECT).ok();
// todo!("discovery object"),
}
@@ -934,7 +964,7 @@ where
// // '5F FF01' (754B)
// YubicoObjects::AttestationCertificate => {
// let data = response::Data::try_from_slice(YUBICO_ATTESTATION_CERTIFICATE).unwrap();
// let data = Data<R>::try_from_slice(YUBICO_ATTESTATION_CERTIFICATE).unwrap();
// reply.extend_from_slice(&data).ok();
// }
@@ -943,21 +973,22 @@ where
Ok(())
}
fn yubico_piv_extension(&mut self, command: &IsoCommand, instruction: YubicoPivExtension, reply: &mut response::Data) -> Result {
fn yubico_piv_extension<R>(&mut self, command: &iso7816::Command<C>, instruction: YubicoPivExtension, reply: &mut Data<R>) -> Result
where
R: ArrayLength<u8>,
{
info_now!("yubico extension: {:?}", &instruction);
match instruction {
YubicoPivExtension::GetSerial => {
// make up a 4-byte serial
let data = response::Data::try_from_slice(
&[0x00, 0x52, 0xf7, 0x43]).unwrap();
reply.extend_from_slice(&data).ok();
reply.extend_from_slice(
&[0x00, 0x52, 0xf7, 0x43]).ok();
}
YubicoPivExtension::GetVersion => {
// make up a version, be >= 5.0.0
let data = response::Data::try_from_slice(
&[0x06, 0x06, 0x06]).unwrap();
reply.extend_from_slice(&data).ok();
reply.extend_from_slice(
&[0x06, 0x06, 0x06]).ok();
}
YubicoPivExtension::Attest => {
@@ -968,8 +999,7 @@ where
let slot = command.p1;
if slot == 0x9a {
let data = response::Data::try_from_slice(YUBICO_ATTESTATION_CERTIFICATE_FOR_9A).unwrap();
reply.extend_from_slice(&data).ok();
reply.extend_from_slice(YUBICO_ATTESTATION_CERTIFICATE_FOR_9A).ok();
} else {
return Err(Status::FunctionNotSupported)
@@ -1040,7 +1070,7 @@ where
#[cfg(feature = "apdu-dispatch")]
impl<T> apdu_dispatch::app::Aid for Authenticator<T> {
impl<T> apdu_dispatch::app::Aid for Authenticator<apdu_dispatch::command::Size, T> {
fn aid(&self) -> &'static [u8] {
&constants::PIV_AID
@@ -1053,17 +1083,17 @@ impl<T> apdu_dispatch::app::Aid for Authenticator<T> {
#[cfg(feature = "apdu-dispatch")]
impl<T> apdu_dispatch::app::App<apdu_dispatch::command::Size, apdu_dispatch::response::Size> for Authenticator<T>
impl<T> apdu_dispatch::app::App<apdu_dispatch::command::Size, apdu_dispatch::response::Size> for Authenticator<apdu_dispatch::command::Size, T>
where
T: client::Client + client::Ed255 + client::Tdes
{
fn select(&mut self, apdu: &IsoCommand, reply: &mut response::Data) -> Result {
fn select(&mut self, apdu: &apdu_dispatch::Command, reply: &mut apdu_dispatch::response::Data) -> Result {
self.select(apdu, reply)
}
fn deselect(&mut self) { self.deselect() }
fn call(&mut self, _: iso7816::Interface, apdu: &IsoCommand, reply: &mut response::Data) -> Result {
fn call(&mut self, _: iso7816::Interface, apdu: &apdu_dispatch::Command, reply: &mut apdu_dispatch::response::Data) -> Result {
self.respond(apdu, reply)
}
}
+6 -8
View File
@@ -1,4 +1,5 @@
use core::convert::{TryFrom, TryInto};
use heapless::ArrayLength;
use trussed::{
block,
@@ -139,14 +140,14 @@ pub struct Keys {
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct State {
pub runtime: Runtime,
pub struct State<C: ArrayLength<u8>> {
pub runtime: Runtime<C>,
// temporary "state", to be removed again
// pub hack: Hack,
// trussed: RefCell<Trussed<S>>,
}
impl State {
impl<C: ArrayLength<u8>> State<C> {
pub fn new() -> Self {
Default::default()
}
@@ -234,7 +235,7 @@ impl<T> AsRef<PersistentState> for Persistent<'_, T> {
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct Runtime {
pub struct Runtime<C: ArrayLength<u8>> {
// aid: Option<
// consecutive_pin_mismatches: u8,
@@ -242,7 +243,7 @@ pub struct Runtime {
pub currently_selected_application: SelectableAid,
pub app_security_status: AppSecurityStatus,
pub command_cache: Option<CommandCache>,
pub chained_command: Option<apdu_dispatch::Command>,
pub chained_command: Option<iso7816::Command<C>>,
}
pub trait Aid {
@@ -551,6 +552,3 @@ where
}
impl Runtime {
}