Use Error::InternalError less, introduce additional specific error variants.
This commit is contained in:
parent
088bb88a02
commit
8ab3a43d6e
29 changed files with 312 additions and 203 deletions
|
@ -4,8 +4,6 @@
|
||||||
//! Perform operations on a card. Different states of a card are modeled by
|
//! Perform operations on a card. Different states of a card are modeled by
|
||||||
//! different types, such as `Open`, `User`, `Sign`, `Admin`.
|
//! different types, such as `Open`, `User`, `Sign`, `Admin`.
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
|
||||||
|
|
||||||
use sequoia_openpgp::cert::amalgamation::key::ValidErasedKeyAmalgamation;
|
use sequoia_openpgp::cert::amalgamation::key::ValidErasedKeyAmalgamation;
|
||||||
use sequoia_openpgp::packet::key::SecretParts;
|
use sequoia_openpgp::packet::key::SecretParts;
|
||||||
use sequoia_openpgp::Cert;
|
use sequoia_openpgp::Cert;
|
||||||
|
@ -188,7 +186,7 @@ impl<'a> Open<'a> {
|
||||||
self.ard.historical_bytes()
|
self.ard.historical_bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn extended_length_information(&self) -> Result<Option<ExtendedLengthInfo>> {
|
pub fn extended_length_information(&self) -> Result<Option<ExtendedLengthInfo>, Error> {
|
||||||
self.ard.extended_length_information()
|
self.ard.extended_length_information()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,12 +204,12 @@ impl<'a> Open<'a> {
|
||||||
self.ard.extended_capabilities()
|
self.ard.extended_capabilities()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn algorithm_attributes(&self, key_type: KeyType) -> Result<Algo> {
|
pub fn algorithm_attributes(&self, key_type: KeyType) -> Result<Algo, Error> {
|
||||||
self.ard.algorithm_attributes(key_type)
|
self.ard.algorithm_attributes(key_type)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// PW status Bytes
|
/// PW status Bytes
|
||||||
pub fn pw_status_bytes(&self) -> Result<PWStatusBytes> {
|
pub fn pw_status_bytes(&self) -> Result<PWStatusBytes, Error> {
|
||||||
self.ard.pw_status_bytes()
|
self.ard.pw_status_bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -259,22 +257,22 @@ impl<'a> Open<'a> {
|
||||||
|
|
||||||
// --- URL (5f50) ---
|
// --- URL (5f50) ---
|
||||||
|
|
||||||
pub fn url(&mut self) -> Result<String> {
|
pub fn url(&mut self) -> Result<String, Error> {
|
||||||
Ok(String::from_utf8_lossy(&self.opt.url()?).to_string())
|
Ok(String::from_utf8_lossy(&self.opt.url()?).to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- cardholder related data (65) ---
|
// --- cardholder related data (65) ---
|
||||||
pub fn cardholder_related_data(&mut self) -> Result<CardholderRelatedData> {
|
pub fn cardholder_related_data(&mut self) -> Result<CardholderRelatedData, Error> {
|
||||||
self.opt.cardholder_related_data()
|
self.opt.cardholder_related_data()
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- security support template (7a) ---
|
// --- security support template (7a) ---
|
||||||
pub fn security_support_template(&mut self) -> Result<SecuritySupportTemplate> {
|
pub fn security_support_template(&mut self) -> Result<SecuritySupportTemplate, Error> {
|
||||||
self.opt.security_support_template()
|
self.opt.security_support_template()
|
||||||
}
|
}
|
||||||
|
|
||||||
// DO "Algorithm Information" (0xFA)
|
// DO "Algorithm Information" (0xFA)
|
||||||
pub fn algorithm_information(&mut self) -> Result<Option<AlgoInfo>> {
|
pub fn algorithm_information(&mut self) -> Result<Option<AlgoInfo>, Error> {
|
||||||
// The DO "Algorithm Information" (Tag FA) shall be present if
|
// The DO "Algorithm Information" (Tag FA) shall be present if
|
||||||
// Algorithm attributes can be changed
|
// Algorithm attributes can be changed
|
||||||
let ec = self.extended_capabilities()?;
|
let ec = self.extended_capabilities()?;
|
||||||
|
@ -288,20 +286,20 @@ impl<'a> Open<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Firmware Version, YubiKey specific (?)
|
/// Firmware Version, YubiKey specific (?)
|
||||||
pub fn firmware_version(&mut self) -> Result<Vec<u8>> {
|
pub fn firmware_version(&mut self) -> Result<Vec<u8>, Error> {
|
||||||
self.opt.firmware_version()
|
self.opt.firmware_version()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------
|
// ----------
|
||||||
|
|
||||||
pub fn public_key(&mut self, key_type: KeyType) -> Result<PublicKeyMaterial> {
|
pub fn public_key(&mut self, key_type: KeyType) -> Result<PublicKeyMaterial, Error> {
|
||||||
self.opt.public_key(key_type).map_err(|e| e.into())
|
self.opt.public_key(key_type).map_err(|e| e.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------
|
// ----------
|
||||||
|
|
||||||
/// Delete all state on this OpenPGP card
|
/// Delete all state on this OpenPGP card
|
||||||
pub fn factory_reset(&mut self) -> Result<()> {
|
pub fn factory_reset(&mut self) -> Result<(), Error> {
|
||||||
self.opt.factory_reset()
|
self.opt.factory_reset()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -354,12 +352,12 @@ impl<'app, 'open> Admin<'app, 'open> {
|
||||||
impl Admin<'_, '_> {
|
impl Admin<'_, '_> {
|
||||||
pub fn set_name(&mut self, name: &str) -> Result<(), Error> {
|
pub fn set_name(&mut self, name: &str) -> Result<(), Error> {
|
||||||
if name.len() >= 40 {
|
if name.len() >= 40 {
|
||||||
return Err(anyhow!("name too long").into());
|
return Err(Error::InternalError("name too long".into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// All chars must be in ASCII7
|
// All chars must be in ASCII7
|
||||||
if name.chars().any(|c| !c.is_ascii()) {
|
if name.chars().any(|c| !c.is_ascii()) {
|
||||||
return Err(anyhow!("Invalid char in name").into());
|
return Err(Error::InternalError("Invalid char in name".into()));
|
||||||
};
|
};
|
||||||
|
|
||||||
self.oc.opt.set_name(name.as_bytes())
|
self.oc.opt.set_name(name.as_bytes())
|
||||||
|
@ -367,7 +365,7 @@ impl Admin<'_, '_> {
|
||||||
|
|
||||||
pub fn set_lang(&mut self, lang: &[Lang]) -> Result<(), Error> {
|
pub fn set_lang(&mut self, lang: &[Lang]) -> Result<(), Error> {
|
||||||
if lang.len() > 8 {
|
if lang.len() > 8 {
|
||||||
return Err(anyhow!("lang too long").into());
|
return Err(Error::InternalError("lang too long".into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.oc.opt.set_lang(lang)
|
self.oc.opt.set_lang(lang)
|
||||||
|
@ -379,7 +377,7 @@ impl Admin<'_, '_> {
|
||||||
|
|
||||||
pub fn set_url(&mut self, url: &str) -> Result<(), Error> {
|
pub fn set_url(&mut self, url: &str) -> Result<(), Error> {
|
||||||
if url.chars().any(|c| !c.is_ascii()) {
|
if url.chars().any(|c| !c.is_ascii()) {
|
||||||
return Err(anyhow!("Invalid char in url").into());
|
return Err(Error::InternalError("Invalid char in url".into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for max len
|
// Check for max len
|
||||||
|
@ -393,7 +391,7 @@ impl Admin<'_, '_> {
|
||||||
|
|
||||||
self.oc.opt.set_url(url.as_bytes())
|
self.oc.opt.set_url(url.as_bytes())
|
||||||
} else {
|
} else {
|
||||||
Err(anyhow!("URL too long").into())
|
Err(Error::InternalError("URL too long".into()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,13 +48,13 @@ impl<'a, 'app> CardDecryptor<'a, 'app> {
|
||||||
let public = eka.key().clone();
|
let public = eka.key().clone();
|
||||||
Ok(Self { ca, public })
|
Ok(Self { ca, public })
|
||||||
} else {
|
} else {
|
||||||
Err(Error::InternalError(anyhow!(
|
Err(Error::InternalError(format!(
|
||||||
"Failed to find (sub)key {} in cert",
|
"Failed to find (sub)key {} in cert",
|
||||||
fp
|
fp
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(Error::InternalError(anyhow!(
|
Err(Error::InternalError(format!(
|
||||||
"Failed to get the decryption key's Fingerprint from the card"
|
"Failed to get the decryption key's Fingerprint from the card"
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
|
|
||||||
use anyhow::Result;
|
|
||||||
use openpgp::cert::amalgamation::key::ValidErasedKeyAmalgamation;
|
use openpgp::cert::amalgamation::key::ValidErasedKeyAmalgamation;
|
||||||
use openpgp::crypto::{mpi, mpi::ProtectedMPI, mpi::MPI};
|
use openpgp::crypto::{mpi, mpi::ProtectedMPI, mpi::MPI};
|
||||||
use openpgp::packet::{
|
use openpgp::packet::{
|
||||||
|
@ -49,14 +48,15 @@ impl SequoiaKey {
|
||||||
/// Implement the `CardUploadableKey` trait that openpgp-card uses to
|
/// Implement the `CardUploadableKey` trait that openpgp-card uses to
|
||||||
/// upload (sub)keys to a card.
|
/// upload (sub)keys to a card.
|
||||||
impl CardUploadableKey for SequoiaKey {
|
impl CardUploadableKey for SequoiaKey {
|
||||||
fn private_key(&self) -> Result<PrivateKeyMaterial> {
|
fn private_key(&self) -> Result<PrivateKeyMaterial, Error> {
|
||||||
// Decrypt key with password, if set
|
// Decrypt key with password, if set
|
||||||
let key = match &self.password {
|
let key = match &self.password {
|
||||||
None => self.key.clone(),
|
None => self.key.clone(),
|
||||||
Some(pw) => self
|
Some(pw) => self
|
||||||
.key
|
.key
|
||||||
.clone()
|
.clone()
|
||||||
.decrypt_secret(&openpgp::crypto::Password::from(pw.as_str()))?,
|
.decrypt_secret(&openpgp::crypto::Password::from(pw.as_str()))
|
||||||
|
.map_err(|e| Error::InternalError(format!("sequoia decrypt failed {:?}", e)))?,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get private cryptographic material
|
// Get private cryptographic material
|
||||||
|
@ -124,8 +124,15 @@ struct SqRSA {
|
||||||
|
|
||||||
impl SqRSA {
|
impl SqRSA {
|
||||||
#[allow(clippy::many_single_char_names)]
|
#[allow(clippy::many_single_char_names)]
|
||||||
fn new(e: MPI, d: ProtectedMPI, n: MPI, p: ProtectedMPI, q: ProtectedMPI) -> Result<Self> {
|
fn new(
|
||||||
let nettle = nettle::rsa::PrivateKey::new(d.value(), p.value(), q.value(), None)?;
|
e: MPI,
|
||||||
|
d: ProtectedMPI,
|
||||||
|
n: MPI,
|
||||||
|
p: ProtectedMPI,
|
||||||
|
q: ProtectedMPI,
|
||||||
|
) -> Result<Self, Error> {
|
||||||
|
let nettle = nettle::rsa::PrivateKey::new(d.value(), p.value(), q.value(), None)
|
||||||
|
.map_err(|e| Error::InternalError(format!("nettle error {:?}", e)))?;
|
||||||
|
|
||||||
Ok(Self { e, n, p, q, nettle })
|
Ok(Self { e, n, p, q, nettle })
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,13 +46,13 @@ impl<'a, 'app> CardSigner<'a, 'app> {
|
||||||
let key = eka.key().clone();
|
let key = eka.key().clone();
|
||||||
Ok(Self::with_pubkey(ca, key))
|
Ok(Self::with_pubkey(ca, key))
|
||||||
} else {
|
} else {
|
||||||
Err(Error::InternalError(anyhow!(
|
Err(Error::InternalError(format!(
|
||||||
"Failed to find (sub)key {} in cert",
|
"Failed to find (sub)key {} in cert",
|
||||||
fp
|
fp
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(Error::InternalError(anyhow!(
|
Err(Error::InternalError(format!(
|
||||||
"Failed to get the signing key's Fingerprint from the card"
|
"Failed to get the signing key's Fingerprint from the card"
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
|
@ -177,12 +177,14 @@ pub fn public_key_material_to_key(
|
||||||
pkm: &PublicKeyMaterial,
|
pkm: &PublicKeyMaterial,
|
||||||
key_type: KeyType,
|
key_type: KeyType,
|
||||||
time: KeyGenerationTime,
|
time: KeyGenerationTime,
|
||||||
) -> Result<PublicKey> {
|
) -> Result<PublicKey, Error> {
|
||||||
let time = Timestamp::from(time.get()).into();
|
let time = Timestamp::from(time.get()).into();
|
||||||
|
|
||||||
match pkm {
|
match pkm {
|
||||||
PublicKeyMaterial::R(rsa) => {
|
PublicKeyMaterial::R(rsa) => {
|
||||||
let k4 = Key4::import_public_rsa(rsa.v(), rsa.n(), Some(time))?;
|
let k4 = Key4::import_public_rsa(rsa.v(), rsa.n(), Some(time)).map_err(|e| {
|
||||||
|
Error::InternalError(format!("sequoia Key4::import_public_rsa failed: {:?}", e))
|
||||||
|
})?;
|
||||||
|
|
||||||
Ok(k4.into())
|
Ok(k4.into())
|
||||||
}
|
}
|
||||||
|
@ -202,7 +204,13 @@ pub fn public_key_material_to_key(
|
||||||
KeyType::Authentication | KeyType::Signing => {
|
KeyType::Authentication | KeyType::Signing => {
|
||||||
if algo_ecc.curve() == Curve::Ed25519 {
|
if algo_ecc.curve() == Curve::Ed25519 {
|
||||||
// EdDSA
|
// EdDSA
|
||||||
let k4 = Key4::import_public_ed25519(ecc.data(), time)?;
|
let k4 =
|
||||||
|
Key4::import_public_ed25519(ecc.data(), time).map_err(|e| {
|
||||||
|
Error::InternalError(format!(
|
||||||
|
"sequoia Key4::import_public_ed25519 failed: {:?}",
|
||||||
|
e
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
|
||||||
Ok(Key::from(k4))
|
Ok(Key::from(k4))
|
||||||
} else {
|
} else {
|
||||||
|
@ -214,7 +222,13 @@ pub fn public_key_material_to_key(
|
||||||
curve,
|
curve,
|
||||||
q: mpi::MPI::new(ecc.data()),
|
q: mpi::MPI::new(ecc.data()),
|
||||||
},
|
},
|
||||||
)?;
|
)
|
||||||
|
.map_err(|e| {
|
||||||
|
Error::InternalError(format!(
|
||||||
|
"sequoia Key4::new for ECDSA failed: {:?}",
|
||||||
|
e
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
|
||||||
Ok(k4.into())
|
Ok(k4.into())
|
||||||
}
|
}
|
||||||
|
@ -225,7 +239,13 @@ pub fn public_key_material_to_key(
|
||||||
// ok when a cert already exists
|
// ok when a cert already exists
|
||||||
|
|
||||||
// EdDSA
|
// EdDSA
|
||||||
let k4 = Key4::import_public_cv25519(ecc.data(), None, None, time)?;
|
let k4 = Key4::import_public_cv25519(ecc.data(), None, None, time)
|
||||||
|
.map_err(|e| {
|
||||||
|
Error::InternalError(format!(
|
||||||
|
"sequoia Key4::import_public_cv25519 failed: {:?}",
|
||||||
|
e
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
|
||||||
Ok(k4.into())
|
Ok(k4.into())
|
||||||
} else {
|
} else {
|
||||||
|
@ -242,7 +262,13 @@ pub fn public_key_material_to_key(
|
||||||
hash: Default::default(),
|
hash: Default::default(),
|
||||||
sym: Default::default(),
|
sym: Default::default(),
|
||||||
},
|
},
|
||||||
)?;
|
)
|
||||||
|
.map_err(|e| {
|
||||||
|
Error::InternalError(format!(
|
||||||
|
"sequoia Key4::new for ECDH failed: {:?}",
|
||||||
|
e
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
|
||||||
Ok(k4.into())
|
Ok(k4.into())
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,7 +86,7 @@ impl AlgoSimple {
|
||||||
key_type: KeyType,
|
key_type: KeyType,
|
||||||
ard: &ApplicationRelatedData,
|
ard: &ApplicationRelatedData,
|
||||||
algo_info: Option<AlgoInfo>,
|
algo_info: Option<AlgoInfo>,
|
||||||
) -> Result<Algo> {
|
) -> Result<Algo, crate::Error> {
|
||||||
let algo = match self {
|
let algo = match self {
|
||||||
Self::RSA1k => Algo::Rsa(keys::determine_rsa_attrs(1024, key_type, ard, algo_info)?),
|
Self::RSA1k => Algo::Rsa(keys::determine_rsa_attrs(1024, key_type, ard, algo_info)?),
|
||||||
Self::RSA2k => Algo::Rsa(keys::determine_rsa_attrs(2048, key_type, ard, algo_info)?),
|
Self::RSA2k => Algo::Rsa(keys::determine_rsa_attrs(2048, key_type, ard, algo_info)?),
|
||||||
|
@ -189,7 +189,9 @@ impl Algo {
|
||||||
match self {
|
match self {
|
||||||
Algo::Rsa(rsa) => Self::rsa_algo_attrs(rsa),
|
Algo::Rsa(rsa) => Self::rsa_algo_attrs(rsa),
|
||||||
Algo::Ecc(ecc) => Self::ecc_algo_attrs(ecc.oid(), ecc.ecc_type()),
|
Algo::Ecc(ecc) => Self::ecc_algo_attrs(ecc.oid(), ecc.ecc_type()),
|
||||||
_ => Err(anyhow!("Unexpected Algo {:?}", self).into()),
|
_ => Err(Error::UnsupportedAlgo(
|
||||||
|
format!("Unexpected Algo {:?}", self).into(),
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -327,7 +329,7 @@ impl Curve {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<&[u8]> for Curve {
|
impl TryFrom<&[u8]> for Curve {
|
||||||
type Error = anyhow::Error;
|
type Error = crate::Error;
|
||||||
|
|
||||||
fn try_from(oid: &[u8]) -> Result<Self, Self::Error> {
|
fn try_from(oid: &[u8]) -> Result<Self, Self::Error> {
|
||||||
use Curve::*;
|
use Curve::*;
|
||||||
|
@ -349,7 +351,7 @@ impl TryFrom<&[u8]> for Curve {
|
||||||
[0x2b, 0x65, 0x71] => Ed448,
|
[0x2b, 0x65, 0x71] => Ed448,
|
||||||
[0x2b, 0x65, 0x6f] => X448,
|
[0x2b, 0x65, 0x6f] => X448,
|
||||||
|
|
||||||
_ => return Err(anyhow!("Unknown curve OID {:?}", oid)),
|
_ => return Err(Error::ParseError(format!("Unknown curve OID {:?}", oid))),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(curve)
|
Ok(curve)
|
||||||
|
|
|
@ -153,9 +153,7 @@ where
|
||||||
let cla = if last { 0x00 } else { 0x10 };
|
let cla = if last { 0x00 } else { 0x10 };
|
||||||
let partial = Command::new(cla, cmd.ins(), cmd.p1(), cmd.p2(), d.to_vec());
|
let partial = Command::new(cla, cmd.ins(), cmd.p1(), cmd.p2(), d.to_vec());
|
||||||
|
|
||||||
let serialized = partial
|
let serialized = partial.serialize(ext_len, expect_response)?;
|
||||||
.serialize(ext_len, expect_response)
|
|
||||||
.map_err(Error::InternalError)?;
|
|
||||||
|
|
||||||
log::debug!(" -> chained APDU command: {:x?}", &serialized);
|
log::debug!(" -> chained APDU command: {:x?}", &serialized);
|
||||||
|
|
||||||
|
|
|
@ -69,7 +69,11 @@ impl Command {
|
||||||
/// Serialize a Command (for sending to a card).
|
/// Serialize a Command (for sending to a card).
|
||||||
///
|
///
|
||||||
/// See OpenPGP card spec, chapter 7 (pg 47)
|
/// See OpenPGP card spec, chapter 7 (pg 47)
|
||||||
pub(crate) fn serialize(&self, ext_len: bool, expect_response: Expect) -> Result<Vec<u8>> {
|
pub(crate) fn serialize(
|
||||||
|
&self,
|
||||||
|
ext_len: bool,
|
||||||
|
expect_response: Expect,
|
||||||
|
) -> Result<Vec<u8>, crate::Error> {
|
||||||
// FIXME? (from scd/apdu.c):
|
// FIXME? (from scd/apdu.c):
|
||||||
// T=0 does not allow the use of Lc together with Le;
|
// T=0 does not allow the use of Lc together with Le;
|
||||||
// thus disable Le in this case.
|
// thus disable Le in this case.
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
|
|
||||||
//! OpenPGP card data objects (DO)
|
//! OpenPGP card data objects (DO)
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
|
@ -41,7 +40,7 @@ impl ApplicationRelatedData {
|
||||||
if let Some(aid) = aid {
|
if let Some(aid) = aid {
|
||||||
Ok(ApplicationIdentifier::try_from(&aid.serialize()[..])?)
|
Ok(ApplicationIdentifier::try_from(&aid.serialize()[..])?)
|
||||||
} else {
|
} else {
|
||||||
Err(anyhow!("Couldn't get Application ID.").into())
|
Err(Error::NotFound("Couldn't get Application ID.".to_string()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,13 +53,15 @@ impl ApplicationRelatedData {
|
||||||
log::debug!("Historical bytes: {:x?}", hist);
|
log::debug!("Historical bytes: {:x?}", hist);
|
||||||
(hist.serialize().as_slice()).try_into()
|
(hist.serialize().as_slice()).try_into()
|
||||||
} else {
|
} else {
|
||||||
Err(anyhow!("Failed to get historical bytes.").into())
|
Err(Error::NotFound(
|
||||||
|
"Failed to get historical bytes.".to_string(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get extended length information (ISO 7816-4), which
|
/// Get extended length information (ISO 7816-4), which
|
||||||
/// contains maximum number of bytes for command and response.
|
/// contains maximum number of bytes for command and response.
|
||||||
pub fn extended_length_information(&self) -> Result<Option<ExtendedLengthInfo>> {
|
pub fn extended_length_information(&self) -> Result<Option<ExtendedLengthInfo>, Error> {
|
||||||
// get from cached "application related data"
|
// get from cached "application related data"
|
||||||
let eli = self.0.find(&[0x7f, 0x66].into());
|
let eli = self.0.find(&[0x7f, 0x66].into());
|
||||||
|
|
||||||
|
@ -100,27 +101,29 @@ impl ApplicationRelatedData {
|
||||||
version,
|
version,
|
||||||
))?)
|
))?)
|
||||||
} else {
|
} else {
|
||||||
Err(anyhow!("Failed to get extended capabilities.").into())
|
Err(Error::NotFound(
|
||||||
|
"Failed to get extended capabilities.".to_string(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get algorithm attributes (for each key type)
|
/// Get algorithm attributes (for each key type)
|
||||||
pub fn algorithm_attributes(&self, key_type: KeyType) -> Result<Algo> {
|
pub fn algorithm_attributes(&self, key_type: KeyType) -> Result<Algo, Error> {
|
||||||
// get from cached "application related data"
|
// get from cached "application related data"
|
||||||
let aa = self.0.find(&[key_type.algorithm_tag()].into());
|
let aa = self.0.find(&[key_type.algorithm_tag()].into());
|
||||||
|
|
||||||
if let Some(aa) = aa {
|
if let Some(aa) = aa {
|
||||||
Algo::try_from(&aa.serialize()[..])
|
Algo::try_from(&aa.serialize()[..])
|
||||||
} else {
|
} else {
|
||||||
Err(anyhow!(
|
Err(Error::NotFound(format!(
|
||||||
"Failed to get algorithm attributes for {:?}.",
|
"Failed to get algorithm attributes for {:?}.",
|
||||||
key_type
|
key_type
|
||||||
))
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get PW status Bytes
|
/// Get PW status Bytes
|
||||||
pub fn pw_status_bytes(&self) -> Result<PWStatusBytes> {
|
pub fn pw_status_bytes(&self) -> Result<PWStatusBytes, Error> {
|
||||||
// get from cached "application related data"
|
// get from cached "application related data"
|
||||||
let psb = self.0.find(&[0xc4].into());
|
let psb = self.0.find(&[0xc4].into());
|
||||||
|
|
||||||
|
@ -131,7 +134,9 @@ impl ApplicationRelatedData {
|
||||||
|
|
||||||
Ok(pws)
|
Ok(pws)
|
||||||
} else {
|
} else {
|
||||||
Err(anyhow!("Failed to get PW status Bytes."))
|
Err(Error::NotFound(
|
||||||
|
"Failed to get PW status Bytes.".to_string(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,12 +153,12 @@ impl ApplicationRelatedData {
|
||||||
|
|
||||||
Ok(fp)
|
Ok(fp)
|
||||||
} else {
|
} else {
|
||||||
Err(anyhow!("Failed to get fingerprints.").into())
|
Err(Error::NotFound("Failed to get fingerprints.".into()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generation dates/times of key pairs
|
/// Generation dates/times of key pairs
|
||||||
pub fn key_generation_times(&self) -> Result<KeySet<KeyGenerationTime>, Error> {
|
pub fn key_generation_times(&self) -> Result<KeySet<KeyGenerationTime>, crate::Error> {
|
||||||
let kg = self.0.find(&[0xcd].into());
|
let kg = self.0.find(&[0xcd].into());
|
||||||
|
|
||||||
if let Some(kg) = kg {
|
if let Some(kg) = kg {
|
||||||
|
@ -163,7 +168,9 @@ impl ApplicationRelatedData {
|
||||||
|
|
||||||
Ok(kg)
|
Ok(kg)
|
||||||
} else {
|
} else {
|
||||||
Err(anyhow!("Failed to get key generation times.").into())
|
Err(Error::NotFound(format!(
|
||||||
|
"Failed to get key generation times."
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -446,11 +453,14 @@ impl Fingerprint {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper fn for nom parsing
|
/// Helper fn for nom parsing
|
||||||
pub(crate) fn complete<O>(result: nom::IResult<&[u8], O>) -> Result<O, anyhow::Error> {
|
pub(crate) fn complete<O>(result: nom::IResult<&[u8], O>) -> Result<O, Error> {
|
||||||
let (rem, output) = result.map_err(|err| anyhow!("Parsing failed: {:?}", err))?;
|
let (rem, output) = result.map_err(|_err| Error::ParseError(format!("Parsing failed")))?;
|
||||||
if rem.is_empty() {
|
if rem.is_empty() {
|
||||||
Ok(output)
|
Ok(output)
|
||||||
} else {
|
} else {
|
||||||
Err(anyhow!("Parsing incomplete -- trailing data: {:x?}", rem))
|
Err(Error::ParseError(format!(
|
||||||
|
"Parsing incomplete, trailing data: {:x?}",
|
||||||
|
rem
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -141,9 +141,9 @@ pub(crate) fn parse(input: &[u8]) -> nom::IResult<&[u8], Algo> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<&[u8]> for Algo {
|
impl TryFrom<&[u8]> for Algo {
|
||||||
type Error = anyhow::Error;
|
type Error = crate::Error;
|
||||||
|
|
||||||
fn try_from(data: &[u8]) -> Result<Self> {
|
fn try_from(data: &[u8]) -> Result<Self, crate::Error> {
|
||||||
complete(parse(data))
|
complete(parse(data))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,9 +87,9 @@ pub(self) fn parse(input: &[u8]) -> nom::IResult<&[u8], Vec<(KeyType, Algo)>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<&[u8]> for AlgoInfo {
|
impl TryFrom<&[u8]> for AlgoInfo {
|
||||||
type Error = anyhow::Error;
|
type Error = crate::Error;
|
||||||
|
|
||||||
fn try_from(input: &[u8]) -> Result<Self> {
|
fn try_from(input: &[u8]) -> Result<Self, Self::Error> {
|
||||||
Ok(AlgoInfo(complete(parse(input))?))
|
Ok(AlgoInfo(complete(parse(input))?))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,9 +31,9 @@ fn parse(input: &[u8]) -> nom::IResult<&[u8], ApplicationIdentifier> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<&[u8]> for ApplicationIdentifier {
|
impl TryFrom<&[u8]> for ApplicationIdentifier {
|
||||||
type Error = anyhow::Error;
|
type Error = crate::Error;
|
||||||
|
|
||||||
fn try_from(data: &[u8]) -> Result<Self> {
|
fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
|
||||||
complete(parse(data))
|
complete(parse(data))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,9 +25,9 @@ impl CardholderRelatedData {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<&[u8]> for CardholderRelatedData {
|
impl TryFrom<&[u8]> for CardholderRelatedData {
|
||||||
type Error = anyhow::Error;
|
type Error = crate::Error;
|
||||||
|
|
||||||
fn try_from(data: &[u8]) -> Result<Self> {
|
fn try_from(data: &[u8]) -> Result<Self, crate::Error> {
|
||||||
let value = Value::from(data, true)?;
|
let value = Value::from(data, true)?;
|
||||||
let tlv = Tlv::new([0x65], value);
|
let tlv = Tlv::new([0x65], value);
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
|
|
||||||
//! 4.4.3.7 Extended Capabilities
|
//! 4.4.3.7 Extended Capabilities
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
use crate::card_do::ExtendedCapabilities;
|
use crate::card_do::ExtendedCapabilities;
|
||||||
|
@ -80,15 +79,17 @@ impl TryFrom<(&[u8], u16)> for ExtendedCapabilities {
|
||||||
let i9 = input[9];
|
let i9 = input[9];
|
||||||
|
|
||||||
if i8 > 1 {
|
if i8 > 1 {
|
||||||
return Err(
|
return Err(Error::ParseError(
|
||||||
anyhow!("Illegal value '{}' for pin_block_2_format_support", i8).into(),
|
format!("Illegal value '{}' for pin_block_2_format_support", i8).into(),
|
||||||
);
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
pin_block_2_format_support = Some(i8 != 0);
|
pin_block_2_format_support = Some(i8 != 0);
|
||||||
|
|
||||||
if i9 > 1 {
|
if i9 > 1 {
|
||||||
return Err(anyhow!("Illegal value '{}' for mse_command_support", i9).into());
|
return Err(Error::ParseError(
|
||||||
|
format!("Illegal value '{}' for mse_command_support", i9).into(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
mse_command_support = Some(i9 != 0);
|
mse_command_support = Some(i9 != 0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ impl ExtendedLengthInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<&[u8]> for ExtendedLengthInfo {
|
impl TryFrom<&[u8]> for ExtendedLengthInfo {
|
||||||
type Error = anyhow::Error;
|
type Error = crate::Error;
|
||||||
|
|
||||||
fn try_from(input: &[u8]) -> Result<Self, Self::Error> {
|
fn try_from(input: &[u8]) -> Result<Self, Self::Error> {
|
||||||
let eli = complete(parse(input))?;
|
let eli = complete(parse(input))?;
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
|
|
||||||
//! Fingerprint for a single key slot
|
//! Fingerprint for a single key slot
|
||||||
|
|
||||||
use anyhow::anyhow;
|
|
||||||
use nom::{bytes::complete as bytes, combinator, sequence};
|
use nom::{bytes::complete as bytes, combinator, sequence};
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
|
@ -28,7 +27,9 @@ impl TryFrom<&[u8]> for Fingerprint {
|
||||||
let array: [u8; 20] = input.try_into().unwrap();
|
let array: [u8; 20] = input.try_into().unwrap();
|
||||||
Ok(array.into())
|
Ok(array.into())
|
||||||
} else {
|
} else {
|
||||||
Err(anyhow!("Unexpected fingerprint length {}", input.len()).into())
|
Err(Error::ParseError(
|
||||||
|
format!("Unexpected fingerprint length {}", input.len()).into(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,8 +89,7 @@ impl TryFrom<&[u8]> for KeySet<Fingerprint> {
|
||||||
// been completely consumed.
|
// been completely consumed.
|
||||||
self::fingerprints(input)
|
self::fingerprints(input)
|
||||||
.map(|res| res.1)
|
.map(|res| res.1)
|
||||||
.map_err(|err| anyhow!("Parsing failed: {:?}", err))
|
.map_err(|_err| Error::ParseError("Parsing failed".into()))
|
||||||
.map_err(Error::InternalError)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
|
|
||||||
use crate::card_do::{CardCapabilities, CardServiceData, HistoricalBytes};
|
use crate::card_do::{CardCapabilities, CardServiceData, HistoricalBytes};
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
use anyhow::{anyhow, Result};
|
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
impl CardCapabilities {
|
impl CardCapabilities {
|
||||||
|
@ -77,7 +76,7 @@ impl HistoricalBytes {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<&[u8]> for HistoricalBytes {
|
impl TryFrom<&[u8]> for HistoricalBytes {
|
||||||
type Error = Error;
|
type Error = crate::Error;
|
||||||
|
|
||||||
fn try_from(mut data: &[u8]) -> Result<Self, Self::Error> {
|
fn try_from(mut data: &[u8]) -> Result<Self, Self::Error> {
|
||||||
// workaround-hack for "ledger" with zero-padded historical bytes
|
// workaround-hack for "ledger" with zero-padded historical bytes
|
||||||
|
@ -95,18 +94,18 @@ impl TryFrom<&[u8]> for HistoricalBytes {
|
||||||
if len < 4 {
|
if len < 4 {
|
||||||
// historical bytes cannot be this short
|
// historical bytes cannot be this short
|
||||||
|
|
||||||
return Err(anyhow!(format!(
|
return Err(Error::ParseError(
|
||||||
"Historical bytes too short ({} bytes), must be >= 4",
|
format!("Historical bytes too short ({} bytes), must be >= 4", len).into(),
|
||||||
len
|
));
|
||||||
))
|
|
||||||
.into());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if data[0] != 0 {
|
if data[0] != 0 {
|
||||||
// The OpenPGP application assumes a category indicator byte
|
// The OpenPGP application assumes a category indicator byte
|
||||||
// set to '00' (o-card 3.4.1, pg 44)
|
// set to '00' (o-card 3.4.1, pg 44)
|
||||||
|
|
||||||
return Err(anyhow!("Unexpected category indicator in historical bytes").into());
|
return Err(Error::ParseError(
|
||||||
|
"Unexpected category indicator in historical bytes".into(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// category indicator byte
|
// category indicator byte
|
||||||
|
@ -127,14 +126,15 @@ impl TryFrom<&[u8]> for HistoricalBytes {
|
||||||
// (1 byte for the tl, plus `l` bytes of data for this ctlv)
|
// (1 byte for the tl, plus `l` bytes of data for this ctlv)
|
||||||
// (e.g. len = 4 -> tl + 3byte data)
|
// (e.g. len = 4 -> tl + 3byte data)
|
||||||
if ctlv.len() < (1 + l as usize) {
|
if ctlv.len() < (1 + l as usize) {
|
||||||
return Err(anyhow!(
|
return Err(Error::ParseError(
|
||||||
"Illegal length value in Historical Bytes TL {} len {} \
|
format!(
|
||||||
l {}",
|
"Illegal length value in Historical Bytes TL {} len {} l {}",
|
||||||
ctlv[0],
|
ctlv[0],
|
||||||
ctlv.len(),
|
ctlv.len(),
|
||||||
l
|
l
|
||||||
)
|
)
|
||||||
.into());
|
.into(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
match (t, l) {
|
match (t, l) {
|
||||||
|
@ -177,7 +177,9 @@ impl TryFrom<&[u8]> for HistoricalBytes {
|
||||||
5
|
5
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Err(anyhow!("unexpected status indicator in historical bytes").into());
|
return Err(Error::ParseError(
|
||||||
|
"unexpected status indicator in historical bytes".into(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -3,12 +3,10 @@
|
||||||
|
|
||||||
//! Generation date/time of key pair (see spec pg. 24)
|
//! Generation date/time of key pair (see spec pg. 24)
|
||||||
|
|
||||||
use anyhow::anyhow;
|
|
||||||
use chrono::{DateTime, NaiveDateTime, Utc};
|
|
||||||
use nom::{combinator, number::complete as number, sequence};
|
|
||||||
|
|
||||||
use crate::card_do::{KeyGenerationTime, KeySet};
|
use crate::card_do::{KeyGenerationTime, KeySet};
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
|
use chrono::{DateTime, NaiveDateTime, Utc};
|
||||||
|
use nom::{combinator, number::complete as number, sequence};
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
impl From<KeyGenerationTime> for DateTime<Utc> {
|
impl From<KeyGenerationTime> for DateTime<Utc> {
|
||||||
|
@ -69,8 +67,7 @@ impl TryFrom<&[u8]> for KeySet<KeyGenerationTime> {
|
||||||
// hasn't been completely consumed.
|
// hasn't been completely consumed.
|
||||||
self::key_generation_set(input)
|
self::key_generation_set(input)
|
||||||
.map(|res| res.1)
|
.map(|res| res.1)
|
||||||
.map_err(|err| anyhow!("Parsing failed: {:?}", err))
|
.map_err(|_err| Error::ParseError(format!("Parsing failed")))
|
||||||
.map_err(Error::InternalError)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,6 @@
|
||||||
|
|
||||||
//! PW status Bytes (see spec page 23)
|
//! PW status Bytes (see spec page 23)
|
||||||
|
|
||||||
use anyhow::anyhow;
|
|
||||||
|
|
||||||
use crate::card_do::PWStatusBytes;
|
use crate::card_do::PWStatusBytes;
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
@ -41,7 +39,7 @@ impl PWStatusBytes {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<&[u8]> for PWStatusBytes {
|
impl TryFrom<&[u8]> for PWStatusBytes {
|
||||||
type Error = Error;
|
type Error = crate::Error;
|
||||||
|
|
||||||
fn try_from(input: &[u8]) -> Result<Self, Self::Error> {
|
fn try_from(input: &[u8]) -> Result<Self, Self::Error> {
|
||||||
if input.len() == 7 {
|
if input.len() == 7 {
|
||||||
|
@ -67,7 +65,7 @@ impl TryFrom<&[u8]> for PWStatusBytes {
|
||||||
err_count_pw3,
|
err_count_pw3,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Err(Error::InternalError(anyhow!(
|
Err(Error::ParseError(format!(
|
||||||
"Unexpected length of PW Status Bytes: {}",
|
"Unexpected length of PW Status Bytes: {}",
|
||||||
input.len()
|
input.len()
|
||||||
)))
|
)))
|
||||||
|
|
|
@ -61,7 +61,7 @@ pub enum Cryptogram<'a> {
|
||||||
/// to an OpenPGP card
|
/// to an OpenPGP card
|
||||||
pub trait CardUploadableKey {
|
pub trait CardUploadableKey {
|
||||||
/// private key data
|
/// private key data
|
||||||
fn private_key(&self) -> Result<PrivateKeyMaterial>;
|
fn private_key(&self) -> Result<PrivateKeyMaterial, crate::Error>;
|
||||||
|
|
||||||
/// timestamp of (sub)key creation
|
/// timestamp of (sub)key creation
|
||||||
fn timestamp(&self) -> KeyGenerationTime;
|
fn timestamp(&self) -> KeyGenerationTime;
|
||||||
|
|
|
@ -26,8 +26,18 @@ pub enum Error {
|
||||||
#[error("Unexpected response length: {0}")]
|
#[error("Unexpected response length: {0}")]
|
||||||
ResponseLength(usize),
|
ResponseLength(usize),
|
||||||
|
|
||||||
|
#[error("Data not found: {0}")]
|
||||||
|
NotFound(String),
|
||||||
|
|
||||||
|
#[error("Couldn't parse data: {0}")]
|
||||||
|
ParseError(String),
|
||||||
|
|
||||||
|
#[error("Unsupported algorithm: {0}")]
|
||||||
|
UnsupportedAlgo(String),
|
||||||
|
|
||||||
|
// FIXME: placeholder, remove again later?
|
||||||
#[error("Internal error: {0}")]
|
#[error("Internal error: {0}")]
|
||||||
InternalError(anyhow::Error),
|
InternalError(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<StatusBytes> for Error {
|
impl From<StatusBytes> for Error {
|
||||||
|
@ -36,12 +46,6 @@ impl From<StatusBytes> for Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<anyhow::Error> for Error {
|
|
||||||
fn from(ae: anyhow::Error) -> Self {
|
|
||||||
Error::InternalError(ae)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// OpenPGP card "Status Bytes" (ok statuses and errors)
|
/// OpenPGP card "Status Bytes" (ok statuses and errors)
|
||||||
#[derive(thiserror::Error, Debug, PartialEq, Copy, Clone)]
|
#[derive(thiserror::Error, Debug, PartialEq, Copy, Clone)]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
|
|
||||||
//! Generate and import keys
|
//! Generate and import keys
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
|
@ -77,7 +76,7 @@ pub(crate) fn gen_key_with_metadata(
|
||||||
// Store creation timestamp (unix time format, limited to u32)
|
// Store creation timestamp (unix time format, limited to u32)
|
||||||
let ts = time
|
let ts = time
|
||||||
.duration_since(UNIX_EPOCH)
|
.duration_since(UNIX_EPOCH)
|
||||||
.map_err(|e| Error::InternalError(anyhow!(e)))?
|
.map_err(|e| Error::InternalError(format!("This should never happen {}", e)))?
|
||||||
.as_secs() as u32;
|
.as_secs() as u32;
|
||||||
|
|
||||||
let ts = ts.into();
|
let ts = ts.into();
|
||||||
|
@ -92,7 +91,7 @@ pub(crate) fn gen_key_with_metadata(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Transform a public key Tlv from the card into PublicKeyMaterial
|
/// Transform a public key Tlv from the card into PublicKeyMaterial
|
||||||
fn tlv_to_pubkey(tlv: &Tlv, algo: &Algo) -> Result<PublicKeyMaterial> {
|
fn tlv_to_pubkey(tlv: &Tlv, algo: &Algo) -> Result<PublicKeyMaterial, crate::Error> {
|
||||||
let n = tlv.find(&[0x81].into());
|
let n = tlv.find(&[0x81].into());
|
||||||
let v = tlv.find(&[0x82].into());
|
let v = tlv.find(&[0x82].into());
|
||||||
|
|
||||||
|
@ -111,10 +110,10 @@ fn tlv_to_pubkey(tlv: &Tlv, algo: &Algo) -> Result<PublicKeyMaterial> {
|
||||||
Ok(PublicKeyMaterial::E(ecc))
|
Ok(PublicKeyMaterial::E(ecc))
|
||||||
}
|
}
|
||||||
|
|
||||||
(_, _, _) => Err(anyhow!(
|
(_, _, _) => Err(Error::UnsupportedAlgo(format!(
|
||||||
"Unexpected public key material from card {:?}",
|
"Unexpected public key material from card {:?}",
|
||||||
tlv
|
tlv
|
||||||
)),
|
))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -229,7 +228,7 @@ pub(crate) fn determine_rsa_attrs(
|
||||||
key_type: KeyType,
|
key_type: KeyType,
|
||||||
ard: &ApplicationRelatedData,
|
ard: &ApplicationRelatedData,
|
||||||
algo_info: Option<AlgoInfo>,
|
algo_info: Option<AlgoInfo>,
|
||||||
) -> Result<RsaAttrs> {
|
) -> Result<RsaAttrs, crate::Error> {
|
||||||
// Figure out suitable RSA algorithm parameters:
|
// Figure out suitable RSA algorithm parameters:
|
||||||
|
|
||||||
// Does the card offer a list of algorithms?
|
// Does the card offer a list of algorithms?
|
||||||
|
@ -273,13 +272,16 @@ pub(crate) fn determine_ecc_attrs(
|
||||||
ecc_type: EccType,
|
ecc_type: EccType,
|
||||||
key_type: KeyType,
|
key_type: KeyType,
|
||||||
algo_info: Option<AlgoInfo>,
|
algo_info: Option<AlgoInfo>,
|
||||||
) -> Result<EccAttrs> {
|
) -> Result<EccAttrs, crate::Error> {
|
||||||
// If we have an algo_info, refuse upload if oid is not listed
|
// If we have an algo_info, refuse upload if oid is not listed
|
||||||
if let Some(algo_info) = algo_info {
|
if let Some(algo_info) = algo_info {
|
||||||
let algos = check_card_algo_ecc(algo_info, key_type, oid);
|
let algos = check_card_algo_ecc(algo_info, key_type, oid);
|
||||||
if algos.is_empty() {
|
if algos.is_empty() {
|
||||||
// If oid is not in algo_info, return error.
|
// If oid is not in algo_info, return error.
|
||||||
return Err(anyhow!("Oid {:?} unsupported according to algo_info", oid));
|
return Err(Error::UnsupportedAlgo(format!(
|
||||||
|
"Oid {:?} unsupported according to algo_info",
|
||||||
|
oid
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: Looking up ecc_type in the card's "Algorithm Information"
|
// Note: Looking up ecc_type in the card's "Algorithm Information"
|
||||||
|
@ -332,7 +334,9 @@ fn card_algo_rsa(algo_info: AlgoInfo, key_type: KeyType, rsa_bits: u16) -> Resul
|
||||||
Ok((**algo.last().unwrap()).clone())
|
Ok((**algo.last().unwrap()).clone())
|
||||||
} else {
|
} else {
|
||||||
// RSA with this bit length is not in algo_info
|
// RSA with this bit length is not in algo_info
|
||||||
return Err(anyhow!("RSA {} unsupported according to algo_info", rsa_bits).into());
|
return Err(Error::UnsupportedAlgo(
|
||||||
|
format!("RSA {} unsupported according to algo_info", rsa_bits).into(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -518,7 +522,7 @@ fn control_reference_template(key_type: KeyType) -> Result<Tlv, Error> {
|
||||||
KeyType::Decryption => 0xB8,
|
KeyType::Decryption => 0xB8,
|
||||||
KeyType::Signing => 0xB6,
|
KeyType::Signing => 0xB6,
|
||||||
KeyType::Authentication => 0xA4,
|
KeyType::Authentication => 0xA4,
|
||||||
_ => return Err(Error::InternalError(anyhow!("Unexpected KeyType"))),
|
_ => return Err(Error::InternalError("Unexpected KeyType".to_string())),
|
||||||
};
|
};
|
||||||
Ok(Tlv::new([tag], Value::S(vec![])))
|
Ok(Tlv::new([tag], Value::S(vec![])))
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,10 +94,10 @@ pub trait CardTransaction {
|
||||||
fn feature_pinpad_modify(&self) -> bool;
|
fn feature_pinpad_modify(&self) -> bool;
|
||||||
|
|
||||||
/// Verify the PIN `id` via the reader pinpad
|
/// Verify the PIN `id` via the reader pinpad
|
||||||
fn pinpad_verify(&mut self, id: u8) -> Result<Vec<u8>>;
|
fn pinpad_verify(&mut self, pin: PinType) -> Result<Vec<u8>, Error>;
|
||||||
|
|
||||||
/// Modify the PIN `id` via the reader pinpad
|
/// Modify the PIN `id` via the reader pinpad
|
||||||
fn pinpad_modify(&mut self, id: u8) -> Result<Vec<u8>>;
|
fn pinpad_modify(&mut self, pin: PinType) -> Result<Vec<u8>, Error>;
|
||||||
|
|
||||||
/// Select the OpenPGP card application
|
/// Select the OpenPGP card application
|
||||||
fn select(&mut self) -> Result<Vec<u8>, Error> {
|
fn select(&mut self) -> Result<Vec<u8>, Error> {
|
||||||
|
@ -110,7 +110,7 @@ pub trait CardTransaction {
|
||||||
/// (This data should probably be cached in a higher layer. Some parts of
|
/// (This data should probably be cached in a higher layer. Some parts of
|
||||||
/// it are needed regularly, and it does not usually change during
|
/// it are needed regularly, and it does not usually change during
|
||||||
/// normal use of a card.)
|
/// normal use of a card.)
|
||||||
fn application_related_data(&mut self) -> Result<ApplicationRelatedData> {
|
fn application_related_data(&mut self) -> Result<ApplicationRelatedData, Error> {
|
||||||
let ad = commands::application_related_data();
|
let ad = commands::application_related_data();
|
||||||
let resp = apdu::send_command(self, ad, true)?;
|
let resp = apdu::send_command(self, ad, true)?;
|
||||||
let value = Value::from(resp.data()?, true)?;
|
let value = Value::from(resp.data()?, true)?;
|
||||||
|
@ -128,7 +128,7 @@ pub trait CardTransaction {
|
||||||
/// This fn initializes the CardCaps by requesting
|
/// This fn initializes the CardCaps by requesting
|
||||||
/// application_related_data from the card, and setting the
|
/// application_related_data from the card, and setting the
|
||||||
/// capabilities accordingly.
|
/// capabilities accordingly.
|
||||||
fn initialize(&mut self) -> Result<()> {
|
fn initialize(&mut self) -> Result<(), Error> {
|
||||||
let ard = self.application_related_data()?;
|
let ard = self.application_related_data()?;
|
||||||
|
|
||||||
// Determine chaining/extended length support from card
|
// Determine chaining/extended length support from card
|
||||||
|
@ -242,6 +242,23 @@ impl CardCaps {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||||
|
pub enum PinType {
|
||||||
|
Sign,
|
||||||
|
User,
|
||||||
|
Admin,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PinType {
|
||||||
|
pub fn id(&self) -> u8 {
|
||||||
|
match self {
|
||||||
|
PinType::Sign => 0x81,
|
||||||
|
PinType::User => 0x82,
|
||||||
|
PinType::Admin => 0x83,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Identify a Key slot on an OpenPGP card
|
/// Identify a Key slot on an OpenPGP card
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: 2021-2022 Heiko Schaefer <heiko@schaefer.name>
|
// SPDX-FileCopyrightText: 2021-2022 Heiko Schaefer <heiko@schaefer.name>
|
||||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
|
||||||
use std::convert::{TryFrom, TryInto};
|
use std::convert::{TryFrom, TryInto};
|
||||||
|
|
||||||
use crate::algorithm::{Algo, AlgoInfo, AlgoSimple};
|
use crate::algorithm::{Algo, AlgoInfo, AlgoSimple};
|
||||||
|
@ -14,7 +13,7 @@ use crate::card_do::{
|
||||||
use crate::crypto_data::{CardUploadableKey, Cryptogram, Hash, PublicKeyMaterial};
|
use crate::crypto_data::{CardUploadableKey, Cryptogram, Hash, PublicKeyMaterial};
|
||||||
use crate::tlv::{value::Value, Tlv};
|
use crate::tlv::{value::Value, Tlv};
|
||||||
use crate::{
|
use crate::{
|
||||||
apdu, keys, CardBackend, CardTransaction, Error, KeyType, SmartcardError, StatusBytes,
|
apdu, keys, CardBackend, CardTransaction, Error, KeyType, PinType, SmartcardError, StatusBytes,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// An OpenPGP card access object, backed by a CardBackend implementation.
|
/// An OpenPGP card access object, backed by a CardBackend implementation.
|
||||||
|
@ -80,7 +79,7 @@ impl<'a> OpenPgpTransaction<'a> {
|
||||||
/// (This data should probably be cached in a higher layer. Some parts of
|
/// (This data should probably be cached in a higher layer. Some parts of
|
||||||
/// it are needed regularly, and it does not usually change during
|
/// it are needed regularly, and it does not usually change during
|
||||||
/// normal use of a card.)
|
/// normal use of a card.)
|
||||||
pub fn application_related_data(&mut self) -> Result<ApplicationRelatedData> {
|
pub fn application_related_data(&mut self) -> Result<ApplicationRelatedData, Error> {
|
||||||
self.tx.application_related_data()
|
self.tx.application_related_data()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,14 +116,14 @@ impl<'a> OpenPgpTransaction<'a> {
|
||||||
// --- login data (5e) ---
|
// --- login data (5e) ---
|
||||||
|
|
||||||
/// Get URL (5f50)
|
/// Get URL (5f50)
|
||||||
pub fn url(&mut self) -> Result<Vec<u8>> {
|
pub fn url(&mut self) -> Result<Vec<u8>, Error> {
|
||||||
let resp = apdu::send_command(self.tx(), commands::url(), true)?;
|
let resp = apdu::send_command(self.tx(), commands::url(), true)?;
|
||||||
|
|
||||||
Ok(resp.data()?.to_vec())
|
Ok(resp.data()?.to_vec())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get cardholder related data (65)
|
/// Get cardholder related data (65)
|
||||||
pub fn cardholder_related_data(&mut self) -> Result<CardholderRelatedData> {
|
pub fn cardholder_related_data(&mut self) -> Result<CardholderRelatedData, Error> {
|
||||||
let crd = commands::cardholder_related_data();
|
let crd = commands::cardholder_related_data();
|
||||||
let resp = apdu::send_command(self.tx(), crd, true)?;
|
let resp = apdu::send_command(self.tx(), crd, true)?;
|
||||||
resp.check_ok()?;
|
resp.check_ok()?;
|
||||||
|
@ -133,15 +132,15 @@ impl<'a> OpenPgpTransaction<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get security support template (7a)
|
/// Get security support template (7a)
|
||||||
pub fn security_support_template(&mut self) -> Result<SecuritySupportTemplate> {
|
pub fn security_support_template(&mut self) -> Result<SecuritySupportTemplate, Error> {
|
||||||
let sst = commands::security_support_template();
|
let sst = commands::security_support_template();
|
||||||
let resp = apdu::send_command(self.tx(), sst, true)?;
|
let resp = apdu::send_command(self.tx(), sst, true)?;
|
||||||
resp.check_ok()?;
|
resp.check_ok()?;
|
||||||
|
|
||||||
let tlv = Tlv::try_from(resp.data()?)?;
|
let tlv = Tlv::try_from(resp.data()?)?;
|
||||||
let res = tlv
|
let res = tlv.find(&[0x93].into()).ok_or_else(|| {
|
||||||
.find(&[0x93].into())
|
Error::NotFound("Couldn't get SecuritySupportTemplate DO".to_string())
|
||||||
.ok_or_else(|| anyhow!("Couldn't get SecuritySupportTemplate DO"))?;
|
})?;
|
||||||
|
|
||||||
if let Value::S(data) = res {
|
if let Value::S(data) = res {
|
||||||
let mut data = data.to_vec();
|
let mut data = data.to_vec();
|
||||||
|
@ -153,7 +152,9 @@ impl<'a> OpenPgpTransaction<'a> {
|
||||||
let dsc: u32 = u32::from_be_bytes(data);
|
let dsc: u32 = u32::from_be_bytes(data);
|
||||||
Ok(SecuritySupportTemplate { dsc })
|
Ok(SecuritySupportTemplate { dsc })
|
||||||
} else {
|
} else {
|
||||||
Err(anyhow!("Failed to process SecuritySupportTemplate"))
|
Err(Error::NotFound(
|
||||||
|
"Failed to process SecuritySupportTemplate".to_string(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,7 +169,7 @@ impl<'a> OpenPgpTransaction<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get "Algorithm Information"
|
/// Get "Algorithm Information"
|
||||||
pub fn algorithm_information(&mut self) -> Result<Option<AlgoInfo>> {
|
pub fn algorithm_information(&mut self) -> Result<Option<AlgoInfo>, Error> {
|
||||||
let resp = apdu::send_command(self.tx(), commands::algo_info(), true)?;
|
let resp = apdu::send_command(self.tx(), commands::algo_info(), true)?;
|
||||||
resp.check_ok()?;
|
resp.check_ok()?;
|
||||||
|
|
||||||
|
@ -177,7 +178,7 @@ impl<'a> OpenPgpTransaction<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Firmware Version (YubiKey specific (?))
|
/// Firmware Version (YubiKey specific (?))
|
||||||
pub fn firmware_version(&mut self) -> Result<Vec<u8>> {
|
pub fn firmware_version(&mut self) -> Result<Vec<u8>, Error> {
|
||||||
let resp = apdu::send_command(self.tx(), commands::firmware_version(), true)?;
|
let resp = apdu::send_command(self.tx(), commands::firmware_version(), true)?;
|
||||||
|
|
||||||
Ok(resp.data()?.into())
|
Ok(resp.data()?.into())
|
||||||
|
@ -187,7 +188,7 @@ impl<'a> OpenPgpTransaction<'a> {
|
||||||
/// [see:
|
/// [see:
|
||||||
/// <https://docs.nitrokey.com/start/linux/multiple-identities.html>
|
/// <https://docs.nitrokey.com/start/linux/multiple-identities.html>
|
||||||
/// <https://github.com/Nitrokey/nitrokey-start-firmware/pull/33/>]
|
/// <https://github.com/Nitrokey/nitrokey-start-firmware/pull/33/>]
|
||||||
pub fn set_identity(&mut self, id: u8) -> Result<Vec<u8>> {
|
pub fn set_identity(&mut self, id: u8) -> Result<Vec<u8>, Error> {
|
||||||
let resp = apdu::send_command(self.tx(), commands::set_identity(id), false);
|
let resp = apdu::send_command(self.tx(), commands::set_identity(id), false);
|
||||||
|
|
||||||
// Apparently it's normal to get "NotTransacted" from pcsclite when
|
// Apparently it's normal to get "NotTransacted" from pcsclite when
|
||||||
|
@ -218,7 +219,7 @@ impl<'a> OpenPgpTransaction<'a> {
|
||||||
/// Get data from "private use" DO.
|
/// Get data from "private use" DO.
|
||||||
///
|
///
|
||||||
/// `num` must be between 1 and 4.
|
/// `num` must be between 1 and 4.
|
||||||
pub fn private_use_do(&mut self, num: u8) -> Result<Vec<u8>> {
|
pub fn private_use_do(&mut self, num: u8) -> Result<Vec<u8>, Error> {
|
||||||
assert!((1..=4).contains(&num));
|
assert!((1..=4).contains(&num));
|
||||||
|
|
||||||
let cmd = commands::private_use_do(num);
|
let cmd = commands::private_use_do(num);
|
||||||
|
@ -234,7 +235,7 @@ impl<'a> OpenPgpTransaction<'a> {
|
||||||
/// Access condition:
|
/// Access condition:
|
||||||
/// - 1/3 need PW1 (82)
|
/// - 1/3 need PW1 (82)
|
||||||
/// - 2/4 need PW3
|
/// - 2/4 need PW3
|
||||||
pub fn set_private_use_do(&mut self, num: u8, data: Vec<u8>) -> Result<Vec<u8>> {
|
pub fn set_private_use_do(&mut self, num: u8, data: Vec<u8>) -> Result<Vec<u8>, Error> {
|
||||||
assert!((1..=4).contains(&num));
|
assert!((1..=4).contains(&num));
|
||||||
|
|
||||||
let cmd = commands::put_private_use_do(num, data);
|
let cmd = commands::put_private_use_do(num, data);
|
||||||
|
@ -260,7 +261,7 @@ impl<'a> OpenPgpTransaction<'a> {
|
||||||
/// (However, e.g. vanilla Gnuk doesn't support this functionality.
|
/// (However, e.g. vanilla Gnuk doesn't support this functionality.
|
||||||
/// Gnuk needs to be built with the `--enable-factory-reset`
|
/// Gnuk needs to be built with the `--enable-factory-reset`
|
||||||
/// option to the `configure` script to enable this functionality).
|
/// option to the `configure` script to enable this functionality).
|
||||||
pub fn factory_reset(&mut self) -> Result<()> {
|
pub fn factory_reset(&mut self) -> Result<(), Error> {
|
||||||
// send 4 bad requests to verify pw1
|
// send 4 bad requests to verify pw1
|
||||||
// [apdu 00 20 00 81 08 40 40 40 40 40 40 40 40]
|
// [apdu 00 20 00 81 08 40 40 40 40 40 40 40 40]
|
||||||
for _ in 0..4 {
|
for _ in 0..4 {
|
||||||
|
@ -270,7 +271,9 @@ impl<'a> OpenPgpTransaction<'a> {
|
||||||
|| resp.status() == StatusBytes::AuthenticationMethodBlocked
|
|| resp.status() == StatusBytes::AuthenticationMethodBlocked
|
||||||
|| matches!(resp.status(), StatusBytes::PasswordNotChecked(_)))
|
|| matches!(resp.status(), StatusBytes::PasswordNotChecked(_)))
|
||||||
{
|
{
|
||||||
return Err(anyhow!("Unexpected status for reset, at pw1."));
|
return Err(Error::InternalError(
|
||||||
|
"Unexpected status for reset, at pw1.".into(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -284,7 +287,9 @@ impl<'a> OpenPgpTransaction<'a> {
|
||||||
|| resp.status() == StatusBytes::AuthenticationMethodBlocked
|
|| resp.status() == StatusBytes::AuthenticationMethodBlocked
|
||||||
|| matches!(resp.status(), StatusBytes::PasswordNotChecked(_)))
|
|| matches!(resp.status(), StatusBytes::PasswordNotChecked(_)))
|
||||||
{
|
{
|
||||||
return Err(anyhow!("Unexpected status for reset, at pw3."));
|
return Err(Error::InternalError(
|
||||||
|
"Unexpected status for reset, at pw3.".into(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -321,7 +326,7 @@ impl<'a> OpenPgpTransaction<'a> {
|
||||||
/// access condition is only valid for one PSO:CDS command or remains
|
/// access condition is only valid for one PSO:CDS command or remains
|
||||||
/// valid for several attempts.
|
/// valid for several attempts.
|
||||||
pub fn verify_pw1_for_signing_pinpad(&mut self) -> Result<(), Error> {
|
pub fn verify_pw1_for_signing_pinpad(&mut self) -> Result<(), Error> {
|
||||||
let res = self.tx().pinpad_verify(0x81)?;
|
let res = self.tx().pinpad_verify(PinType::Sign)?;
|
||||||
RawResponse::try_from(res)?.try_into()
|
RawResponse::try_from(res)?.try_into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -350,7 +355,7 @@ impl<'a> OpenPgpTransaction<'a> {
|
||||||
/// an error is returned.
|
/// an error is returned.
|
||||||
|
|
||||||
pub fn verify_pw1_pinpad(&mut self) -> Result<(), Error> {
|
pub fn verify_pw1_pinpad(&mut self) -> Result<(), Error> {
|
||||||
let res = self.tx().pinpad_verify(0x82)?;
|
let res = self.tx().pinpad_verify(PinType::User)?;
|
||||||
RawResponse::try_from(res)?.try_into()
|
RawResponse::try_from(res)?.try_into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -377,7 +382,7 @@ impl<'a> OpenPgpTransaction<'a> {
|
||||||
/// Verify PW3 (admin) using a pinpad on the card reader. If no usable
|
/// Verify PW3 (admin) using a pinpad on the card reader. If no usable
|
||||||
/// pinpad is found, an error is returned.
|
/// pinpad is found, an error is returned.
|
||||||
pub fn verify_pw3_pinpad(&mut self) -> Result<(), Error> {
|
pub fn verify_pw3_pinpad(&mut self) -> Result<(), Error> {
|
||||||
let res = self.tx().pinpad_verify(0x83)?;
|
let res = self.tx().pinpad_verify(PinType::Admin)?;
|
||||||
RawResponse::try_from(res)?.try_into()
|
RawResponse::try_from(res)?.try_into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -406,10 +411,12 @@ impl<'a> OpenPgpTransaction<'a> {
|
||||||
apdu::send_command(self.tx(), change, false)?.try_into()
|
apdu::send_command(self.tx(), change, false)?.try_into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Change the value of PW1 (user password) using a pinpad on the
|
/// Change the value of PW1 (0x81) using a pinpad on the
|
||||||
/// card reader. If no usable pinpad is found, an error is returned.
|
/// card reader. If no usable pinpad is found, an error is returned.
|
||||||
pub fn change_pw1_pinpad(&mut self) -> Result<(), Error> {
|
pub fn change_pw1_pinpad(&mut self) -> Result<(), Error> {
|
||||||
let res = self.tx().pinpad_modify(0x81)?;
|
// Note: for change PW, only 0x81 and 0x83 are used!
|
||||||
|
// 0x82 is implicitly the same as 0x81.
|
||||||
|
let res = self.tx().pinpad_modify(PinType::Sign)?;
|
||||||
RawResponse::try_from(res)?.try_into()
|
RawResponse::try_from(res)?.try_into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -428,7 +435,7 @@ impl<'a> OpenPgpTransaction<'a> {
|
||||||
/// Change the value of PW3 (admin password) using a pinpad on the
|
/// Change the value of PW3 (admin password) using a pinpad on the
|
||||||
/// card reader. If no usable pinpad is found, an error is returned.
|
/// card reader. If no usable pinpad is found, an error is returned.
|
||||||
pub fn change_pw3_pinpad(&mut self) -> Result<(), Error> {
|
pub fn change_pw3_pinpad(&mut self) -> Result<(), Error> {
|
||||||
let res = self.tx().pinpad_modify(0x83)?;
|
let res = self.tx().pinpad_modify(PinType::Admin)?;
|
||||||
RawResponse::try_from(res)?.try_into()
|
RawResponse::try_from(res)?.try_into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -72,7 +72,7 @@ impl Tlv {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<&[u8]> for Tlv {
|
impl TryFrom<&[u8]> for Tlv {
|
||||||
type Error = anyhow::Error;
|
type Error = crate::Error;
|
||||||
|
|
||||||
fn try_from(input: &[u8]) -> Result<Self, Self::Error> {
|
fn try_from(input: &[u8]) -> Result<Self, Self::Error> {
|
||||||
complete(Tlv::parse(input))
|
complete(Tlv::parse(input))
|
||||||
|
|
|
@ -37,7 +37,7 @@ impl Value {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from(data: &[u8], constructed: bool) -> Result<Self> {
|
pub fn from(data: &[u8], constructed: bool) -> Result<Self, crate::Error> {
|
||||||
complete(Self::parse(data, constructed))
|
complete(Self::parse(data, constructed))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,13 +5,12 @@
|
||||||
//! `openpgp-card`. It uses the PCSC middleware to access the OpenPGP
|
//! `openpgp-card`. It uses the PCSC middleware to access the OpenPGP
|
||||||
//! application on smart cards.
|
//! application on smart cards.
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
|
||||||
use iso7816_tlv::simple::Tlv;
|
use iso7816_tlv::simple::Tlv;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
|
|
||||||
use openpgp_card::card_do::ApplicationRelatedData;
|
use openpgp_card::card_do::ApplicationRelatedData;
|
||||||
use openpgp_card::{CardBackend, CardCaps, CardTransaction, Error, SmartcardError};
|
use openpgp_card::{CardBackend, CardCaps, CardTransaction, Error, PinType, SmartcardError};
|
||||||
|
|
||||||
const FEATURE_VERIFY_PIN_DIRECT: u8 = 0x06;
|
const FEATURE_VERIFY_PIN_DIRECT: u8 = 0x06;
|
||||||
const FEATURE_MODIFY_PIN_DIRECT: u8 = 0x07;
|
const FEATURE_MODIFY_PIN_DIRECT: u8 = 0x07;
|
||||||
|
@ -181,23 +180,21 @@ impl<'b> PcscTransaction<'b> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the minimum pin length for pin_id.
|
/// Get the minimum pin length for pin_id.
|
||||||
fn min_pin_len(&self, pin_id: u8) -> Result<u8> {
|
fn min_pin_len(&self, pin: PinType) -> u8 {
|
||||||
match pin_id {
|
match pin {
|
||||||
0x81 | 0x82 => Ok(6),
|
PinType::User | PinType::Sign => 6,
|
||||||
0x83 => Ok(8),
|
PinType::Admin => 8,
|
||||||
_ => Err(anyhow!("Unexpected pin_id {}", pin_id)),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Get the maximum pin length for pin_id.
|
/// Get the maximum pin length for pin_id.
|
||||||
fn max_pin_len(&self, pin_id: u8) -> Result<u8> {
|
fn max_pin_len(&self, pin: PinType) -> Result<u8, Error> {
|
||||||
if let Some(card_caps) = self.card_caps {
|
if let Some(card_caps) = self.card_caps {
|
||||||
match pin_id {
|
match pin {
|
||||||
0x81 | 0x82 => Ok(card_caps.pw1_max_len()),
|
PinType::User | PinType::Sign => Ok(card_caps.pw1_max_len()),
|
||||||
0x83 => Ok(card_caps.pw3_max_len()),
|
PinType::Admin => Ok(card_caps.pw3_max_len()),
|
||||||
_ => Err(anyhow!("Unexpected pin_id {}", pin_id)),
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(anyhow!("card_caps is None"))
|
Err(Error::InternalError("card_caps is None".into()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -235,9 +232,9 @@ impl CardTransaction for PcscTransaction<'_> {
|
||||||
self.reader_caps.contains_key(&FEATURE_MODIFY_PIN_DIRECT)
|
self.reader_caps.contains_key(&FEATURE_MODIFY_PIN_DIRECT)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pinpad_verify(&mut self, pin_id: u8) -> Result<Vec<u8>> {
|
fn pinpad_verify(&mut self, pin: PinType) -> Result<Vec<u8>, Error> {
|
||||||
let pin_min_size = self.min_pin_len(pin_id)?;
|
let pin_min_size = self.min_pin_len(pin);
|
||||||
let pin_max_size = self.max_pin_len(pin_id)?;
|
let pin_max_size = self.max_pin_len(pin)?;
|
||||||
|
|
||||||
// Default to varlen, for now.
|
// Default to varlen, for now.
|
||||||
// (NOTE: Some readers don't support varlen, and need explicit length
|
// (NOTE: Some readers don't support varlen, and need explicit length
|
||||||
|
@ -249,7 +246,7 @@ impl CardTransaction for PcscTransaction<'_> {
|
||||||
0x00, /* CLA */
|
0x00, /* CLA */
|
||||||
0x20, /* INS: VERIFY */
|
0x20, /* INS: VERIFY */
|
||||||
0x00, /* P1 */
|
0x00, /* P1 */
|
||||||
pin_id, /* P2 */
|
pin.id(), /* P2 */
|
||||||
fixedlen, /* Lc: 'fixedlen' data bytes */
|
fixedlen, /* Lc: 'fixedlen' data bytes */
|
||||||
];
|
];
|
||||||
ab_data.extend([0xff].repeat(fixedlen as usize));
|
ab_data.extend([0xff].repeat(fixedlen as usize));
|
||||||
|
@ -311,22 +308,26 @@ impl CardTransaction for PcscTransaction<'_> {
|
||||||
let verify_ioctl: [u8; 4] = self
|
let verify_ioctl: [u8; 4] = self
|
||||||
.reader_caps
|
.reader_caps
|
||||||
.get(&FEATURE_VERIFY_PIN_DIRECT)
|
.get(&FEATURE_VERIFY_PIN_DIRECT)
|
||||||
.ok_or_else(|| anyhow!("no reader_capability"))?
|
.ok_or_else(|| Error::Smartcard(SmartcardError::Error("no reader_capability".into())))?
|
||||||
.value()
|
.value()
|
||||||
.try_into()?;
|
.try_into()
|
||||||
|
.map_err(|e| Error::ParseError(format!("unexpected feature data: {:?}", e)))?;
|
||||||
|
|
||||||
let res = self
|
let res = self
|
||||||
.tx
|
.tx
|
||||||
.control(u32::from_be_bytes(verify_ioctl).into(), &send, &mut recv)?;
|
.control(u32::from_be_bytes(verify_ioctl).into(), &send, &mut recv)
|
||||||
|
.map_err(|e: pcsc::Error| {
|
||||||
|
Error::Smartcard(SmartcardError::Error(format!("pcsc Error: {:?}", e)))
|
||||||
|
})?;
|
||||||
|
|
||||||
log::debug!(" <- pcsc pinpad_verify result: {:x?}", res);
|
log::debug!(" <- pcsc pinpad_verify result: {:x?}", res);
|
||||||
|
|
||||||
Ok(res.to_vec())
|
Ok(res.to_vec())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pinpad_modify(&mut self, pin_id: u8) -> Result<Vec<u8>> {
|
fn pinpad_modify(&mut self, pin: PinType) -> Result<Vec<u8>, Error> {
|
||||||
let pin_min_size = self.min_pin_len(pin_id)?;
|
let pin_min_size = self.min_pin_len(pin);
|
||||||
let pin_max_size = self.max_pin_len(pin_id)?;
|
let pin_max_size = self.max_pin_len(pin)?;
|
||||||
|
|
||||||
// Default to varlen, for now.
|
// Default to varlen, for now.
|
||||||
// (NOTE: Some readers don't support varlen, and need explicit length
|
// (NOTE: Some readers don't support varlen, and need explicit length
|
||||||
|
@ -338,7 +339,7 @@ impl CardTransaction for PcscTransaction<'_> {
|
||||||
0x00, /* CLA */
|
0x00, /* CLA */
|
||||||
0x24, /* INS: CHANGE_REFERENCE_DATA */
|
0x24, /* INS: CHANGE_REFERENCE_DATA */
|
||||||
0x00, /* P1 */
|
0x00, /* P1 */
|
||||||
pin_id, /* P2 */
|
pin.id(), /* P2 */
|
||||||
fixedlen * 2, /* Lc: 'fixedlen' data bytes */
|
fixedlen * 2, /* Lc: 'fixedlen' data bytes */
|
||||||
];
|
];
|
||||||
ab_data.extend([0xff].repeat(fixedlen as usize * 2));
|
ab_data.extend([0xff].repeat(fixedlen as usize * 2));
|
||||||
|
@ -410,13 +411,17 @@ impl CardTransaction for PcscTransaction<'_> {
|
||||||
let modify_ioctl: [u8; 4] = self
|
let modify_ioctl: [u8; 4] = self
|
||||||
.reader_caps
|
.reader_caps
|
||||||
.get(&FEATURE_MODIFY_PIN_DIRECT)
|
.get(&FEATURE_MODIFY_PIN_DIRECT)
|
||||||
.ok_or_else(|| anyhow!("no reader_capability"))?
|
.ok_or_else(|| Error::Smartcard(SmartcardError::Error("no reader_capability".into())))?
|
||||||
.value()
|
.value()
|
||||||
.try_into()?;
|
.try_into()
|
||||||
|
.map_err(|e| Error::ParseError(format!("unexpected feature data: {:?}", e)))?;
|
||||||
|
|
||||||
let res = self
|
let res = self
|
||||||
.tx
|
.tx
|
||||||
.control(u32::from_be_bytes(modify_ioctl).into(), &send, &mut recv)?;
|
.control(u32::from_be_bytes(modify_ioctl).into(), &send, &mut recv)
|
||||||
|
.map_err(|e: pcsc::Error| {
|
||||||
|
Error::Smartcard(SmartcardError::Error(format!("pcsc Error: {:?}", e)))
|
||||||
|
})?;
|
||||||
|
|
||||||
log::debug!(" <- pcsc pinpad_modify result: {:x?}", res);
|
log::debug!(" <- pcsc pinpad_modify result: {:x?}", res);
|
||||||
|
|
||||||
|
@ -602,7 +607,7 @@ impl PcscBackend {
|
||||||
/// Initialized a PcscCard:
|
/// Initialized a PcscCard:
|
||||||
/// - Obtain and store feature lists from reader (pinpad functionality).
|
/// - Obtain and store feature lists from reader (pinpad functionality).
|
||||||
/// - Get ARD from card, set CardCaps based on ARD.
|
/// - Get ARD from card, set CardCaps based on ARD.
|
||||||
fn initialize_card(mut self) -> Result<Self> {
|
fn initialize_card(mut self) -> Result<Self, Error> {
|
||||||
log::debug!("pcsc initialize_card");
|
log::debug!("pcsc initialize_card");
|
||||||
|
|
||||||
let mut h: HashMap<u8, Tlv> = HashMap::default();
|
let mut h: HashMap<u8, Tlv> = HashMap::default();
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
//! `openpgp-card` crate.
|
//! `openpgp-card` crate.
|
||||||
//! It uses GnuPG's scdaemon (via GnuPG Agent) to access OpenPGP cards.
|
//! It uses GnuPG's scdaemon (via GnuPG Agent) to access OpenPGP cards.
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use sequoia_ipc::assuan::Response;
|
use sequoia_ipc::assuan::Response;
|
||||||
|
@ -13,7 +12,7 @@ use sequoia_ipc::gnupg::{Agent, Context};
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use tokio::runtime::Runtime;
|
use tokio::runtime::Runtime;
|
||||||
|
|
||||||
use openpgp_card::{CardBackend, CardCaps, CardTransaction, Error};
|
use openpgp_card::{CardBackend, CardCaps, CardTransaction, Error, PinType, SmartcardError};
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref RT: Mutex<Runtime> = Mutex::new(tokio::runtime::Runtime::new().unwrap());
|
static ref RT: Mutex<Runtime> = Mutex::new(tokio::runtime::Runtime::new().unwrap());
|
||||||
|
@ -82,7 +81,7 @@ impl ScdBackend {
|
||||||
|
|
||||||
/// Helper fn that shuts down scdaemon via GnuPG Agent.
|
/// Helper fn that shuts down scdaemon via GnuPG Agent.
|
||||||
/// This may be useful to obtain access to a Smard card via PCSC.
|
/// This may be useful to obtain access to a Smard card via PCSC.
|
||||||
pub fn shutdown_scd(agent: Option<Agent>) -> Result<()> {
|
pub fn shutdown_scd(agent: Option<Agent>) -> Result<(), Error> {
|
||||||
let mut scdc = Self::new(agent, false)?;
|
let mut scdc = Self::new(agent, false)?;
|
||||||
|
|
||||||
scdc.send("SCD RESTART")?;
|
scdc.send("SCD RESTART")?;
|
||||||
|
@ -96,13 +95,23 @@ impl ScdBackend {
|
||||||
///
|
///
|
||||||
/// If `agent` is None, a Context with the default GnuPG home directory
|
/// If `agent` is None, a Context with the default GnuPG home directory
|
||||||
/// is used.
|
/// is used.
|
||||||
fn new(agent: Option<Agent>, init: bool) -> Result<Self> {
|
fn new(agent: Option<Agent>, init: bool) -> Result<Self, Error> {
|
||||||
let agent = if let Some(agent) = agent {
|
let agent = if let Some(agent) = agent {
|
||||||
agent
|
agent
|
||||||
} else {
|
} else {
|
||||||
// Create and use a new Agent based on a default Context
|
// Create and use a new Agent based on a default Context
|
||||||
let ctx = Context::new()?;
|
let ctx = Context::new().map_err(|e| {
|
||||||
RT.lock().unwrap().block_on(Agent::connect(&ctx))?
|
Error::Smartcard(SmartcardError::Error(format!("Context::new failed {}", e)))
|
||||||
|
})?;
|
||||||
|
RT.lock()
|
||||||
|
.unwrap()
|
||||||
|
.block_on(Agent::connect(&ctx))
|
||||||
|
.map_err(|e| {
|
||||||
|
Error::Smartcard(SmartcardError::Error(format!(
|
||||||
|
"Agent::connect failed {}",
|
||||||
|
e
|
||||||
|
)))
|
||||||
|
})?
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut scdc = Self {
|
let mut scdc = Self {
|
||||||
|
@ -117,13 +126,22 @@ impl ScdBackend {
|
||||||
Ok(scdc)
|
Ok(scdc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn send2(&mut self, cmd: &str) -> Result<(), Error> {
|
||||||
|
self.agent.send(cmd).map_err(|e| {
|
||||||
|
Error::Smartcard(SmartcardError::Error(format!(
|
||||||
|
"scdc agent send failed: {}",
|
||||||
|
e
|
||||||
|
)))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Call "SCD SERIALNO", which causes scdaemon to be started by gpg
|
/// Call "SCD SERIALNO", which causes scdaemon to be started by gpg
|
||||||
/// agent (if it's not running yet).
|
/// agent (if it's not running yet).
|
||||||
fn serialno(&mut self) -> Result<()> {
|
fn serialno(&mut self) -> Result<(), Error> {
|
||||||
let mut rt = RT.lock().unwrap();
|
let mut rt = RT.lock().unwrap();
|
||||||
|
|
||||||
let send = "SCD SERIALNO";
|
let send = "SCD SERIALNO";
|
||||||
self.agent.send(send)?;
|
self.send2(send)?;
|
||||||
|
|
||||||
while let Some(response) = rt.block_on(self.agent.next()) {
|
while let Some(response) = rt.block_on(self.agent.next()) {
|
||||||
log::debug!("init res: {:x?}", response);
|
log::debug!("init res: {:x?}", response);
|
||||||
|
@ -138,14 +156,16 @@ impl ScdBackend {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(anyhow!("SCDC init() failed"))
|
Err(Error::Smartcard(SmartcardError::Error(
|
||||||
|
"SCDC init() failed".into(),
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ask scdameon to switch to using a specific OpenPGP card, based on
|
/// Ask scdameon to switch to using a specific OpenPGP card, based on
|
||||||
/// its `serial`.
|
/// its `serial`.
|
||||||
fn select_card(&mut self, serial: &str) -> Result<()> {
|
fn select_card(&mut self, serial: &str) -> Result<(), Error> {
|
||||||
let send = format!("SCD SERIALNO --demand={}", serial);
|
let send = format!("SCD SERIALNO --demand={}", serial);
|
||||||
self.agent.send(send)?;
|
self.send2(&send)?;
|
||||||
|
|
||||||
let mut rt = RT.lock().unwrap();
|
let mut rt = RT.lock().unwrap();
|
||||||
|
|
||||||
|
@ -153,7 +173,9 @@ impl ScdBackend {
|
||||||
log::debug!("select res: {:x?}", response);
|
log::debug!("select res: {:x?}", response);
|
||||||
|
|
||||||
if response.is_err() {
|
if response.is_err() {
|
||||||
return Err(anyhow!("Card not found"));
|
return Err(Error::Smartcard(SmartcardError::CardNotFound(
|
||||||
|
serial.into(),
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(Response::Status { .. }) = response {
|
if let Ok(Response::Status { .. }) = response {
|
||||||
|
@ -166,11 +188,13 @@ impl ScdBackend {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(anyhow!("Card not found"))
|
Err(Error::Smartcard(SmartcardError::CardNotFound(
|
||||||
|
serial.into(),
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send(&mut self, cmd: &str) -> Result<()> {
|
fn send(&mut self, cmd: &str) -> Result<(), Error> {
|
||||||
self.agent.send(cmd)?;
|
self.send2(cmd)?;
|
||||||
|
|
||||||
let mut rt = RT.lock().unwrap();
|
let mut rt = RT.lock().unwrap();
|
||||||
|
|
||||||
|
@ -178,7 +202,7 @@ impl ScdBackend {
|
||||||
log::debug!("select res: {:x?}", response);
|
log::debug!("select res: {:x?}", response);
|
||||||
|
|
||||||
if let Err(e) = response {
|
if let Err(e) = response {
|
||||||
return Err(anyhow!("Err {:?}", e));
|
return Err(Error::Smartcard(SmartcardError::Error(format!("{:?}", e))));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(..) = response {
|
if let Ok(..) = response {
|
||||||
|
@ -191,7 +215,10 @@ impl ScdBackend {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(anyhow!("Error sending command {}", cmd))
|
Err(Error::Smartcard(SmartcardError::Error(format!(
|
||||||
|
"Error sending command {}",
|
||||||
|
cmd
|
||||||
|
))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,23 +252,23 @@ impl CardTransaction for ScdTransaction<'_> {
|
||||||
log::debug!("SCDC command: '{}'", send);
|
log::debug!("SCDC command: '{}'", send);
|
||||||
|
|
||||||
if send.len() > ASSUAN_LINELENGTH {
|
if send.len() > ASSUAN_LINELENGTH {
|
||||||
return Err(Error::InternalError(anyhow!(
|
return Err(Error::Smartcard(SmartcardError::Error(format!(
|
||||||
"APDU command is too long ({}) to send via Assuan",
|
"APDU command is too long ({}) to send via Assuan",
|
||||||
send.len()
|
send.len()
|
||||||
)));
|
))));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.scd.agent.send(send)?;
|
self.scd.send2(&send)?;
|
||||||
|
|
||||||
let mut rt = RT.lock().unwrap();
|
let mut rt = RT.lock().unwrap();
|
||||||
|
|
||||||
while let Some(response) = rt.block_on(self.scd.agent.next()) {
|
while let Some(response) = rt.block_on(self.scd.agent.next()) {
|
||||||
log::debug!("res: {:x?}", response);
|
log::debug!("res: {:x?}", response);
|
||||||
if response.is_err() {
|
if response.is_err() {
|
||||||
return Err(Error::InternalError(anyhow!(
|
return Err(Error::Smartcard(SmartcardError::Error(format!(
|
||||||
"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 {
|
||||||
|
@ -256,7 +283,9 @@ impl CardTransaction for ScdTransaction<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(Error::InternalError(anyhow!("no response found")))
|
Err(Error::Smartcard(SmartcardError::Error(
|
||||||
|
"no response found".into(),
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_card_caps(&mut self, caps: CardCaps) {
|
fn init_card_caps(&mut self, caps: CardCaps) {
|
||||||
|
@ -284,12 +313,12 @@ impl CardTransaction for ScdTransaction<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// FIXME: not implemented yet
|
/// FIXME: not implemented yet
|
||||||
fn pinpad_verify(&mut self, _id: u8) -> Result<Vec<u8>> {
|
fn pinpad_verify(&mut self, _id: PinType) -> Result<Vec<u8>, Error> {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// FIXME: not implemented yet
|
/// FIXME: not implemented yet
|
||||||
fn pinpad_modify(&mut self, _id: u8) -> Result<Vec<u8>> {
|
fn pinpad_modify(&mut self, _id: PinType) -> Result<Vec<u8>, Error> {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -372,7 +372,7 @@ fn factory_reset(ident: &str) -> Result<()> {
|
||||||
let mut pgp = OpenPgp::new(&mut card);
|
let mut pgp = OpenPgp::new(&mut card);
|
||||||
|
|
||||||
let mut open = Open::new(pgp.transaction()?)?;
|
let mut open = Open::new(pgp.transaction()?)?;
|
||||||
open.factory_reset()
|
open.factory_reset().map_err(|e| anyhow!(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn key_import_yolo(mut admin: Admin, key: &Cert) -> Result<()> {
|
fn key_import_yolo(mut admin: Admin, key: &Cert) -> Result<()> {
|
||||||
|
|
Loading…
Reference in a new issue