mirror of
https://github.com/trussed-dev/piv-authenticator.git
synced 2026-06-20 04:16:15 -07:00
Have authnr not directly depend on apdu-dispatch
This commit is contained in:
committed by
Nicolas Stalder
parent
9d59e891c7
commit
d9857cef35
+1
-1
@@ -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
@@ -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
@@ -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
@@ -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 {
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user