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![])
|
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
|
/// GET RESPONSE
|
||||||
pub(crate) fn get_response() -> Command {
|
pub(crate) fn get_response() -> Command {
|
||||||
Command::new(0x00, 0xC0, 0x00, 0x00, vec![])
|
Command::new(0x00, 0xC0, 0x00, 0x00, vec![])
|
||||||
|
|
|
@ -18,7 +18,9 @@ use crate::crypto_data::{
|
||||||
CardUploadableKey, Cryptogram, Hash, PublicKeyMaterial,
|
CardUploadableKey, Cryptogram, Hash, PublicKeyMaterial,
|
||||||
};
|
};
|
||||||
use crate::tlv::{tag::Tag, value::Value, Tlv};
|
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};
|
use crate::{Error, StatusBytes};
|
||||||
|
|
||||||
/// Low-level access to OpenPGP card functionality.
|
/// Low-level access to OpenPGP card functionality.
|
||||||
|
@ -244,6 +246,26 @@ impl CardApp {
|
||||||
Ok(resp.data()?.into())
|
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"
|
/// SELECT DATA "select a DO in the current template"
|
||||||
/// (e.g. for cardholder certificate)
|
/// (e.g. for cardholder certificate)
|
||||||
pub fn select_data(
|
pub fn select_data(
|
||||||
|
|
|
@ -179,6 +179,9 @@ pub enum SmartcardError {
|
||||||
#[error("Failed to connect to the card: {0}")]
|
#[error("Failed to connect to the card: {0}")]
|
||||||
SmartCardConnectionError(String),
|
SmartCardConnectionError(String),
|
||||||
|
|
||||||
|
#[error("NotTransacted (SCARD_E_NOT_TRANSACTED)")]
|
||||||
|
NotTransacted,
|
||||||
|
|
||||||
#[error("Generic SmartCard Error: {0}")]
|
#[error("Generic SmartCard Error: {0}")]
|
||||||
Error(String),
|
Error(String),
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,11 @@ pub trait CardClient {
|
||||||
///
|
///
|
||||||
/// `buf_size` is a hint to the backend (the backend may ignore it)
|
/// `buf_size` is a hint to the backend (the backend may ignore it)
|
||||||
/// indicating the expected maximum response size.
|
/// 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.
|
/// Set the card capabilities in the CardClient.
|
||||||
///
|
///
|
||||||
|
|
|
@ -136,15 +136,25 @@ impl PcscClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CardClient for 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 mut resp_buffer = vec![0; buf_size];
|
||||||
|
|
||||||
let resp = self.card.transmit(cmd, &mut resp_buffer).map_err(|e| {
|
let resp =
|
||||||
Error::Smartcard(SmartcardError::Error(format!(
|
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: {:?}",
|
"Transmit failed: {:?}",
|
||||||
e
|
e
|
||||||
)))
|
))),
|
||||||
})?;
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
log::debug!(" <- APDU response: {:x?}", resp);
|
log::debug!(" <- APDU response: {:x?}", resp);
|
||||||
|
|
||||||
|
|
|
@ -194,7 +194,7 @@ impl ScdClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CardClient for 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());
|
log::trace!("SCDC cmd len {}", cmd.len());
|
||||||
|
|
||||||
let hex = hex::encode(cmd);
|
let hex = hex::encode(cmd);
|
||||||
|
@ -215,10 +215,10 @@ impl CardClient for ScdClient {
|
||||||
log::debug!("SCDC command: '{}'", send);
|
log::debug!("SCDC command: '{}'", send);
|
||||||
|
|
||||||
if send.len() > ASSUAN_LINELENGTH {
|
if send.len() > ASSUAN_LINELENGTH {
|
||||||
return Err(anyhow!(
|
return Err(Error::InternalError(anyhow!(
|
||||||
"APDU command is too long ({}) to send via Assuan",
|
"APDU command is too long ({}) to send via Assuan",
|
||||||
send.len()
|
send.len()
|
||||||
));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.agent.send(send)?;
|
self.agent.send(send)?;
|
||||||
|
@ -228,10 +228,10 @@ impl CardClient for ScdClient {
|
||||||
while let Some(response) = rt.block_on(self.agent.next()) {
|
while let Some(response) = rt.block_on(self.agent.next()) {
|
||||||
log::debug!("res: {:x?}", response);
|
log::debug!("res: {:x?}", response);
|
||||||
if response.is_err() {
|
if response.is_err() {
|
||||||
return Err(anyhow!(
|
return Err(Error::InternalError(anyhow!(
|
||||||
"Unexpected error response from SCD {:?}",
|
"Unexpected error response from SCD {:?}",
|
||||||
response
|
response
|
||||||
));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(Response::Data { partial }) = 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) {
|
fn init_caps(&mut self, caps: CardCaps) {
|
||||||
|
|
|
@ -31,6 +31,13 @@ pub enum Command {
|
||||||
#[structopt(name = "card ident", short = "c", long = "card")]
|
#[structopt(name = "card ident", short = "c", long = "card")]
|
||||||
ident: String,
|
ident: String,
|
||||||
},
|
},
|
||||||
|
SetIdentity {
|
||||||
|
#[structopt(name = "card ident", short = "c", long = "card")]
|
||||||
|
ident: String,
|
||||||
|
|
||||||
|
#[structopt(name = "identity")]
|
||||||
|
id: u8,
|
||||||
|
},
|
||||||
Admin {
|
Admin {
|
||||||
#[structopt(name = "card ident", short = "c", long = "card")]
|
#[structopt(name = "card ident", short = "c", long = "card")]
|
||||||
ident: String,
|
ident: String,
|
||||||
|
|
|
@ -34,6 +34,9 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
cli::Command::Status { ident, verbose } => {
|
cli::Command::Status { ident, verbose } => {
|
||||||
print_status(ident, verbose)?;
|
print_status(ident, verbose)?;
|
||||||
}
|
}
|
||||||
|
cli::Command::SetIdentity { ident, id } => {
|
||||||
|
set_identity(&ident, id)?;
|
||||||
|
}
|
||||||
cli::Command::Decrypt {
|
cli::Command::Decrypt {
|
||||||
ident,
|
ident,
|
||||||
user_pin,
|
user_pin,
|
||||||
|
@ -147,6 +150,17 @@ fn list_cards() -> Result<()> {
|
||||||
Ok(())
|
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<()> {
|
fn print_status(ident: Option<String>, verbose: bool) -> Result<()> {
|
||||||
let mut ca = if let Some(ident) = ident {
|
let mut ca = if let Some(ident) = ident {
|
||||||
util::open_card(&ident)?
|
util::open_card(&ident)?
|
||||||
|
|
Loading…
Reference in a new issue