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
|
||||
//! different types, such as `Open`, `User`, `Sign`, `Admin`.
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
|
||||
use sequoia_openpgp::cert::amalgamation::key::ValidErasedKeyAmalgamation;
|
||||
use sequoia_openpgp::packet::key::SecretParts;
|
||||
use sequoia_openpgp::Cert;
|
||||
|
@ -188,7 +186,7 @@ impl<'a> Open<'a> {
|
|||
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()
|
||||
}
|
||||
|
||||
|
@ -206,12 +204,12 @@ impl<'a> Open<'a> {
|
|||
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)
|
||||
}
|
||||
|
||||
/// 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()
|
||||
}
|
||||
|
||||
|
@ -259,22 +257,22 @@ impl<'a> Open<'a> {
|
|||
|
||||
// --- 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())
|
||||
}
|
||||
|
||||
// --- 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()
|
||||
}
|
||||
|
||||
// --- 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()
|
||||
}
|
||||
|
||||
// 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
|
||||
// Algorithm attributes can be changed
|
||||
let ec = self.extended_capabilities()?;
|
||||
|
@ -288,20 +286,20 @@ impl<'a> Open<'a> {
|
|||
}
|
||||
|
||||
/// 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()
|
||||
}
|
||||
|
||||
// ----------
|
||||
|
||||
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())
|
||||
}
|
||||
|
||||
// ----------
|
||||
|
||||
/// 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()
|
||||
}
|
||||
}
|
||||
|
@ -354,12 +352,12 @@ impl<'app, 'open> Admin<'app, 'open> {
|
|||
impl Admin<'_, '_> {
|
||||
pub fn set_name(&mut self, name: &str) -> Result<(), Error> {
|
||||
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
|
||||
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())
|
||||
|
@ -367,7 +365,7 @@ impl Admin<'_, '_> {
|
|||
|
||||
pub fn set_lang(&mut self, lang: &[Lang]) -> Result<(), Error> {
|
||||
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)
|
||||
|
@ -379,7 +377,7 @@ impl Admin<'_, '_> {
|
|||
|
||||
pub fn set_url(&mut self, url: &str) -> Result<(), Error> {
|
||||
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
|
||||
|
@ -393,7 +391,7 @@ impl Admin<'_, '_> {
|
|||
|
||||
self.oc.opt.set_url(url.as_bytes())
|
||||
} 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();
|
||||
Ok(Self { ca, public })
|
||||
} else {
|
||||
Err(Error::InternalError(anyhow!(
|
||||
Err(Error::InternalError(format!(
|
||||
"Failed to find (sub)key {} in cert",
|
||||
fp
|
||||
)))
|
||||
}
|
||||
} else {
|
||||
Err(Error::InternalError(anyhow!(
|
||||
Err(Error::InternalError(format!(
|
||||
"Failed to get the decryption key's Fingerprint from the card"
|
||||
)))
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
use std::convert::TryFrom;
|
||||
use std::convert::TryInto;
|
||||
|
||||
use anyhow::Result;
|
||||
use openpgp::cert::amalgamation::key::ValidErasedKeyAmalgamation;
|
||||
use openpgp::crypto::{mpi, mpi::ProtectedMPI, mpi::MPI};
|
||||
use openpgp::packet::{
|
||||
|
@ -49,14 +48,15 @@ impl SequoiaKey {
|
|||
/// Implement the `CardUploadableKey` trait that openpgp-card uses to
|
||||
/// upload (sub)keys to a card.
|
||||
impl CardUploadableKey for SequoiaKey {
|
||||
fn private_key(&self) -> Result<PrivateKeyMaterial> {
|
||||
fn private_key(&self) -> Result<PrivateKeyMaterial, Error> {
|
||||
// Decrypt key with password, if set
|
||||
let key = match &self.password {
|
||||
None => self.key.clone(),
|
||||
Some(pw) => self
|
||||
.key
|
||||
.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
|
||||
|
@ -124,8 +124,15 @@ struct SqRSA {
|
|||
|
||||
impl SqRSA {
|
||||
#[allow(clippy::many_single_char_names)]
|
||||
fn new(e: MPI, d: ProtectedMPI, n: MPI, p: ProtectedMPI, q: ProtectedMPI) -> Result<Self> {
|
||||
let nettle = nettle::rsa::PrivateKey::new(d.value(), p.value(), q.value(), None)?;
|
||||
fn new(
|
||||
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 })
|
||||
}
|
||||
|
|
|
@ -46,13 +46,13 @@ impl<'a, 'app> CardSigner<'a, 'app> {
|
|||
let key = eka.key().clone();
|
||||
Ok(Self::with_pubkey(ca, key))
|
||||
} else {
|
||||
Err(Error::InternalError(anyhow!(
|
||||
Err(Error::InternalError(format!(
|
||||
"Failed to find (sub)key {} in cert",
|
||||
fp
|
||||
)))
|
||||
}
|
||||
} else {
|
||||
Err(Error::InternalError(anyhow!(
|
||||
Err(Error::InternalError(format!(
|
||||
"Failed to get the signing key's Fingerprint from the card"
|
||||
)))
|
||||
}
|
||||
|
|
|
@ -177,12 +177,14 @@ pub fn public_key_material_to_key(
|
|||
pkm: &PublicKeyMaterial,
|
||||
key_type: KeyType,
|
||||
time: KeyGenerationTime,
|
||||
) -> Result<PublicKey> {
|
||||
) -> Result<PublicKey, Error> {
|
||||
let time = Timestamp::from(time.get()).into();
|
||||
|
||||
match pkm {
|
||||
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())
|
||||
}
|
||||
|
@ -202,7 +204,13 @@ pub fn public_key_material_to_key(
|
|||
KeyType::Authentication | KeyType::Signing => {
|
||||
if algo_ecc.curve() == Curve::Ed25519 {
|
||||
// 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))
|
||||
} else {
|
||||
|
@ -214,7 +222,13 @@ pub fn public_key_material_to_key(
|
|||
curve,
|
||||
q: mpi::MPI::new(ecc.data()),
|
||||
},
|
||||
)?;
|
||||
)
|
||||
.map_err(|e| {
|
||||
Error::InternalError(format!(
|
||||
"sequoia Key4::new for ECDSA failed: {:?}",
|
||||
e
|
||||
))
|
||||
})?;
|
||||
|
||||
Ok(k4.into())
|
||||
}
|
||||
|
@ -225,7 +239,13 @@ pub fn public_key_material_to_key(
|
|||
// ok when a cert already exists
|
||||
|
||||
// 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())
|
||||
} else {
|
||||
|
@ -242,7 +262,13 @@ pub fn public_key_material_to_key(
|
|||
hash: Default::default(),
|
||||
sym: Default::default(),
|
||||
},
|
||||
)?;
|
||||
)
|
||||
.map_err(|e| {
|
||||
Error::InternalError(format!(
|
||||
"sequoia Key4::new for ECDH failed: {:?}",
|
||||
e
|
||||
))
|
||||
})?;
|
||||
|
||||
Ok(k4.into())
|
||||
}
|
||||
|
|
|
@ -86,7 +86,7 @@ impl AlgoSimple {
|
|||
key_type: KeyType,
|
||||
ard: &ApplicationRelatedData,
|
||||
algo_info: Option<AlgoInfo>,
|
||||
) -> Result<Algo> {
|
||||
) -> Result<Algo, crate::Error> {
|
||||
let algo = match self {
|
||||
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)?),
|
||||
|
@ -189,7 +189,9 @@ impl Algo {
|
|||
match self {
|
||||
Algo::Rsa(rsa) => Self::rsa_algo_attrs(rsa),
|
||||
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 {
|
||||
type Error = anyhow::Error;
|
||||
type Error = crate::Error;
|
||||
|
||||
fn try_from(oid: &[u8]) -> Result<Self, Self::Error> {
|
||||
use Curve::*;
|
||||
|
@ -349,7 +351,7 @@ impl TryFrom<&[u8]> for Curve {
|
|||
[0x2b, 0x65, 0x71] => Ed448,
|
||||
[0x2b, 0x65, 0x6f] => X448,
|
||||
|
||||
_ => return Err(anyhow!("Unknown curve OID {:?}", oid)),
|
||||
_ => return Err(Error::ParseError(format!("Unknown curve OID {:?}", oid))),
|
||||
};
|
||||
|
||||
Ok(curve)
|
||||
|
|
|
@ -153,9 +153,7 @@ where
|
|||
let cla = if last { 0x00 } else { 0x10 };
|
||||
let partial = Command::new(cla, cmd.ins(), cmd.p1(), cmd.p2(), d.to_vec());
|
||||
|
||||
let serialized = partial
|
||||
.serialize(ext_len, expect_response)
|
||||
.map_err(Error::InternalError)?;
|
||||
let serialized = partial.serialize(ext_len, expect_response)?;
|
||||
|
||||
log::debug!(" -> chained APDU command: {:x?}", &serialized);
|
||||
|
||||
|
|
|
@ -69,7 +69,11 @@ impl Command {
|
|||
/// Serialize a Command (for sending to a card).
|
||||
///
|
||||
/// 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):
|
||||
// T=0 does not allow the use of Lc together with Le;
|
||||
// thus disable Le in this case.
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
//! OpenPGP card data objects (DO)
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use chrono::{DateTime, Utc};
|
||||
use std::convert::TryFrom;
|
||||
use std::convert::TryInto;
|
||||
|
@ -41,7 +40,7 @@ impl ApplicationRelatedData {
|
|||
if let Some(aid) = aid {
|
||||
Ok(ApplicationIdentifier::try_from(&aid.serialize()[..])?)
|
||||
} 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);
|
||||
(hist.serialize().as_slice()).try_into()
|
||||
} 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
|
||||
/// 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"
|
||||
let eli = self.0.find(&[0x7f, 0x66].into());
|
||||
|
||||
|
@ -100,27 +101,29 @@ impl ApplicationRelatedData {
|
|||
version,
|
||||
))?)
|
||||
} 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)
|
||||
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"
|
||||
let aa = self.0.find(&[key_type.algorithm_tag()].into());
|
||||
|
||||
if let Some(aa) = aa {
|
||||
Algo::try_from(&aa.serialize()[..])
|
||||
} else {
|
||||
Err(anyhow!(
|
||||
Err(Error::NotFound(format!(
|
||||
"Failed to get algorithm attributes for {:?}.",
|
||||
key_type
|
||||
))
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
/// 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"
|
||||
let psb = self.0.find(&[0xc4].into());
|
||||
|
||||
|
@ -131,7 +134,9 @@ impl ApplicationRelatedData {
|
|||
|
||||
Ok(pws)
|
||||
} 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)
|
||||
} else {
|
||||
Err(anyhow!("Failed to get fingerprints.").into())
|
||||
Err(Error::NotFound("Failed to get fingerprints.".into()))
|
||||
}
|
||||
}
|
||||
|
||||
/// 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());
|
||||
|
||||
if let Some(kg) = kg {
|
||||
|
@ -163,7 +168,9 @@ impl ApplicationRelatedData {
|
|||
|
||||
Ok(kg)
|
||||
} 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
|
||||
pub(crate) fn complete<O>(result: nom::IResult<&[u8], O>) -> Result<O, anyhow::Error> {
|
||||
let (rem, output) = result.map_err(|err| anyhow!("Parsing failed: {:?}", err))?;
|
||||
pub(crate) fn complete<O>(result: nom::IResult<&[u8], O>) -> Result<O, Error> {
|
||||
let (rem, output) = result.map_err(|_err| Error::ParseError(format!("Parsing failed")))?;
|
||||
if rem.is_empty() {
|
||||
Ok(output)
|
||||
} 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 {
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,9 +87,9 @@ pub(self) fn parse(input: &[u8]) -> nom::IResult<&[u8], Vec<(KeyType, Algo)>> {
|
|||
}
|
||||
|
||||
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))?))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,9 +31,9 @@ fn parse(input: &[u8]) -> nom::IResult<&[u8], 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))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,9 +25,9 @@ impl 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 tlv = Tlv::new([0x65], value);
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
//! 4.4.3.7 Extended Capabilities
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use crate::card_do::ExtendedCapabilities;
|
||||
|
@ -80,15 +79,17 @@ impl TryFrom<(&[u8], u16)> for ExtendedCapabilities {
|
|||
let i9 = input[9];
|
||||
|
||||
if i8 > 1 {
|
||||
return Err(
|
||||
anyhow!("Illegal value '{}' for pin_block_2_format_support", i8).into(),
|
||||
);
|
||||
return Err(Error::ParseError(
|
||||
format!("Illegal value '{}' for pin_block_2_format_support", i8).into(),
|
||||
));
|
||||
}
|
||||
|
||||
pin_block_2_format_support = Some(i8 != 0);
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ impl ExtendedLengthInfo {
|
|||
}
|
||||
|
||||
impl TryFrom<&[u8]> for ExtendedLengthInfo {
|
||||
type Error = anyhow::Error;
|
||||
type Error = crate::Error;
|
||||
|
||||
fn try_from(input: &[u8]) -> Result<Self, Self::Error> {
|
||||
let eli = complete(parse(input))?;
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
//! Fingerprint for a single key slot
|
||||
|
||||
use anyhow::anyhow;
|
||||
use nom::{bytes::complete as bytes, combinator, sequence};
|
||||
use std::convert::TryFrom;
|
||||
use std::convert::TryInto;
|
||||
|
@ -28,7 +27,9 @@ impl TryFrom<&[u8]> for Fingerprint {
|
|||
let array: [u8; 20] = input.try_into().unwrap();
|
||||
Ok(array.into())
|
||||
} 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.
|
||||
self::fingerprints(input)
|
||||
.map(|res| res.1)
|
||||
.map_err(|err| anyhow!("Parsing failed: {:?}", err))
|
||||
.map_err(Error::InternalError)
|
||||
.map_err(|_err| Error::ParseError("Parsing failed".into()))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
|
||||
use crate::card_do::{CardCapabilities, CardServiceData, HistoricalBytes};
|
||||
use crate::Error;
|
||||
use anyhow::{anyhow, Result};
|
||||
use std::convert::TryFrom;
|
||||
|
||||
impl CardCapabilities {
|
||||
|
@ -77,7 +76,7 @@ impl HistoricalBytes {
|
|||
}
|
||||
|
||||
impl TryFrom<&[u8]> for HistoricalBytes {
|
||||
type Error = Error;
|
||||
type Error = crate::Error;
|
||||
|
||||
fn try_from(mut data: &[u8]) -> Result<Self, Self::Error> {
|
||||
// workaround-hack for "ledger" with zero-padded historical bytes
|
||||
|
@ -95,18 +94,18 @@ impl TryFrom<&[u8]> for HistoricalBytes {
|
|||
if len < 4 {
|
||||
// historical bytes cannot be this short
|
||||
|
||||
return Err(anyhow!(format!(
|
||||
"Historical bytes too short ({} bytes), must be >= 4",
|
||||
len
|
||||
))
|
||||
.into());
|
||||
return Err(Error::ParseError(
|
||||
format!("Historical bytes too short ({} bytes), must be >= 4", len).into(),
|
||||
));
|
||||
}
|
||||
|
||||
if data[0] != 0 {
|
||||
// The OpenPGP application assumes a category indicator byte
|
||||
// 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
|
||||
|
@ -127,14 +126,15 @@ impl TryFrom<&[u8]> for HistoricalBytes {
|
|||
// (1 byte for the tl, plus `l` bytes of data for this ctlv)
|
||||
// (e.g. len = 4 -> tl + 3byte data)
|
||||
if ctlv.len() < (1 + l as usize) {
|
||||
return Err(anyhow!(
|
||||
"Illegal length value in Historical Bytes TL {} len {} \
|
||||
l {}",
|
||||
ctlv[0],
|
||||
ctlv.len(),
|
||||
l
|
||||
)
|
||||
.into());
|
||||
return Err(Error::ParseError(
|
||||
format!(
|
||||
"Illegal length value in Historical Bytes TL {} len {} l {}",
|
||||
ctlv[0],
|
||||
ctlv.len(),
|
||||
l
|
||||
)
|
||||
.into(),
|
||||
));
|
||||
}
|
||||
|
||||
match (t, l) {
|
||||
|
@ -177,7 +177,9 @@ impl TryFrom<&[u8]> for HistoricalBytes {
|
|||
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)
|
||||
|
||||
use anyhow::anyhow;
|
||||
use chrono::{DateTime, NaiveDateTime, Utc};
|
||||
use nom::{combinator, number::complete as number, sequence};
|
||||
|
||||
use crate::card_do::{KeyGenerationTime, KeySet};
|
||||
use crate::Error;
|
||||
use chrono::{DateTime, NaiveDateTime, Utc};
|
||||
use nom::{combinator, number::complete as number, sequence};
|
||||
use std::convert::TryFrom;
|
||||
|
||||
impl From<KeyGenerationTime> for DateTime<Utc> {
|
||||
|
@ -69,8 +67,7 @@ impl TryFrom<&[u8]> for KeySet<KeyGenerationTime> {
|
|||
// hasn't been completely consumed.
|
||||
self::key_generation_set(input)
|
||||
.map(|res| res.1)
|
||||
.map_err(|err| anyhow!("Parsing failed: {:?}", err))
|
||||
.map_err(Error::InternalError)
|
||||
.map_err(|_err| Error::ParseError(format!("Parsing failed")))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
|
||||
//! PW status Bytes (see spec page 23)
|
||||
|
||||
use anyhow::anyhow;
|
||||
|
||||
use crate::card_do::PWStatusBytes;
|
||||
use crate::Error;
|
||||
use std::convert::TryFrom;
|
||||
|
@ -41,7 +39,7 @@ impl PWStatusBytes {
|
|||
}
|
||||
|
||||
impl TryFrom<&[u8]> for PWStatusBytes {
|
||||
type Error = Error;
|
||||
type Error = crate::Error;
|
||||
|
||||
fn try_from(input: &[u8]) -> Result<Self, Self::Error> {
|
||||
if input.len() == 7 {
|
||||
|
@ -67,7 +65,7 @@ impl TryFrom<&[u8]> for PWStatusBytes {
|
|||
err_count_pw3,
|
||||
})
|
||||
} else {
|
||||
Err(Error::InternalError(anyhow!(
|
||||
Err(Error::ParseError(format!(
|
||||
"Unexpected length of PW Status Bytes: {}",
|
||||
input.len()
|
||||
)))
|
||||
|
|
|
@ -61,7 +61,7 @@ pub enum Cryptogram<'a> {
|
|||
/// to an OpenPGP card
|
||||
pub trait CardUploadableKey {
|
||||
/// private key data
|
||||
fn private_key(&self) -> Result<PrivateKeyMaterial>;
|
||||
fn private_key(&self) -> Result<PrivateKeyMaterial, crate::Error>;
|
||||
|
||||
/// timestamp of (sub)key creation
|
||||
fn timestamp(&self) -> KeyGenerationTime;
|
||||
|
|
|
@ -26,8 +26,18 @@ pub enum Error {
|
|||
#[error("Unexpected response length: {0}")]
|
||||
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}")]
|
||||
InternalError(anyhow::Error),
|
||||
InternalError(String),
|
||||
}
|
||||
|
||||
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)
|
||||
#[derive(thiserror::Error, Debug, PartialEq, Copy, Clone)]
|
||||
#[non_exhaustive]
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
//! Generate and import keys
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use std::convert::TryFrom;
|
||||
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)
|
||||
let ts = time
|
||||
.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;
|
||||
|
||||
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
|
||||
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 v = tlv.find(&[0x82].into());
|
||||
|
||||
|
@ -111,10 +110,10 @@ fn tlv_to_pubkey(tlv: &Tlv, algo: &Algo) -> Result<PublicKeyMaterial> {
|
|||
Ok(PublicKeyMaterial::E(ecc))
|
||||
}
|
||||
|
||||
(_, _, _) => Err(anyhow!(
|
||||
(_, _, _) => Err(Error::UnsupportedAlgo(format!(
|
||||
"Unexpected public key material from card {:?}",
|
||||
tlv
|
||||
)),
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -229,7 +228,7 @@ pub(crate) fn determine_rsa_attrs(
|
|||
key_type: KeyType,
|
||||
ard: &ApplicationRelatedData,
|
||||
algo_info: Option<AlgoInfo>,
|
||||
) -> Result<RsaAttrs> {
|
||||
) -> Result<RsaAttrs, crate::Error> {
|
||||
// Figure out suitable RSA algorithm parameters:
|
||||
|
||||
// Does the card offer a list of algorithms?
|
||||
|
@ -273,13 +272,16 @@ pub(crate) fn determine_ecc_attrs(
|
|||
ecc_type: EccType,
|
||||
key_type: KeyType,
|
||||
algo_info: Option<AlgoInfo>,
|
||||
) -> Result<EccAttrs> {
|
||||
) -> Result<EccAttrs, crate::Error> {
|
||||
// If we have an algo_info, refuse upload if oid is not listed
|
||||
if let Some(algo_info) = algo_info {
|
||||
let algos = check_card_algo_ecc(algo_info, key_type, oid);
|
||||
if algos.is_empty() {
|
||||
// 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"
|
||||
|
@ -332,7 +334,9 @@ fn card_algo_rsa(algo_info: AlgoInfo, key_type: KeyType, rsa_bits: u16) -> Resul
|
|||
Ok((**algo.last().unwrap()).clone())
|
||||
} else {
|
||||
// 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::Signing => 0xB6,
|
||||
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![])))
|
||||
}
|
||||
|
|
|
@ -94,10 +94,10 @@ pub trait CardTransaction {
|
|||
fn feature_pinpad_modify(&self) -> bool;
|
||||
|
||||
/// 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
|
||||
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
|
||||
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
|
||||
/// it are needed regularly, and it does not usually change during
|
||||
/// 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 resp = apdu::send_command(self, ad, true)?;
|
||||
let value = Value::from(resp.data()?, true)?;
|
||||
|
@ -128,7 +128,7 @@ pub trait CardTransaction {
|
|||
/// This fn initializes the CardCaps by requesting
|
||||
/// application_related_data from the card, and setting the
|
||||
/// capabilities accordingly.
|
||||
fn initialize(&mut self) -> Result<()> {
|
||||
fn initialize(&mut self) -> Result<(), Error> {
|
||||
let ard = self.application_related_data()?;
|
||||
|
||||
// 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
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
#[non_exhaustive]
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// SPDX-FileCopyrightText: 2021-2022 Heiko Schaefer <heiko@schaefer.name>
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
|
||||
use crate::algorithm::{Algo, AlgoInfo, AlgoSimple};
|
||||
|
@ -14,7 +13,7 @@ use crate::card_do::{
|
|||
use crate::crypto_data::{CardUploadableKey, Cryptogram, Hash, PublicKeyMaterial};
|
||||
use crate::tlv::{value::Value, Tlv};
|
||||
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.
|
||||
|
@ -80,7 +79,7 @@ impl<'a> OpenPgpTransaction<'a> {
|
|||
/// (This data should probably be cached in a higher layer. Some parts of
|
||||
/// it are needed regularly, and it does not usually change during
|
||||
/// 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()
|
||||
}
|
||||
|
||||
|
@ -117,14 +116,14 @@ impl<'a> OpenPgpTransaction<'a> {
|
|||
// --- login data (5e) ---
|
||||
|
||||
/// 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)?;
|
||||
|
||||
Ok(resp.data()?.to_vec())
|
||||
}
|
||||
|
||||
/// 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 resp = apdu::send_command(self.tx(), crd, true)?;
|
||||
resp.check_ok()?;
|
||||
|
@ -133,15 +132,15 @@ impl<'a> OpenPgpTransaction<'a> {
|
|||
}
|
||||
|
||||
/// 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 resp = apdu::send_command(self.tx(), sst, true)?;
|
||||
resp.check_ok()?;
|
||||
|
||||
let tlv = Tlv::try_from(resp.data()?)?;
|
||||
let res = tlv
|
||||
.find(&[0x93].into())
|
||||
.ok_or_else(|| anyhow!("Couldn't get SecuritySupportTemplate DO"))?;
|
||||
let res = tlv.find(&[0x93].into()).ok_or_else(|| {
|
||||
Error::NotFound("Couldn't get SecuritySupportTemplate DO".to_string())
|
||||
})?;
|
||||
|
||||
if let Value::S(data) = res {
|
||||
let mut data = data.to_vec();
|
||||
|
@ -153,7 +152,9 @@ impl<'a> OpenPgpTransaction<'a> {
|
|||
let dsc: u32 = u32::from_be_bytes(data);
|
||||
Ok(SecuritySupportTemplate { dsc })
|
||||
} 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"
|
||||
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)?;
|
||||
resp.check_ok()?;
|
||||
|
||||
|
@ -177,7 +178,7 @@ impl<'a> OpenPgpTransaction<'a> {
|
|||
}
|
||||
|
||||
/// 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)?;
|
||||
|
||||
Ok(resp.data()?.into())
|
||||
|
@ -187,7 +188,7 @@ impl<'a> OpenPgpTransaction<'a> {
|
|||
/// [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>> {
|
||||
pub fn set_identity(&mut self, id: u8) -> Result<Vec<u8>, Error> {
|
||||
let resp = apdu::send_command(self.tx(), commands::set_identity(id), false);
|
||||
|
||||
// Apparently it's normal to get "NotTransacted" from pcsclite when
|
||||
|
@ -218,7 +219,7 @@ impl<'a> OpenPgpTransaction<'a> {
|
|||
/// Get data from "private use" DO.
|
||||
///
|
||||
/// `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));
|
||||
|
||||
let cmd = commands::private_use_do(num);
|
||||
|
@ -234,7 +235,7 @@ impl<'a> OpenPgpTransaction<'a> {
|
|||
/// Access condition:
|
||||
/// - 1/3 need PW1 (82)
|
||||
/// - 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));
|
||||
|
||||
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.
|
||||
/// Gnuk needs to be built with the `--enable-factory-reset`
|
||||
/// 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
|
||||
// [apdu 00 20 00 81 08 40 40 40 40 40 40 40 40]
|
||||
for _ in 0..4 {
|
||||
|
@ -270,7 +271,9 @@ impl<'a> OpenPgpTransaction<'a> {
|
|||
|| resp.status() == StatusBytes::AuthenticationMethodBlocked
|
||||
|| 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
|
||||
|| 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
|
||||
/// valid for several attempts.
|
||||
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()
|
||||
}
|
||||
|
||||
|
@ -350,7 +355,7 @@ impl<'a> OpenPgpTransaction<'a> {
|
|||
/// an error is returned.
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
|
@ -377,7 +382,7 @@ impl<'a> OpenPgpTransaction<'a> {
|
|||
/// Verify PW3 (admin) using a pinpad on the card reader. If no usable
|
||||
/// pinpad is found, an error is returned.
|
||||
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()
|
||||
}
|
||||
|
||||
|
@ -406,10 +411,12 @@ impl<'a> OpenPgpTransaction<'a> {
|
|||
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.
|
||||
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()
|
||||
}
|
||||
|
||||
|
@ -428,7 +435,7 @@ impl<'a> OpenPgpTransaction<'a> {
|
|||
/// Change the value of PW3 (admin password) using a pinpad on the
|
||||
/// card reader. If no usable pinpad is found, an error is returned.
|
||||
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()
|
||||
}
|
||||
|
||||
|
|
|
@ -72,7 +72,7 @@ impl Tlv {
|
|||
}
|
||||
|
||||
impl TryFrom<&[u8]> for Tlv {
|
||||
type Error = anyhow::Error;
|
||||
type Error = crate::Error;
|
||||
|
||||
fn try_from(input: &[u8]) -> Result<Self, Self::Error> {
|
||||
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))
|
||||
}
|
||||
|
||||
|
|
|
@ -5,13 +5,12 @@
|
|||
//! `openpgp-card`. It uses the PCSC middleware to access the OpenPGP
|
||||
//! application on smart cards.
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use iso7816_tlv::simple::Tlv;
|
||||
use std::collections::HashMap;
|
||||
use std::convert::TryInto;
|
||||
|
||||
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_MODIFY_PIN_DIRECT: u8 = 0x07;
|
||||
|
@ -181,23 +180,21 @@ impl<'b> PcscTransaction<'b> {
|
|||
}
|
||||
|
||||
/// Get the minimum pin length for pin_id.
|
||||
fn min_pin_len(&self, pin_id: u8) -> Result<u8> {
|
||||
match pin_id {
|
||||
0x81 | 0x82 => Ok(6),
|
||||
0x83 => Ok(8),
|
||||
_ => Err(anyhow!("Unexpected pin_id {}", pin_id)),
|
||||
fn min_pin_len(&self, pin: PinType) -> u8 {
|
||||
match pin {
|
||||
PinType::User | PinType::Sign => 6,
|
||||
PinType::Admin => 8,
|
||||
}
|
||||
}
|
||||
/// 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 {
|
||||
match pin_id {
|
||||
0x81 | 0x82 => Ok(card_caps.pw1_max_len()),
|
||||
0x83 => Ok(card_caps.pw3_max_len()),
|
||||
_ => Err(anyhow!("Unexpected pin_id {}", pin_id)),
|
||||
match pin {
|
||||
PinType::User | PinType::Sign => Ok(card_caps.pw1_max_len()),
|
||||
PinType::Admin => Ok(card_caps.pw3_max_len()),
|
||||
}
|
||||
} 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)
|
||||
}
|
||||
|
||||
fn pinpad_verify(&mut self, pin_id: u8) -> Result<Vec<u8>> {
|
||||
let pin_min_size = self.min_pin_len(pin_id)?;
|
||||
let pin_max_size = self.max_pin_len(pin_id)?;
|
||||
fn pinpad_verify(&mut self, pin: PinType) -> Result<Vec<u8>, Error> {
|
||||
let pin_min_size = self.min_pin_len(pin);
|
||||
let pin_max_size = self.max_pin_len(pin)?;
|
||||
|
||||
// Default to varlen, for now.
|
||||
// (NOTE: Some readers don't support varlen, and need explicit length
|
||||
|
@ -249,7 +246,7 @@ impl CardTransaction for PcscTransaction<'_> {
|
|||
0x00, /* CLA */
|
||||
0x20, /* INS: VERIFY */
|
||||
0x00, /* P1 */
|
||||
pin_id, /* P2 */
|
||||
pin.id(), /* P2 */
|
||||
fixedlen, /* Lc: 'fixedlen' data bytes */
|
||||
];
|
||||
ab_data.extend([0xff].repeat(fixedlen as usize));
|
||||
|
@ -311,22 +308,26 @@ impl CardTransaction for PcscTransaction<'_> {
|
|||
let verify_ioctl: [u8; 4] = self
|
||||
.reader_caps
|
||||
.get(&FEATURE_VERIFY_PIN_DIRECT)
|
||||
.ok_or_else(|| anyhow!("no reader_capability"))?
|
||||
.ok_or_else(|| Error::Smartcard(SmartcardError::Error("no reader_capability".into())))?
|
||||
.value()
|
||||
.try_into()?;
|
||||
.try_into()
|
||||
.map_err(|e| Error::ParseError(format!("unexpected feature data: {:?}", e)))?;
|
||||
|
||||
let res = self
|
||||
.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);
|
||||
|
||||
Ok(res.to_vec())
|
||||
}
|
||||
|
||||
fn pinpad_modify(&mut self, pin_id: u8) -> Result<Vec<u8>> {
|
||||
let pin_min_size = self.min_pin_len(pin_id)?;
|
||||
let pin_max_size = self.max_pin_len(pin_id)?;
|
||||
fn pinpad_modify(&mut self, pin: PinType) -> Result<Vec<u8>, Error> {
|
||||
let pin_min_size = self.min_pin_len(pin);
|
||||
let pin_max_size = self.max_pin_len(pin)?;
|
||||
|
||||
// Default to varlen, for now.
|
||||
// (NOTE: Some readers don't support varlen, and need explicit length
|
||||
|
@ -338,7 +339,7 @@ impl CardTransaction for PcscTransaction<'_> {
|
|||
0x00, /* CLA */
|
||||
0x24, /* INS: CHANGE_REFERENCE_DATA */
|
||||
0x00, /* P1 */
|
||||
pin_id, /* P2 */
|
||||
pin.id(), /* P2 */
|
||||
fixedlen * 2, /* Lc: 'fixedlen' data bytes */
|
||||
];
|
||||
ab_data.extend([0xff].repeat(fixedlen as usize * 2));
|
||||
|
@ -410,13 +411,17 @@ impl CardTransaction for PcscTransaction<'_> {
|
|||
let modify_ioctl: [u8; 4] = self
|
||||
.reader_caps
|
||||
.get(&FEATURE_MODIFY_PIN_DIRECT)
|
||||
.ok_or_else(|| anyhow!("no reader_capability"))?
|
||||
.ok_or_else(|| Error::Smartcard(SmartcardError::Error("no reader_capability".into())))?
|
||||
.value()
|
||||
.try_into()?;
|
||||
.try_into()
|
||||
.map_err(|e| Error::ParseError(format!("unexpected feature data: {:?}", e)))?;
|
||||
|
||||
let res = self
|
||||
.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);
|
||||
|
||||
|
@ -602,7 +607,7 @@ impl PcscBackend {
|
|||
/// Initialized a PcscCard:
|
||||
/// - Obtain and store feature lists from reader (pinpad functionality).
|
||||
/// - 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");
|
||||
|
||||
let mut h: HashMap<u8, Tlv> = HashMap::default();
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
//! `openpgp-card` crate.
|
||||
//! It uses GnuPG's scdaemon (via GnuPG Agent) to access OpenPGP cards.
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use futures::StreamExt;
|
||||
use lazy_static::lazy_static;
|
||||
use sequoia_ipc::assuan::Response;
|
||||
|
@ -13,7 +12,7 @@ use sequoia_ipc::gnupg::{Agent, Context};
|
|||
use std::sync::Mutex;
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
use openpgp_card::{CardBackend, CardCaps, CardTransaction, Error};
|
||||
use openpgp_card::{CardBackend, CardCaps, CardTransaction, Error, PinType, SmartcardError};
|
||||
|
||||
lazy_static! {
|
||||
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.
|
||||
/// 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)?;
|
||||
|
||||
scdc.send("SCD RESTART")?;
|
||||
|
@ -96,13 +95,23 @@ impl ScdBackend {
|
|||
///
|
||||
/// If `agent` is None, a Context with the default GnuPG home directory
|
||||
/// 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 {
|
||||
agent
|
||||
} else {
|
||||
// Create and use a new Agent based on a default Context
|
||||
let ctx = Context::new()?;
|
||||
RT.lock().unwrap().block_on(Agent::connect(&ctx))?
|
||||
let ctx = Context::new().map_err(|e| {
|
||||
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 {
|
||||
|
@ -117,13 +126,22 @@ impl ScdBackend {
|
|||
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
|
||||
/// 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 send = "SCD SERIALNO";
|
||||
self.agent.send(send)?;
|
||||
self.send2(send)?;
|
||||
|
||||
while let Some(response) = rt.block_on(self.agent.next()) {
|
||||
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
|
||||
/// 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);
|
||||
self.agent.send(send)?;
|
||||
self.send2(&send)?;
|
||||
|
||||
let mut rt = RT.lock().unwrap();
|
||||
|
||||
|
@ -153,7 +173,9 @@ impl ScdBackend {
|
|||
log::debug!("select res: {:x?}", response);
|
||||
|
||||
if response.is_err() {
|
||||
return Err(anyhow!("Card not found"));
|
||||
return Err(Error::Smartcard(SmartcardError::CardNotFound(
|
||||
serial.into(),
|
||||
)));
|
||||
}
|
||||
|
||||
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<()> {
|
||||
self.agent.send(cmd)?;
|
||||
fn send(&mut self, cmd: &str) -> Result<(), Error> {
|
||||
self.send2(cmd)?;
|
||||
|
||||
let mut rt = RT.lock().unwrap();
|
||||
|
||||
|
@ -178,7 +202,7 @@ impl ScdBackend {
|
|||
log::debug!("select res: {:x?}", response);
|
||||
|
||||
if let Err(e) = response {
|
||||
return Err(anyhow!("Err {:?}", e));
|
||||
return Err(Error::Smartcard(SmartcardError::Error(format!("{:?}", e))));
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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",
|
||||
send.len()
|
||||
)));
|
||||
))));
|
||||
}
|
||||
|
||||
self.scd.agent.send(send)?;
|
||||
self.scd.send2(&send)?;
|
||||
|
||||
let mut rt = RT.lock().unwrap();
|
||||
|
||||
while let Some(response) = rt.block_on(self.scd.agent.next()) {
|
||||
log::debug!("res: {:x?}", response);
|
||||
if response.is_err() {
|
||||
return Err(Error::InternalError(anyhow!(
|
||||
return Err(Error::Smartcard(SmartcardError::Error(format!(
|
||||
"Unexpected error response from SCD {:?}",
|
||||
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) {
|
||||
|
@ -284,12 +313,12 @@ impl CardTransaction for ScdTransaction<'_> {
|
|||
}
|
||||
|
||||
/// 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!()
|
||||
}
|
||||
|
||||
/// 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!()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -372,7 +372,7 @@ fn factory_reset(ident: &str) -> Result<()> {
|
|||
let mut pgp = OpenPgp::new(&mut card);
|
||||
|
||||
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<()> {
|
||||
|
|
Loading…
Reference in a new issue