Implement setting of 'identity' for NitroKey Start.

This commit is contained in:
Heiko Schaefer 2021-11-29 18:32:26 +01:00
parent 9de79477b9
commit 9e9cddc225
No known key found for this signature in database
GPG key ID: 4A849A1904CCBD7D
8 changed files with 80 additions and 15 deletions

View file

@ -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![])

View file

@ -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(

View file

@ -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),
}

View file

@ -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.
///

View file

@ -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);

View file

@ -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) {

View file

@ -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,

View file

@ -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)?