Skip to content

Commit

Permalink
Implement command::{Delete,Reset}
Browse files Browse the repository at this point in the history
  • Loading branch information
nickray committed Mar 6, 2021
1 parent 63ae890 commit 37b9b49
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 15 deletions.
124 changes: 109 additions & 15 deletions src/authenticator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,16 +105,17 @@ where
assert!(class.channel() == Some(0));

// parse Iso7816Command as PivCommand
info_now!("before command try_into");
let command: Command = command.try_into()?;
info_now!("{:?}", &command);
info_now!("\n====\n{:?}\n====\n", &command);

match command {
Command::Select(select) => self.select(select),
Command::ListCredentials => self.list_credentials(),
Command::Register(register) => self.register(register),
Command::Calculate(calculate) => self.calculate(calculate),
Command::CalculateAll(calculate_all) => self.calculate_all(calculate_all),
Command::Delete(delete) => self.delete(delete),
Command::Reset => self.reset(),
_ => Err(iso7816::Status::FunctionNotSupported),

}
Expand All @@ -129,6 +130,106 @@ where
Ok(Data::from(data))
}

fn secret_from_credential_filename<'a>(&mut self, filename: &'a [u8]) -> Option<ObjectHandle> {
let serialized_credential = try_syscall!(
self.trussed.read_file(Location::Internal, PathBuf::from(filename))
)
.ok()?
.data;

let credential: Credential = postcard_deserialize(serialized_credential.as_ref())
.ok()?;

Some(credential.secret)
}

fn load_credential<'a>(&mut self, label: &'a [u8]) -> Option<Credential<'a>> {
let filename = self.filename_for_label(label);

let serialized_credential = try_syscall!(
self.trussed.read_file(Location::Internal, filename)
)
.ok()?
.data;

let credential: Credential = postcard_deserialize(serialized_credential.as_ref())
.ok()?;

let credential = Credential { label, ..credential };

Some(credential)
}

fn reset(&mut self) -> Result {
let mut maybe_entry = syscall!(self.trussed.read_dir_first(
Location::Internal,
PathBuf::new(),
None
)).entry;

while let Some(ref entry) = maybe_entry {
let entry_pathbuf = PathBuf::from(entry.path());
let secret = match self.secret_from_credential_filename(entry.path().as_ref().as_bytes()) {
Some(secret) => secret,
None => continue,
};
if !syscall!(self.trussed.delete(secret)).success {
debug_now!("could not delete secret {:?}", secret);
} else {
debug_now!("deleted secret {:?}", secret);
}
if try_syscall!(self.trussed.remove_file(Location::Internal, PathBuf::from(entry.path()))).is_err() {
debug_now!("could not delete credential with filename {}", entry.path());
} else {
debug_now!("deleted credential with filename {}", &entry_pathbuf);
}

// onwards
// NB: at this point, the command cache for looping is reset (this should be fixed
// upstream in Trussed, but...)
// On the other hand, we already deleted the first credential, so we can just read the
// first remaining credential.
maybe_entry = syscall!(self.trussed.read_dir_first(
Location::Internal,
PathBuf::new(),
None,
)).entry;
debug_now!("is there more? {:?}", &maybe_entry);
}
Ok(Default::default())
}

fn delete(&mut self, delete: command::Delete<'_>) -> Result {
debug_now!("{:?}", delete);
// It seems tooling first lists all credentials, so the case of
// delete being called on a non-existing label hardly occurs.

// APDU: 00 A4 04 00 07 A0 00 00 05 27 21 01
// SW: 79 03 01 00 00 71 08 26 9F 14 54 3A 0E C7 AC 90 00
// APDU: 00 A1 00 00 00
// SW: 72 13 21 74 6F 74 70 2E 64 61 6E 68 65 72 73 61 6D 2E 63 6F 6D 72 07 21 79 75 62 69 63 6F 90 00

// APDU: 00 02 00 00 08 71 06 79 75 62 69 63 6F
// SW: 90 00

let label = &delete.label;
if let Some(credential) = self.load_credential(label) {
if !syscall!(self.trussed.delete(credential.secret)).success {
debug_now!("could not delete secret {:?}", credential.secret);
} else {
debug_now!("deleted secret {:?}", credential.secret);
}

let _filename = self.filename_for_label(label);
if try_syscall!(self.trussed.remove_file(Location::Internal, _filename)).is_err() {
debug_now!("could not delete credential with filename {}", &self.filename_for_label(label));
} else {
debug_now!("deleted credential with filename {}", &self.filename_for_label(label));
}
}
Ok(Default::default())
}

/// The YK5 can store a Grande Total of 32 OATH credentials.
fn list_credentials(&mut self) -> Result {
info_now!("recv ListCredentials");
Expand Down Expand Up @@ -191,6 +292,11 @@ where
fn register(&mut self, register: command::Register<'_>) -> Result {
info_now!("recv {:?}", &register);

// 0. ykman does not call delete before register, so we need to speculatively
// delete the credential (the credential file would be replaced, but we need
// to delete the secret key).
self.delete(command::Delete { label: register.credential.label }).ok();

// 1. Store secret in Trussed
let raw_key = register.credential.secret;
let key_handle = syscall!(
Expand Down Expand Up @@ -295,19 +401,7 @@ where
fn calculate(&mut self, calculate: command::Calculate<'_>) -> Result {
info_now!("recv {:?}", &calculate);

let filename = self.filename_for_label(&calculate.label);

let serialized_credential = try_syscall!(self.trussed.read_file(
Location::Internal,
filename,
))
.map_err(|_| Status::NotFound)?
.data;

let credential: Credential = postcard_deserialize(serialized_credential.as_ref())
.map_err(|_| Status::NotFound)?;
debug!("found credential: {:?}", &credential);

let credential = self.load_credential(&calculate.label).ok_or(Status::NotFound)?;

let truncated_digest = crate::calculate::calculate(
&mut self.trussed,
Expand Down
3 changes: 3 additions & 0 deletions src/oath.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ pub enum Tag {
Key = 0x73,
Challenge = 0x74,
Response = 0x75,
/// Tag denots what follows is (digits, dynamically truncated HMAC digest)
///
/// The client then further processes u32::from_be_bytes(truncated-digest)/10**digits.
TruncatedResponse = 0x76,
NoResponse = 0x77,
Property = 0x78,
Expand Down

0 comments on commit 37b9b49

Please sign in to comment.