Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow to rename credential #97

Merged
merged 4 commits into from
Aug 10, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 59 additions & 3 deletions src/authenticator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ use core::time::Duration;
use flexiber::EncodableHeapless;
use heapless_bytes::Bytes;
use iso7816::{Data, Status};
use trussed::types::Location;
use trussed::types::{KeyId, Message};
use trussed::types::{Location, ShortData};
use trussed::{self, client, syscall, try_syscall};

use crate::calculate::hmac_challenge;
Expand Down Expand Up @@ -316,6 +316,7 @@ where
Command::Register(register) => self.register(register),
Command::Calculate(calculate) => self.calculate(calculate, reply),
Command::GetCredential(get) => self.get_credential(get, reply),
Command::RenameCredential(rename) => self.rename_credential(rename, reply),
#[cfg(feature = "calculate-all")]
Command::CalculateAll(calculate_all) => self.calculate_all(calculate_all, reply),
Command::Delete(delete) => self.delete(delete),
Expand Down Expand Up @@ -449,9 +450,9 @@ where

let label = &delete.label;
if let Some(_credential) = self.load_credential(label) {
let _filename = self.filename_for_label(label);
let filename = self.filename_for_label(label);
let _deletion_result =
try_syscall!(self.trussed.remove_file(self.options.location, _filename));
try_syscall!(self.trussed.remove_file(self.options.location, filename));
debug_now!(
"Delete credential with filename {}, result: {:?}",
&self.filename_for_label(label),
Expand Down Expand Up @@ -799,6 +800,61 @@ where
Ok(())
}

/// Rename credential
///
/// Realized by loading FlatCredential object, changing its label,
/// writing under the new name and removing the previous file.
///
/// Requires button confirmation before starting.
///
/// returns: no data, except for the result code
/// Errors:
/// - OperationBlocked if the new name is occupied already (checked by name hash)
/// - NotFound, if the current credential can't be open (e.g. due to key not available) or deserialized
/// - UnspecifiedNonpersistentExecutionError, if the old file cannot be removed, or serialization error
/// - NotEnoughMemory, if new file cannot be written
/// - SecurityStatusNotSatisfied, if the encryption key cannot be fetched
fn rename_credential<const R: usize>(
&mut self,
rename_req: command::RenameCredential<'_>,
_reply: &mut Data<R>,
) -> Result {
// DESIGN Get operation confirmation from user before proceeding
self.user_present()?;

// DESIGN check if the target name is occupied already
self.err_if_credential_with_label_exists(rename_req.new_label)?;
if !self.credential_with_label_exists(rename_req.label) {
return Err(Status::NotFound);
}

let credential = {
let mut c = self
.load_credential(rename_req.label)
.ok_or(Status::NotFound)?;
c.label =
ShortData::from_slice(rename_req.new_label).map_err(|_| Status::NotEnoughMemory)?;
c
};

// Serialize the credential (implicitly) and store it
let filename = self.filename_for_label(rename_req.new_label);
self.state.try_write_file(
&mut self.trussed,
filename,
&credential,
credential.encryption_key_type,
)?;

// Remove old file name
let filename_old = self.filename_for_label(rename_req.label);
try_syscall!(self
.trussed
.remove_file(self.options.location, filename_old))
.map_err(|_| Status::UnspecifiedNonpersistentExecutionError)?;
szszszsz marked this conversation as resolved.
Show resolved Hide resolved
Ok(())
}

fn get_credential<const R: usize>(
&mut self,
get_credential_req: command::GetCredential<'_>,
Expand Down
35 changes: 35 additions & 0 deletions src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ pub enum Command<'l> {
SendRemaining,
/// Get Credential data
GetCredential(GetCredential<'l>),
/// Rename Credential
RenameCredential(RenameCredential<'l>),
/// Return serial number of the device. Yubikey-compatible command. Used in KeepassXC.
YkSerial,
/// Return application's status. Yubikey-compatible command. Used in KeepassXC.
Expand Down Expand Up @@ -297,6 +299,36 @@ impl<'l, const C: usize> TryFrom<&'l Data<C>> for SetPin<'l> {
}
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct RenameCredential<'l> {
pub label: &'l [u8],
pub new_label: &'l [u8],
}

impl<'l, const C: usize> TryFrom<&'l Data<C>> for RenameCredential<'l> {
type Error = Status;
fn try_from(data: &'l Data<C>) -> Result<Self, Self::Error> {
use flexiber::TaggedSlice;
let mut decoder = flexiber::Decoder::new(data);

let first: TaggedSlice = decoder.decode().map_err(|_| FAILED_PARSING_ERROR)?;
ensure(
first.tag() == (Tag::Name as u8).try_into().unwrap(),
FAILED_PARSING_ERROR,
)?;
let label = first.as_bytes();

let second: TaggedSlice = decoder.decode().map_err(|_| FAILED_PARSING_ERROR)?;
ensure(
second.tag() == (Tag::Name as u8).try_into().unwrap(),
FAILED_PARSING_ERROR,
)?;
let new_label = second.as_bytes();

Ok(RenameCredential { label, new_label })
}
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct GetCredential<'l> {
pub label: &'l [u8],
Expand Down Expand Up @@ -854,6 +886,9 @@ impl<'l, const C: usize> TryFrom<&'l iso7816::Command<C>> for Command<'l> {
(0x00, oath::Instruction::GetCredential, 0x00, 0x00) => {
Self::GetCredential(GetCredential::try_from(data)?)
}
(0x00, oath::Instruction::RenameCredential, 0x00, 0x00) => {
Self::RenameCredential(RenameCredential::try_from(data)?)
}
(0x00, oath::Instruction::SendRemaining, 0x00, 0x00) => Self::SendRemaining,
_ => return Err(Status::InstructionNotSupportedOrInvalid),
})
Expand Down
1 change: 1 addition & 0 deletions src/oath.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ pub enum Instruction {
ChangePIN = 0xb3,
SetPIN = 0xb4,
GetCredential = 0xb5,
RenameCredential = 0xb6,
}

impl TryFrom<u8> for Instruction {
Expand Down