Implement setting of 'identity' for NitroKey Start.
This commit is contained in:
parent
9de79477b9
commit
9e9cddc225
8 changed files with 80 additions and 15 deletions
|
@ -71,6 +71,11 @@ pub(crate) fn get_firmware_version() -> Command {
|
|||
Command::new(0x00, 0xF1, 0x00, 0x00, vec![])
|
||||
}
|
||||
|
||||
/// Set identity [0-2] (NitroKey Start specific(?))
|
||||
pub(crate) fn set_identity(id: u8) -> Command {
|
||||
Command::new(0x00, 0x85, 0x00, id, vec![])
|
||||
}
|
||||
|
||||
/// GET RESPONSE
|
||||
pub(crate) fn get_response() -> Command {
|
||||
Command::new(0x00, 0xC0, 0x00, 0x00, vec![])
|
||||
|
|
|
@ -18,7 +18,9 @@ use crate::crypto_data::{
|
|||
CardUploadableKey, Cryptogram, Hash, PublicKeyMaterial,
|
||||
};
|
||||
use crate::tlv::{tag::Tag, value::Value, Tlv};
|
||||
use crate::{apdu, keys, CardCaps, CardClient, CardClientBox, KeyType};
|
||||
use crate::{
|
||||
apdu, keys, CardCaps, CardClient, CardClientBox, KeyType, SmartcardError,
|
||||
};
|
||||
use crate::{Error, StatusBytes};
|
||||
|
||||
/// Low-level access to OpenPGP card functionality.
|
||||
|
@ -244,6 +246,26 @@ impl CardApp {
|
|||
Ok(resp.data()?.into())
|
||||
}
|
||||
|
||||
/// Set identity (Nitrokey Start specific (?)).
|
||||
/// [see:
|
||||
/// https://docs.nitrokey.com/start/linux/multiple-identities.html
|
||||
/// https://github.com/Nitrokey/nitrokey-start-firmware/pull/33/]
|
||||
pub fn set_identity(&mut self, id: u8) -> Result<Vec<u8>> {
|
||||
let resp = apdu::send_command(
|
||||
self.card_client(),
|
||||
commands::set_identity(id),
|
||||
false,
|
||||
);
|
||||
|
||||
// Apparently it's normal to get "NotTransacted" from pcsclite when
|
||||
// the identity switch was successful.
|
||||
if let Err(Error::Smartcard(SmartcardError::NotTransacted)) = resp {
|
||||
Ok(vec![])
|
||||
} else {
|
||||
Ok(resp?.data()?.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// SELECT DATA "select a DO in the current template"
|
||||
/// (e.g. for cardholder certificate)
|
||||
pub fn select_data(
|
||||
|
|
|
@ -179,6 +179,9 @@ pub enum SmartcardError {
|
|||
#[error("Failed to connect to the card: {0}")]
|
||||
SmartCardConnectionError(String),
|
||||
|
||||
#[error("NotTransacted (SCARD_E_NOT_TRANSACTED)")]
|
||||
NotTransacted,
|
||||
|
||||
#[error("Generic SmartCard Error: {0}")]
|
||||
Error(String),
|
||||
}
|
||||
|
|
|
@ -51,7 +51,11 @@ pub trait CardClient {
|
|||
///
|
||||
/// `buf_size` is a hint to the backend (the backend may ignore it)
|
||||
/// indicating the expected maximum response size.
|
||||
fn transmit(&mut self, cmd: &[u8], buf_size: usize) -> Result<Vec<u8>>;
|
||||
fn transmit(
|
||||
&mut self,
|
||||
cmd: &[u8],
|
||||
buf_size: usize,
|
||||
) -> Result<Vec<u8>, Error>;
|
||||
|
||||
/// Set the card capabilities in the CardClient.
|
||||
///
|
||||
|
|
|
@ -136,15 +136,25 @@ impl PcscClient {
|
|||
}
|
||||
|
||||
impl CardClient for PcscClient {
|
||||
fn transmit(&mut self, cmd: &[u8], buf_size: usize) -> Result<Vec<u8>> {
|
||||
fn transmit(
|
||||
&mut self,
|
||||
cmd: &[u8],
|
||||
buf_size: usize,
|
||||
) -> Result<Vec<u8>, Error> {
|
||||
let mut resp_buffer = vec![0; buf_size];
|
||||
|
||||
let resp = self.card.transmit(cmd, &mut resp_buffer).map_err(|e| {
|
||||
Error::Smartcard(SmartcardError::Error(format!(
|
||||
"Transmit failed: {:?}",
|
||||
e
|
||||
)))
|
||||
})?;
|
||||
let resp =
|
||||
self.card.transmit(cmd, &mut resp_buffer).map_err(
|
||||
|e| match e {
|
||||
pcsc::Error::NotTransacted => {
|
||||
Error::Smartcard(SmartcardError::NotTransacted)
|
||||
}
|
||||
_ => Error::Smartcard(SmartcardError::Error(format!(
|
||||
"Transmit failed: {:?}",
|
||||
e
|
||||
))),
|
||||
},
|
||||
)?;
|
||||
|
||||
log::debug!(" <- APDU response: {:x?}", resp);
|
||||
|
||||
|
|
|
@ -194,7 +194,7 @@ impl ScdClient {
|
|||
}
|
||||
|
||||
impl CardClient for ScdClient {
|
||||
fn transmit(&mut self, cmd: &[u8], _: usize) -> Result<Vec<u8>> {
|
||||
fn transmit(&mut self, cmd: &[u8], _: usize) -> Result<Vec<u8>, Error> {
|
||||
log::trace!("SCDC cmd len {}", cmd.len());
|
||||
|
||||
let hex = hex::encode(cmd);
|
||||
|
@ -215,10 +215,10 @@ impl CardClient for ScdClient {
|
|||
log::debug!("SCDC command: '{}'", send);
|
||||
|
||||
if send.len() > ASSUAN_LINELENGTH {
|
||||
return Err(anyhow!(
|
||||
return Err(Error::InternalError(anyhow!(
|
||||
"APDU command is too long ({}) to send via Assuan",
|
||||
send.len()
|
||||
));
|
||||
)));
|
||||
}
|
||||
|
||||
self.agent.send(send)?;
|
||||
|
@ -228,10 +228,10 @@ impl CardClient for ScdClient {
|
|||
while let Some(response) = rt.block_on(self.agent.next()) {
|
||||
log::debug!("res: {:x?}", response);
|
||||
if response.is_err() {
|
||||
return Err(anyhow!(
|
||||
return Err(Error::InternalError(anyhow!(
|
||||
"Unexpected error response from SCD {:?}",
|
||||
response
|
||||
));
|
||||
)));
|
||||
}
|
||||
|
||||
if let Ok(Response::Data { partial }) = response {
|
||||
|
@ -246,7 +246,7 @@ impl CardClient for ScdClient {
|
|||
}
|
||||
}
|
||||
|
||||
Err(anyhow!("no response found"))
|
||||
Err(Error::InternalError(anyhow!("no response found")))
|
||||
}
|
||||
|
||||
fn init_caps(&mut self, caps: CardCaps) {
|
||||
|
|
|
@ -31,6 +31,13 @@ pub enum Command {
|
|||
#[structopt(name = "card ident", short = "c", long = "card")]
|
||||
ident: String,
|
||||
},
|
||||
SetIdentity {
|
||||
#[structopt(name = "card ident", short = "c", long = "card")]
|
||||
ident: String,
|
||||
|
||||
#[structopt(name = "identity")]
|
||||
id: u8,
|
||||
},
|
||||
Admin {
|
||||
#[structopt(name = "card ident", short = "c", long = "card")]
|
||||
ident: String,
|
||||
|
|
|
@ -34,6 +34,9 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
cli::Command::Status { ident, verbose } => {
|
||||
print_status(ident, verbose)?;
|
||||
}
|
||||
cli::Command::SetIdentity { ident, id } => {
|
||||
set_identity(&ident, id)?;
|
||||
}
|
||||
cli::Command::Decrypt {
|
||||
ident,
|
||||
user_pin,
|
||||
|
@ -147,6 +150,17 @@ fn list_cards() -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn set_identity(
|
||||
ident: &str,
|
||||
id: u8,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut card = util::open_card(ident)?;
|
||||
|
||||
card.set_identity(id)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_status(ident: Option<String>, verbose: bool) -> Result<()> {
|
||||
let mut ca = if let Some(ident) = ident {
|
||||
util::open_card(&ident)?
|
||||
|
|
Loading…
Reference in a new issue