This commit is contained in:
Heiko Schaefer 2021-07-01 23:46:12 +02:00
parent 959342bb5a
commit f00865ab75
25 changed files with 1225 additions and 794 deletions

9
.rustfmt.toml Normal file
View file

@ -0,0 +1,9 @@
# SPDX-FileCopyrightText: 2019-2020 Heiko Schaefer <heiko@schaefer.name>
# SPDX-License-Identifier: CC0-1.0
# The following configuration gives warnings in stable rust versions since
# they're unstable (Jan 2020).
max_width = 79
comment_width = 79
#report_fixme = "Always"

View file

@ -3,7 +3,6 @@
use anyhow::anyhow;
use openpgp::Cert;
use openpgp::crypto;
use openpgp::crypto::mpi;
use openpgp::crypto::SessionKey;
@ -13,10 +12,11 @@ use openpgp::parse::stream::{
};
use openpgp::policy::Policy;
use openpgp::types::{Curve, SymmetricAlgorithm};
use openpgp::Cert;
use sequoia_openpgp as openpgp;
use openpgp_card::{DecryptMe, OpenPGPCardUser};
use openpgp_card::errors::OpenpgpCardError;
use openpgp_card::{DecryptMe, OpenPGPCardUser};
use crate::PublicKey;
@ -33,10 +33,11 @@ impl<'a> CardDecryptor<'a> {
///
/// An Error is returned if no match between the card's decryption
/// key and a (sub)key of `cert` can be made.
pub fn new(ocu: &'a OpenPGPCardUser,
cert: &Cert,
policy: &dyn Policy) -> Result<CardDecryptor<'a>, OpenpgpCardError> {
pub fn new(
ocu: &'a OpenPGPCardUser,
cert: &Cert,
policy: &dyn Policy,
) -> Result<CardDecryptor<'a>, OpenpgpCardError> {
// Get the fingerprint for the decryption key from the card.
let fps = ocu.get_fingerprints()?;
let fp = fps.decryption();
@ -46,27 +47,28 @@ impl<'a> CardDecryptor<'a> {
let fp = openpgp::Fingerprint::from_bytes(fp.as_bytes());
// Find the matching encryption-capable (sub)key in `cert`
let keys: Vec<_> =
cert.keys()
.with_policy(policy, None)
.for_storage_encryption()
.for_transport_encryption()
.filter(|ka| ka.fingerprint() == fp)
.map(|ka| ka.key())
.collect();
let keys: Vec<_> = cert
.keys()
.with_policy(policy, None)
.for_storage_encryption()
.for_transport_encryption()
.filter(|ka| ka.fingerprint() == fp)
.map(|ka| ka.key())
.collect();
// Exactly one matching (sub)key should be found. If not, fail!
if keys.len() == 1 {
let public = keys[0].clone();
Ok(Self { ocu, public })
} else {
Err(OpenpgpCardError::InternalError(
anyhow!("Failed to find a matching (sub)key in cert")))
Err(OpenpgpCardError::InternalError(anyhow!(
"Failed to find a matching (sub)key in cert"
)))
}
} else {
Err(OpenpgpCardError::InternalError(
anyhow!("Failed to get the decryption key's Fingerprint \
from the card")))
Err(OpenpgpCardError::InternalError(anyhow!(
"Failed to get the decryption key's Fingerprint from the card"
)))
}
}
}
@ -88,10 +90,7 @@ impl<'a> crypto::Decryptor for CardDecryptor<'a> {
_plaintext_len: Option<usize>,
) -> openpgp::Result<crypto::SessionKey> {
match (ciphertext, self.public.mpis()) {
(
mpi::Ciphertext::RSA { c: ct },
mpi::PublicKey::RSA { .. },
) => {
(mpi::Ciphertext::RSA { c: ct }, mpi::PublicKey::RSA { .. }) => {
let dm = DecryptMe::RSA(ct.value());
let dec = self.ocu.decrypt(dm)?;
@ -102,29 +101,29 @@ impl<'a> crypto::Decryptor for CardDecryptor<'a> {
mpi::Ciphertext::ECDH { ref e, .. },
mpi::PublicKey::ECDH { ref curve, .. },
) => {
let dm =
if curve == &Curve::Cv25519 {
// Ephemeral key without header byte 0x40
DecryptMe::ECDH(&e.value()[1..])
} else {
// NIST curves: ephemeral key with header byte
DecryptMe::ECDH(&e.value())
};
let dm = if curve == &Curve::Cv25519 {
// Ephemeral key without header byte 0x40
DecryptMe::ECDH(&e.value()[1..])
} else {
// NIST curves: ephemeral key with header byte
DecryptMe::ECDH(&e.value())
};
// Decryption operation on the card
let dec = self.ocu.decrypt(dm)?;
#[allow(non_snake_case)]
let S: openpgp::crypto::mem::Protected = dec.into();
let S: openpgp::crypto::mem::Protected = dec.into();
Ok(crypto::ecdh::decrypt_unwrap(&self.public, &S, ciphertext)?)
}
(ciphertext, public) =>
Err(anyhow!(
"Unsupported combination of ciphertext {:?} \
and public key {:?} ", ciphertext, public
)),
(ciphertext, public) => Err(anyhow!(
"Unsupported combination of ciphertext {:?} \
and public key {:?} ",
ciphertext,
public
)),
}
}
}
@ -137,7 +136,8 @@ impl<'a> DecryptionHelper for CardDecryptor<'a> {
sym_algo: Option<SymmetricAlgorithm>,
mut dec_fn: D,
) -> openpgp::Result<Option<openpgp::Fingerprint>>
where D: FnMut(SymmetricAlgorithm, &SessionKey) -> bool,
where
D: FnMut(SymmetricAlgorithm, &SessionKey) -> bool,
{
// Try to decrypt each PKESK, see:
// https://docs.sequoia-pgp.org/src/sequoia_openpgp/packet/pkesk.rs.html#125
@ -160,7 +160,10 @@ impl<'a> DecryptionHelper for CardDecryptor<'a> {
}
impl<'a> VerificationHelper for CardDecryptor<'a> {
fn get_certs(&mut self, _ids: &[openpgp::KeyHandle]) -> openpgp::Result<Vec<openpgp::Cert>> {
fn get_certs(
&mut self,
_ids: &[openpgp::KeyHandle],
) -> openpgp::Result<Vec<openpgp::Cert>> {
Ok(vec![])
}
fn check(&mut self, _structure: MessageStructure) -> openpgp::Result<()> {

View file

@ -12,17 +12,18 @@ use chrono::prelude::*;
use openpgp::armor;
use openpgp::cert::amalgamation::key::ValidErasedKeyAmalgamation;
use openpgp::crypto::mpi;
use openpgp::crypto::mpi::{MPI, ProtectedMPI};
use openpgp::packet::{Key, key};
use openpgp::crypto::mpi::{ProtectedMPI, MPI};
use openpgp::packet::key::{SecretParts, UnspecifiedRole};
use openpgp::parse::{Parse, stream::DecryptorBuilder};
use openpgp::packet::{key, Key};
use openpgp::parse::{stream::DecryptorBuilder, Parse};
use openpgp::policy::StandardPolicy;
use openpgp::serialize::stream::{Message, Signer};
use sequoia_openpgp as openpgp;
use openpgp_card::{CardUploadableKey, EccKey, EccType, errors::OpenpgpCardError,
KeyType, OpenPGPCardAdmin, OpenPGPCardUser, PrivateKeyMaterial,
RSAKey};
use openpgp_card::{
errors::OpenpgpCardError, CardUploadableKey, EccKey, EccType, KeyType,
OpenPGPCardAdmin, OpenPGPCardUser, PrivateKeyMaterial, RSAKey,
};
mod decryptor;
mod signer;
@ -30,7 +31,6 @@ mod signer;
/// Shorthand for public key data
pub(crate) type PublicKey = Key<key::PublicParts, key::UnspecifiedRole>;
/// A SequoiaKey represents the private cryptographic key material of an
/// OpenPGP (sub)key to be uploaded to an OpenPGP card.
struct SequoiaKey {
@ -43,8 +43,10 @@ impl SequoiaKey {
/// A `SequoiaKey` wraps a Sequoia PGP private (sub)key data
/// (i.e. a ValidErasedKeyAmalgamation) in a form that can be uploaded
/// by the openpgp-card crate.
fn new(vka: ValidErasedKeyAmalgamation<SecretParts>,
password: Option<String>) -> Self {
fn new(
vka: ValidErasedKeyAmalgamation<SecretParts>,
password: Option<String>,
) -> Self {
let public = vka.parts_as_public().mpis().clone();
Self {
@ -62,51 +64,72 @@ impl CardUploadableKey for SequoiaKey {
// 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()))?
Some(pw) => self.key.clone().decrypt_secret(
&openpgp::crypto::Password::from(pw.as_str()),
)?,
};
// Get private cryptographic material
let unenc =
if let Some(openpgp::packet::key::SecretKeyMaterial::Unencrypted(ref u)) = key.optional_secret() {
u
} else {
panic!("can't get private key material");
};
let unenc = if let Some(
openpgp::packet::key::SecretKeyMaterial::Unencrypted(ref u),
) = key.optional_secret()
{
u
} else {
panic!("can't get private key material");
};
let secret_key_material = unenc.map(|mpis| mpis.clone());
match (&self.public, secret_key_material) {
(mpi::PublicKey::RSA { e, n },
mpi::SecretKeyMaterial::RSA { d: _, p, q, u: _ }) => {
(
mpi::PublicKey::RSA { e, n },
mpi::SecretKeyMaterial::RSA { d: _, p, q, u: _ },
) => {
let sq_rsa = SqRSA::new(e.clone(), n.clone(), p, q);
Ok(PrivateKeyMaterial::R(Box::new(sq_rsa)))
}
(mpi::PublicKey::ECDH { curve, .. },
mpi::SecretKeyMaterial::ECDH { scalar }) => {
let sq_ecc = SqEccKey::new(curve.oid().to_vec(),
scalar, EccType::ECDH);
(
mpi::PublicKey::ECDH { curve, .. },
mpi::SecretKeyMaterial::ECDH { scalar },
) => {
let sq_ecc =
SqEccKey::new(curve.oid().to_vec(), scalar, EccType::ECDH);
Ok(PrivateKeyMaterial::E(Box::new(sq_ecc)))
}
(mpi::PublicKey::ECDSA { curve, .. },
mpi::SecretKeyMaterial::ECDSA { scalar }) => {
let sq_ecc = SqEccKey::new(curve.oid().to_vec(),
scalar, EccType::ECDSA);
(
mpi::PublicKey::ECDSA { curve, .. },
mpi::SecretKeyMaterial::ECDSA { scalar },
) => {
let sq_ecc = SqEccKey::new(
curve.oid().to_vec(),
scalar,
EccType::ECDSA,
);
Ok(PrivateKeyMaterial::E(Box::new(sq_ecc)))
}
(mpi::PublicKey::EdDSA { curve, .. },
mpi::SecretKeyMaterial::EdDSA { scalar }) => {
let sq_ecc = SqEccKey::new(curve.oid().to_vec(),
scalar, EccType::EdDSA);
(
mpi::PublicKey::EdDSA { curve, .. },
mpi::SecretKeyMaterial::EdDSA { scalar },
) => {
let sq_ecc = SqEccKey::new(
curve.oid().to_vec(),
scalar,
EccType::EdDSA,
);
Ok(PrivateKeyMaterial::E(Box::new(sq_ecc)))
}
(p, s) => {
unimplemented!("Unexpected algorithms: public {:?}, \
secret {:?}", p, s);
unimplemented!(
"Unexpected algorithms: public {:?}, \
secret {:?}",
p,
s
);
}
}
}
@ -121,7 +144,6 @@ impl CardUploadableKey for SequoiaKey {
}
}
/// RSA-specific data-structure to hold private (sub)key material for upload
/// with the `openpgp-card` crate.
struct SqRSA {
@ -165,7 +187,11 @@ struct SqEccKey {
impl SqEccKey {
fn new(oid: Vec<u8>, scalar: ProtectedMPI, ecc_type: EccType) -> Self {
SqEccKey { oid, scalar, ecc_type }
SqEccKey {
oid,
scalar,
ecc_type,
}
}
}
@ -183,7 +209,6 @@ impl EccKey for SqEccKey {
}
}
/// Convenience fn to select and upload a (sub)key from a Cert, as a given
/// KeyType. If multiple suitable (sub)keys are found, the first one is
/// used.
@ -267,9 +292,7 @@ pub fn sign(
let s = signer::CardSigner::new(ocu, cert, &p)?;
let message = Message::new(&mut armorer);
let mut message = Signer::new(message, s)
.detached()
.build()?;
let mut message = Signer::new(message, s).detached().build()?;
// Process input data, via message
io::copy(input, &mut message)?;
@ -279,6 +302,5 @@ pub fn sign(
let buffer = armorer.finalize()?;
String::from_utf8(buffer)
.context("Failed to convert signature to utf8")
String::from_utf8(buffer).context("Failed to convert signature to utf8")
}

View file

@ -1,12 +1,12 @@
// SPDX-FileCopyrightText: 2021 Heiko Schaefer <heiko@schaefer.name>
// SPDX-License-Identifier: MIT OR Apache-2.0
use std::error::Error;
use std::env;
use std::error::Error;
use anyhow::Result;
use sequoia_openpgp::Cert;
use sequoia_openpgp::parse::Parse;
use sequoia_openpgp::Cert;
use openpgp_card::{KeyType, OpenPGPCard};
@ -21,7 +21,6 @@ use openpgp_card::{KeyType, OpenPGPCard};
const TEST_KEY_PATH: &str = "example/test25519.sec";
const TEST_ENC_MSG: &str = "example/encrypted_to_25519.asc";
fn main() -> Result<(), Box<dyn Error>> {
env_logger::init();
@ -72,14 +71,12 @@ fn main() -> Result<(), Box<dyn Error>> {
let algo = oc.get_algorithm_attributes(KeyType::Authentication)?;
println!("algo aut {:?}", algo);
// ---------------------------------------------
// CAUTION: Write commands ahead!
// Try not to overwrite your production cards.
// ---------------------------------------------
assert_eq!(app_id.serial(), test_card_serial);
oc.factory_reset()?;
match oc.verify_pw3("12345678") {
@ -89,7 +86,8 @@ fn main() -> Result<(), Box<dyn Error>> {
let res = oc_admin.set_name("Bar<<Foo")?;
println!("set name {:x?}", res);
let res = oc_admin.set_sex(openpgp_card::Sex::NotApplicable)?;
let res =
oc_admin.set_sex(openpgp_card::Sex::NotApplicable)?;
println!("set sex {:x?}", res);
let res = oc_admin.set_lang("en")?;
@ -98,7 +96,6 @@ fn main() -> Result<(), Box<dyn Error>> {
let res = oc_admin.set_url("https://keys.openpgp.org")?;
println!("set url {:x?}", res);
let cert = Cert::from_file(TEST_KEY_PATH)?;
openpgp_card_sequoia::upload_from_cert_yolo(
@ -123,7 +120,7 @@ fn main() -> Result<(), Box<dyn Error>> {
// None,
// )?;
}
_ => panic!()
_ => panic!(),
}
// -----------------------------
@ -141,21 +138,23 @@ fn main() -> Result<(), Box<dyn Error>> {
println!("pw1 82 verify ok");
let cert = Cert::from_file(TEST_KEY_PATH)?;
let msg = std::fs::read_to_string
(TEST_ENC_MSG)
let msg = std::fs::read_to_string(TEST_ENC_MSG)
.expect("Unable to read file");
println!("{:?}", msg);
let res = openpgp_card_sequoia::decrypt
(&oc_user, &cert, msg.into_bytes())?;
let res = openpgp_card_sequoia::decrypt(
&oc_user,
&cert,
msg.into_bytes(),
)?;
let plain = String::from_utf8_lossy(&res);
println!("decrypted plaintext: {}", plain);
assert_eq!(plain, "Hello world!\n");
}
_ => panic!("verify pw1 failed")
_ => panic!("verify pw1 failed"),
}
// -----------------------------
@ -171,8 +170,11 @@ fn main() -> Result<(), Box<dyn Error>> {
let cert = Cert::from_file(TEST_KEY_PATH)?;
let text = "Hello world, I am signed.";
let res = openpgp_card_sequoia::sign(&oc_user, &cert,
&mut text.as_bytes());
let res = openpgp_card_sequoia::sign(
&oc_user,
&cert,
&mut text.as_bytes(),
);
println!("res sign {:?}", res);
@ -180,7 +182,7 @@ fn main() -> Result<(), Box<dyn Error>> {
// FIXME: validate sig
}
_ => panic!("verify pw1 failed")
_ => panic!("verify pw1 failed"),
}
} else {
println!("Please set environment variable TEST_CARD_SERIAL.");
@ -190,8 +192,7 @@ fn main() -> Result<(), Box<dyn Error>> {
println!("So do NOT use your production card for testing.");
println!();
println!("The following OpenPGP cards are currently connected to \
your system:");
println!("The following OpenPGP cards are connected to your system:");
let cards = openpgp_card::OpenPGPCard::list_cards()?;
for c in cards {

View file

@ -10,13 +10,12 @@ use openpgp::policy::Policy;
use openpgp::types::PublicKeyAlgorithm;
use sequoia_openpgp as openpgp;
use openpgp_card::errors::OpenpgpCardError;
use openpgp_card::Hash;
use openpgp_card::OpenPGPCardUser;
use openpgp_card::errors::OpenpgpCardError;
use crate::PublicKey;
pub(crate) struct CardSigner<'a> {
/// The OpenPGP card (authenticated to allow signing operations)
ocu: &'a OpenPGPCardUser,
@ -30,11 +29,11 @@ impl<'a> CardSigner<'a> {
///
/// An Error is returned if no match between the card's signing
/// key and a (sub)key of `cert` can be made.
pub fn new(ocu: &'a OpenPGPCardUser,
cert: &openpgp::Cert,
policy: &dyn Policy)
-> Result<CardSigner<'a>, OpenpgpCardError> {
pub fn new(
ocu: &'a OpenPGPCardUser,
cert: &openpgp::Cert,
policy: &dyn Policy,
) -> Result<CardSigner<'a>, OpenpgpCardError> {
// Get the fingerprint for the signing key from the card.
let fps = ocu.get_fingerprints()?;
let fp = fps.signature();
@ -44,16 +43,15 @@ impl<'a> CardSigner<'a> {
let fp = openpgp::Fingerprint::from_bytes(fp.as_bytes());
// Find the matching signing-capable (sub)key in `cert`
let keys: Vec<_> =
cert
.keys()
.with_policy(policy, None)
.alive()
.revoked(false)
.for_signing()
.filter(|ka| ka.fingerprint() == fp)
.map(|ka| ka.key())
.collect();
let keys: Vec<_> = cert
.keys()
.with_policy(policy, None)
.alive()
.revoked(false)
.for_signing()
.filter(|ka| ka.fingerprint() == fp)
.map(|ka| ka.key())
.collect();
// Exactly one matching (sub)key should be found. If not, fail!
if keys.len() == 1 {
@ -64,13 +62,15 @@ impl<'a> CardSigner<'a> {
public: public.role_as_unspecified().clone(),
})
} else {
Err(OpenpgpCardError::InternalError(
anyhow!("Failed to find a matching (sub)key in cert")))
Err(OpenpgpCardError::InternalError(anyhow!(
"Failed to find a matching (sub)key in cert"
)))
}
} else {
Err(OpenpgpCardError::InternalError(
anyhow!("Failed to get the signing key's Fingerprint \
from the card")))
Err(OpenpgpCardError::InternalError(anyhow!(
"Failed to get the signing key's Fingerprint \
from the card"
)))
}
}
}
@ -86,34 +86,45 @@ impl<'a> crypto::Signer for CardSigner<'a> {
/// perform the signing operation.
///
/// (7.2.10 PSO: COMPUTE DIGITAL SIGNATURE)
fn sign(&mut self,
hash_algo: openpgp::types::HashAlgorithm,
digest: &[u8],
fn sign(
&mut self,
hash_algo: openpgp::types::HashAlgorithm,
digest: &[u8],
) -> openpgp::Result<mpi::Signature> {
match (self.public.pk_algo(), self.public.mpis()) {
#[allow(deprecated)]
(PublicKeyAlgorithm::RSASign, mpi::PublicKey::RSA { .. }) |
(PublicKeyAlgorithm::RSAEncryptSign, mpi::PublicKey::RSA { .. }) => {
(PublicKeyAlgorithm::RSASign, mpi::PublicKey::RSA { .. })
| (
PublicKeyAlgorithm::RSAEncryptSign,
mpi::PublicKey::RSA { .. },
) => {
let sig = match hash_algo {
openpgp::types::HashAlgorithm::SHA256 => {
let hash = Hash::SHA256(digest.try_into()
.map_err(|_| anyhow!("invalid slice length"))?);
let hash =
Hash::SHA256(digest.try_into().map_err(|_| {
anyhow!("invalid slice length")
})?);
self.ocu.signature_for_hash(hash)?
}
openpgp::types::HashAlgorithm::SHA384 => {
let hash = Hash::SHA384(digest.try_into()
.map_err(|_| anyhow!("invalid slice length"))?);
let hash =
Hash::SHA384(digest.try_into().map_err(|_| {
anyhow!("invalid slice length")
})?);
self.ocu.signature_for_hash(hash)?
}
openpgp::types::HashAlgorithm::SHA512 => {
let hash = Hash::SHA512(digest.try_into()
.map_err(|_| anyhow!("invalid slice length"))?);
let hash =
Hash::SHA512(digest.try_into().map_err(|_| {
anyhow!("invalid slice length")
})?);
self.ocu.signature_for_hash(hash)?
}
_ => {
return Err(
anyhow!("Unsupported hash algorithm for RSA {:?}",
hash_algo));
return Err(anyhow!(
"Unsupported hash algorithm for RSA {:?}",
hash_algo
));
}
};
@ -133,7 +144,8 @@ impl<'a> crypto::Signer for CardSigner<'a> {
// FIXME: implement NIST etc
(pk_algo, _) => Err(anyhow!(
"Unsupported combination of algorithm {:?} and pubkey {:?}",
pk_algo, self.public
pk_algo,
self.public
)),
}
}

View file

@ -1,8 +1,8 @@
// SPDX-FileCopyrightText: 2021 Heiko Schaefer <heiko@schaefer.name>
// SPDX-License-Identifier: MIT OR Apache-2.0
use anyhow::Result;
use crate::apdu::Le;
use anyhow::Result;
#[allow(clippy::upper_case_acronyms)]
#[derive(Clone, Debug)]
@ -22,7 +22,13 @@ pub struct Command {
impl Command {
pub fn new(cla: u8, ins: u8, p1: u8, p2: u8, data: Vec<u8>) -> Self {
Command { cla, ins, p1, p2, data }
Command {
cla,
ins,
p1,
p2,
data,
}
}
pub(crate) fn serialize(&self, ext: Le) -> Result<Vec<u8>> {
@ -34,9 +40,11 @@ impl Command {
// (must be the same)
let data_len = if self.data.len() as u16 > 0xff || ext == Le::Long {
vec![0,
(self.data.len() as u16 >> 8) as u8,
(self.data.len() as u16 & 255) as u8]
vec![
0,
(self.data.len() as u16 >> 8) as u8,
(self.data.len() as u16 & 255) as u8,
]
} else {
vec![self.data.len() as u8]
};
@ -56,7 +64,6 @@ impl Command {
// thus disable Le in this case. */
// if (reader_table[slot].is_t0)
// le = -1;
Le::None => (),
Le::Short => buf.push(0),

View file

@ -2,13 +2,15 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
/// APDU Commands for OpenPGP card operations
use crate::apdu::command::Command;
/// Select the OpenPGP applet
pub fn select_openpgp() -> Command {
Command::new(
0x00, 0xA4, 0x04, 0x00,
0x00,
0xA4,
0x04,
0x00,
vec![0xD2, 0x76, 0x00, 0x01, 0x24, 0x01],
)
}
@ -68,7 +70,6 @@ pub fn activate_file() -> Command {
Command::new(0x00, 0x44, 0x00, 0x00, vec![])
}
/// 7.2.8 PUT DATA,
/// ('tag' must consist of either one or two bytes)
pub fn put_data(tag: &[u8], data: Vec<u8>) -> Command {
@ -82,7 +83,6 @@ pub fn put_data(tag: &[u8], data: Vec<u8>) -> Command {
Command::new(0x00, 0xda, p1, p2, data)
}
/// PUT DO Name
pub fn put_name(name: Vec<u8>) -> Command {
put_data(&[0x5b], name)

View file

@ -5,26 +5,33 @@ pub mod command;
pub mod commands;
pub mod response;
use std::convert::TryFrom;
use pcsc::Card;
use std::convert::TryFrom;
use crate::OpenPGPCard;
use crate::apdu::command::Command;
use crate::errors::{OcErrorStatus, OpenpgpCardError, SmartcardError};
use crate::apdu::response::Response;
use crate::errors::{OcErrorStatus, OpenpgpCardError, SmartcardError};
use crate::OpenPGPCard;
#[derive(Clone, Copy, PartialEq)]
pub(crate) enum Le { None, Short, Long }
pub(crate) enum Le {
None,
Short,
Long,
}
/// Send a Command and return the result as a Response.
///
/// If the reply is truncated, this fn assembles all the parts and returns
/// them as one aggregated Response.
pub(crate) fn send_command(card: &Card, cmd: Command, ext: Le,
oc: Option<&OpenPGPCard>)
-> Result<Response, OpenpgpCardError> {
let mut resp = Response::try_from(
send_command_low_level(&card, cmd, ext, oc)?)?;
pub(crate) fn send_command(
card: &Card,
cmd: Command,
ext: Le,
oc: Option<&OpenPGPCard>,
) -> Result<Response, OpenpgpCardError> {
let mut resp =
Response::try_from(send_command_low_level(&card, cmd, ext, oc)?)?;
while resp.status()[0] == 0x61 {
// More data is available for this command from the card
@ -32,9 +39,12 @@ pub(crate) fn send_command(card: &Card, cmd: Command, ext: Le,
log::trace!(" response was truncated, getting more data");
// Get additional data
let next = Response::try_from
(send_command_low_level(&card,
commands::get_response(), ext, oc)?)?;
let next = Response::try_from(send_command_low_level(
&card,
commands::get_response(),
ext,
oc,
)?)?;
// FIXME: first check for 0x61xx or 0x9000?
log::trace!(" appending {} bytes to response", next.raw_data().len());
@ -54,11 +64,12 @@ pub(crate) fn send_command(card: &Card, cmd: Command, ext: Le,
///
/// If the response is chained, this fn only returns one chunk, the caller
/// needs take care of chained responses
fn send_command_low_level(card: &Card,
cmd: Command,
ext: Le,
oc: Option<&OpenPGPCard>)
-> Result<Vec<u8>, OpenpgpCardError> {
fn send_command_low_level(
card: &Card,
cmd: Command,
ext: Le,
oc: Option<&OpenPGPCard>,
) -> Result<Vec<u8>, OpenpgpCardError> {
log::trace!(" -> full APDU command: {:x?}", cmd);
log::trace!(" serialized: {:x?}", cmd.serialize(ext));
@ -81,14 +92,18 @@ fn send_command_low_level(card: &Card,
}
}
log::trace!("ext le/lc {}, chaining {}, command chunk size {}",
ext_support, chaining_support, chunk_size);
log::trace!(
"ext le/lc {}, chaining {}, command chunk size {}",
ext_support,
chaining_support,
chunk_size
);
// update Le setting to 'long', if we're using a larger chunk size
let ext = match (ext, chunk_size > 0xff) {
(Le::None, _) => Le::None,
(_, true) => Le::Long,
_ => ext
_ => ext,
};
let buf_size = if !ext_support {
@ -109,21 +124,23 @@ fn send_command_low_level(card: &Card,
for (i, d) in chunks.iter().enumerate() {
let last = i == chunks.len() - 1;
let partial =
Command {
cla: if last { 0x00 } else { 0x10 },
data: d.to_vec(),
..cmd
};
let partial = Command {
cla: if last { 0x00 } else { 0x10 },
data: d.to_vec(),
..cmd
};
let serialized = partial.serialize(ext).
map_err(OpenpgpCardError::InternalError)?;
let serialized = partial
.serialize(ext)
.map_err(OpenpgpCardError::InternalError)?;
log::trace!(" -> chunked APDU command: {:x?}", &serialized);
let resp = card
.transmit(&serialized, &mut resp_buffer)
.map_err(|e| OpenpgpCardError::Smartcard(SmartcardError::Error(
format!("Transmit failed: {:?}", e))))?;
let resp =
card.transmit(&serialized, &mut resp_buffer).map_err(|e| {
OpenpgpCardError::Smartcard(SmartcardError::Error(
format!("Transmit failed: {:?}", e),
))
})?;
log::trace!(" <- APDU chunk response: {:x?}", &resp);
@ -139,8 +156,8 @@ fn send_command_low_level(card: &Card,
// ISO: "If SW1-SW2 is set to '6883', then the last
// command of the chain is expected."
if !((sw1 == 0x90 && sw2 == 0x00)
|| (sw1 == 0x68 && sw2 == 0x83)) {
|| (sw1 == 0x68 && sw2 == 0x83))
{
// Unexpected status for a non-final chunked response
return Err(OcErrorStatus::from((sw1, sw2)).into());
}
@ -156,11 +173,13 @@ fn send_command_low_level(card: &Card,
} else {
let serialized = cmd.serialize(ext)?;
let resp = card
.transmit(&serialized, &mut resp_buffer)
.map_err(|e| OpenpgpCardError::Smartcard(SmartcardError::Error(
format!("Transmit failed: {:?}", e))))?;
let resp =
card.transmit(&serialized, &mut resp_buffer).map_err(|e| {
OpenpgpCardError::Smartcard(SmartcardError::Error(format!(
"Transmit failed: {:?}",
e
)))
})?;
log::trace!(" <- APDU response: {:x?}", resp);

View file

@ -66,16 +66,17 @@ impl TryFrom<Vec<u8>> for Response {
type Error = OcErrorStatus;
fn try_from(mut data: Vec<u8>) -> Result<Self, OcErrorStatus> {
let sw2 = data.pop()
let sw2 = data
.pop()
.ok_or_else(|| OcErrorStatus::ResponseLength(data.len()))?;
let sw1 = data.pop()
let sw1 = data
.pop()
.ok_or_else(|| OcErrorStatus::ResponseLength(data.len()))?;
Ok(Response { data, sw1, sw2 })
}
}
impl Response {
/// Is the response (0x90 0x00)?
pub fn is_ok(&self) -> bool {

View file

@ -2,15 +2,16 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
use anyhow::Result;
use pcsc::{Card, Context, Scope, ShareMode, Protocols, Error};
use pcsc::{Card, Context, Error, Protocols, Scope, ShareMode};
use crate::errors;
pub fn get_cards() -> Result<Vec<Card>, errors::SmartcardError> {
let ctx = match Context::establish(Scope::User) {
Ok(ctx) => ctx,
Err(err) => return Err(errors::SmartcardError::ContextError(err.to_string())),
Err(err) => {
return Err(errors::SmartcardError::ContextError(err.to_string()))
}
};
// List available readers.
@ -32,7 +33,8 @@ pub fn get_cards() -> Result<Vec<Card>, errors::SmartcardError> {
found_reader = true;
// Try connecting to card in this reader
let card = match ctx.connect(reader, ShareMode::Shared, Protocols::ANY) {
let card = match ctx.connect(reader, ShareMode::Shared, Protocols::ANY)
{
Ok(card) => card,
Err(Error::NoSmartcard) => {
continue; // try next reader

View file

@ -100,17 +100,16 @@ pub enum OcErrorStatus {
#[error("Unexpected response length: {0}")]
ResponseLength(usize),
}
impl From<(u8, u8)> for OcErrorStatus {
fn from(status: (u8, u8)) -> Self {
match (status.0, status.1) {
(0x62, 0x85) => OcErrorStatus::TerminationState,
(0x63, 0xC0..=0xCF) =>
OcErrorStatus::PasswordNotChecked(status.1 & 0xf),
(0x64, 0x02..=0x80) =>
OcErrorStatus::TriggeringByCard(status.1),
(0x63, 0xC0..=0xCF) => {
OcErrorStatus::PasswordNotChecked(status.1 & 0xf)
}
(0x64, 0x02..=0x80) => OcErrorStatus::TriggeringByCard(status.1),
(0x65, 0x01) => OcErrorStatus::MemoryFailure,
(0x66, 0x00) => OcErrorStatus::SecurityRelatedIssues,
(0x67, 0x00) => OcErrorStatus::WrongLength,
@ -130,7 +129,7 @@ impl From<(u8, u8)> for OcErrorStatus {
(0x6D, 0x00) => OcErrorStatus::INSNotSupported,
(0x6E, 0x00) => OcErrorStatus::CLANotSupported,
(0x6F, 0x00) => OcErrorStatus::NoPreciseDiagnosis,
_ => OcErrorStatus::UnknownStatus(status.0, status.1)
_ => OcErrorStatus::UnknownStatus(status.0, status.1),
}
}
}

View file

@ -3,17 +3,18 @@
use anyhow::{anyhow, Result};
use crate::{KeyType, OpenPGPCardAdmin, tlv,
CardUploadableKey, EccKey, RSAKey, EccType, PrivateKeyMaterial};
use crate::apdu;
use crate::apdu::commands;
use crate::apdu::command::Command;
use crate::apdu::commands;
use crate::apdu::Le;
use crate::errors::OpenpgpCardError;
use crate::parse::algo_attrs::{Algo, RsaAttrs};
use crate::parse::algo_info::AlgoInfo;
use crate::tlv::{Tlv, TlvEntry, tag::Tag};
use crate::errors::OpenpgpCardError;
use crate::tlv::{tag::Tag, Tlv, TlvEntry};
use crate::{
tlv, CardUploadableKey, EccKey, EccType, KeyType, OpenPGPCardAdmin,
PrivateKeyMaterial, RSAKey,
};
/// Upload an explicitly selected Key to the card as a specific KeyType.
///
@ -27,56 +28,66 @@ pub(crate) fn upload_key(
// be cached
let algo_list = oca.list_supported_algo()?;
let (algo_cmd, key_cmd) =
match key.get_key()? {
PrivateKeyMaterial::R(rsa_key) => {
// RSA bitsize
// (round up to 4-bytes, in case the key has 8+ leading zeros)
let rsa_bits =
(((rsa_key.get_n().len() * 8 + 31) / 32) * 32) as u16;
let (algo_cmd, key_cmd) = match key.get_key()? {
PrivateKeyMaterial::R(rsa_key) => {
// RSA bitsize
// (round up to 4-bytes, in case the key has 8+ leading zeros)
let rsa_bits =
(((rsa_key.get_n().len() * 8 + 31) / 32) * 32) as u16;
// FIXME: deal with absence of algo list (unwrap!)
// Get suitable algorithm from card's list
let algo = get_card_algo_rsa(algo_list.unwrap(), key_type, rsa_bits);
// FIXME: deal with absence of algo list (unwrap!)
// Get suitable algorithm from card's list
let algo =
get_card_algo_rsa(algo_list.unwrap(), key_type, rsa_bits);
let algo_cmd = rsa_algo_attrs_cmd(key_type, rsa_bits, &algo)?;
let key_cmd = rsa_key_cmd(key_type, rsa_key, &algo)?;
let algo_cmd = rsa_algo_attrs_cmd(key_type, rsa_bits, &algo)?;
let key_cmd = rsa_key_cmd(key_type, rsa_key, &algo)?;
// Return commands
(algo_cmd, key_cmd)
}
PrivateKeyMaterial::E(ecc_key) => {
// Initially there were checks of the following form, here.
// However, some cards seem to report erroneous
// information about supported algorithms.
// (e.g. Yk5 reports support of EdDSA over Cv25519/Ed25519,
// but not ECDH).
// Return commands
(algo_cmd, key_cmd)
}
PrivateKeyMaterial::E(ecc_key) => {
// Initially there were checks of the following form, here.
// However, some cards seem to report erroneous
// information about supported algorithms.
// (e.g. Yk5 reports support of EdDSA over Cv25519/Ed25519,
// but not ECDH).
// if !check_card_algo_*(algo_list.unwrap(),
// key_type, ecc_key.get_oid()) {
// // Error
// }
// if !check_card_algo_*(algo_list.unwrap(),
// key_type, ecc_key.get_oid()) {
// // Error
// }
let algo_cmd = ecc_algo_attrs_cmd(key_type,
ecc_key.get_oid(),
ecc_key.get_type());
let algo_cmd = ecc_algo_attrs_cmd(
key_type,
ecc_key.get_oid(),
ecc_key.get_type(),
);
let key_cmd = ecc_key_cmd(ecc_key, key_type)?;
let key_cmd = ecc_key_cmd(ecc_key, key_type)?;
(algo_cmd, key_cmd)
}
};
(algo_cmd, key_cmd)
}
};
copy_key_to_card(oca, key_type, key.get_ts(), key.get_fp(), algo_cmd,
key_cmd)?;
copy_key_to_card(
oca,
key_type,
key.get_ts(),
key.get_fp(),
algo_cmd,
key_cmd,
)?;
Ok(())
}
// FIXME: refactor, these checks currently pointlessly duplicate code
fn get_card_algo_rsa(algo_list: AlgoInfo, key_type: KeyType, rsa_bits: u16)
-> RsaAttrs {
fn get_card_algo_rsa(
algo_list: AlgoInfo,
key_type: KeyType,
rsa_bits: u16,
) -> RsaAttrs {
// Find suitable algorithm parameters (from card's list of algorithms).
// FIXME: handle "no list available" (older cards?)
// (Current algo parameters of the key slot should be used, then (?))
@ -84,22 +95,15 @@ fn get_card_algo_rsa(algo_list: AlgoInfo, key_type: KeyType, rsa_bits: u16)
// Get Algos for this keytype
let keytype_algos: Vec<_> = algo_list.get_by_keytype(key_type);
// Get RSA algo attributes
let rsa_algos: Vec<_> = keytype_algos.iter()
.map(|a|
{
if let Algo::Rsa(r) = a {
Some(r)
} else {
None
}
})
let rsa_algos: Vec<_> = keytype_algos
.iter()
.map(|a| if let Algo::Rsa(r) = a { Some(r) } else { None })
.flatten()
.collect();
// Filter card algorithms by rsa bitlength of the key we want to upload
let algo: Vec<_> = rsa_algos.iter()
.filter(|&a| a.len_n == rsa_bits)
.collect();
let algo: Vec<_> =
rsa_algos.iter().filter(|&a| a.len_n == rsa_bits).collect();
// FIXME: handle error if no algo found
let algo = *algo[0];
@ -108,7 +112,11 @@ fn get_card_algo_rsa(algo_list: AlgoInfo, key_type: KeyType, rsa_bits: u16)
}
// FIXME: refactor, these checks currently pointlessly duplicate code
fn check_card_algo_ecdh(algo_list: AlgoInfo, key_type: KeyType, oid: &[u8]) -> bool {
fn check_card_algo_ecdh(
algo_list: AlgoInfo,
key_type: KeyType,
oid: &[u8],
) -> bool {
// Find suitable algorithm parameters (from card's list of algorithms).
// FIXME: handle "no list available" (older cards?)
// (Current algo parameters of the key slot should be used, then (?))
@ -117,15 +125,9 @@ fn check_card_algo_ecdh(algo_list: AlgoInfo, key_type: KeyType, oid: &[u8]) -> b
let keytype_algos: Vec<_> = algo_list.get_by_keytype(key_type);
// Get attributes
let ecdh_algos: Vec<_> = keytype_algos.iter()
.map(|a|
{
if let Algo::Ecdh(e) = a {
Some(e)
} else {
None
}
})
let ecdh_algos: Vec<_> = keytype_algos
.iter()
.map(|a| if let Algo::Ecdh(e) = a { Some(e) } else { None })
.flatten()
.collect();
@ -134,8 +136,11 @@ fn check_card_algo_ecdh(algo_list: AlgoInfo, key_type: KeyType, oid: &[u8]) -> b
}
// FIXME: refactor, these checks currently pointlessly duplicate code
fn check_card_algo_ecdsa(algo_list: AlgoInfo,
key_type: KeyType, oid: &[u8]) -> bool {
fn check_card_algo_ecdsa(
algo_list: AlgoInfo,
key_type: KeyType,
oid: &[u8],
) -> bool {
// Find suitable algorithm parameters (from card's list of algorithms).
// FIXME: handle "no list available" (older cards?)
// (Current algo parameters of the key slot should be used, then (?))
@ -144,15 +149,15 @@ fn check_card_algo_ecdsa(algo_list: AlgoInfo,
let keytype_algos: Vec<_> = algo_list.get_by_keytype(key_type);
// Get attributes
let ecdsa_algos: Vec<_> = keytype_algos.iter()
.map(|a|
{
if let Algo::Ecdsa(e) = a {
Some(e)
} else {
None
}
})
let ecdsa_algos: Vec<_> = keytype_algos
.iter()
.map(|a| {
if let Algo::Ecdsa(e) = a {
Some(e)
} else {
None
}
})
.flatten()
.collect();
@ -161,8 +166,11 @@ fn check_card_algo_ecdsa(algo_list: AlgoInfo,
}
// FIXME: refactor, these checks currently pointlessly duplicate code
fn check_card_algo_eddsa(algo_list: AlgoInfo,
key_type: KeyType, oid: &[u8]) -> bool {
fn check_card_algo_eddsa(
algo_list: AlgoInfo,
key_type: KeyType,
oid: &[u8],
) -> bool {
// Find suitable algorithm parameters (from card's list of algorithms).
// FIXME: handle "no list available" (older cards?)
// (Current algo parameters of the key slot should be used, then (?))
@ -171,15 +179,15 @@ fn check_card_algo_eddsa(algo_list: AlgoInfo,
let keytype_algos: Vec<_> = algo_list.get_by_keytype(key_type);
// Get attributes
let eddsa_algos: Vec<_> = keytype_algos.iter()
.map(|a|
{
if let Algo::Eddsa(e) = a {
Some(e)
} else {
None
}
})
let eddsa_algos: Vec<_> = keytype_algos
.iter()
.map(|a| {
if let Algo::Eddsa(e) = a {
Some(e)
} else {
None
}
})
.flatten()
.collect();
@ -187,8 +195,10 @@ fn check_card_algo_eddsa(algo_list: AlgoInfo,
eddsa_algos.iter().any(|e| e.oid == oid)
}
fn ecc_key_cmd(ecc_key: Box<dyn EccKey>, key_type: KeyType)
-> Result<Command, OpenpgpCardError> {
fn ecc_key_cmd(
ecc_key: Box<dyn EccKey>,
key_type: KeyType,
) -> Result<Command, OpenpgpCardError> {
let scalar_data = ecc_key.get_scalar();
let scalar_len = scalar_data.len() as u8;
@ -196,23 +206,24 @@ fn ecc_key_cmd(ecc_key: Box<dyn EccKey>, key_type: KeyType)
let crt = get_crt(key_type)?;
// 2) "Cardholder private key template" (7F48)
let cpkt = Tlv(Tag(vec![0x7F, 0x48]),
TlvEntry::S(vec![0x92, scalar_len]));
let cpkt = Tlv(Tag(vec![0x7F, 0x48]), TlvEntry::S(vec![0x92, scalar_len]));
// 3) "Cardholder private key" (5F48)
let cpk = Tlv(Tag(vec![0x5F, 0x48]), TlvEntry::S(scalar_data.to_vec()));
// "Extended header list (DO 4D)" (contains the three inner TLV)
let ehl = Tlv(Tag(vec![0x4d]),
TlvEntry::C(vec![crt, cpkt, cpk]));
let ehl = Tlv(Tag(vec![0x4d]), TlvEntry::C(vec![crt, cpkt, cpk]));
// The key import uses a PUT DATA command with odd INS (DB) and an
// Extended header list (DO 4D) as described in ISO 7816-8
Ok(Command::new(0x00, 0xDB, 0x3F, 0xFF,
ehl.serialize().to_vec()))
Ok(Command::new(
0x00,
0xDB,
0x3F,
0xFF,
ehl.serialize().to_vec(),
))
}
fn get_crt(key_type: KeyType) -> Result<Tlv, OpenpgpCardError> {
@ -221,22 +232,25 @@ fn get_crt(key_type: KeyType) -> Result<Tlv, OpenpgpCardError> {
KeyType::Decryption => 0xB8,
KeyType::Signing => 0xB6,
KeyType::Authentication => 0xA4,
_ => return Err(OpenpgpCardError::InternalError
(anyhow!("Unexpected KeyType")))
_ => {
return Err(OpenpgpCardError::InternalError(anyhow!(
"Unexpected KeyType"
)))
}
};
Ok(Tlv(Tag(vec![tag]), TlvEntry::S(vec![])))
}
fn rsa_key_cmd(key_type: KeyType,
rsa_key: Box<dyn RSAKey>,
algo_attrs: &RsaAttrs) -> Result<Command, OpenpgpCardError> {
fn rsa_key_cmd(
key_type: KeyType,
rsa_key: Box<dyn RSAKey>,
algo_attrs: &RsaAttrs,
) -> Result<Command, OpenpgpCardError> {
// Assemble key command, which contains three sub-TLV:
// 1) "Control Reference Template"
let crt = get_crt(key_type)?;
// 2) "Cardholder private key template" (7F48)
// "describes the input and the length of the content of the following DO"
@ -265,7 +279,6 @@ fn rsa_key_cmd(key_type: KeyType,
let cpkt = Tlv(Tag(vec![0x7F, 0x48]), TlvEntry::S(value));
// 3) "Cardholder private key" (5F48)
//
// "represents a concatenation of the key data elements according to
@ -287,24 +300,27 @@ fn rsa_key_cmd(key_type: KeyType,
let cpk = Tlv(Tag(vec![0x5F, 0x48]), TlvEntry::S(keydata));
// "Extended header list (DO 4D)"
let ehl = Tlv(Tag(vec![0x4d]), TlvEntry::C(vec![crt, cpkt, cpk]));
// The key import uses a PUT DATA command with odd INS (DB) and an
// Extended header list (DO 4D) as described in ISO 7816-8
Ok(Command::new(0x00, 0xDB, 0x3F, 0xFF,
ehl.serialize().to_vec()))
Ok(Command::new(
0x00,
0xDB,
0x3F,
0xFF,
ehl.serialize().to_vec(),
))
}
/// Set algorithm attributes [4.4.3.9 Algorithm Attributes]
fn rsa_algo_attrs_cmd(key_type: KeyType,
rsa_bits: u16,
algo_attrs: &RsaAttrs) ->
Result<Command> {
fn rsa_algo_attrs_cmd(
key_type: KeyType,
rsa_bits: u16,
algo_attrs: &RsaAttrs,
) -> Result<Command> {
// Algorithm ID (01 = RSA (Encrypt or Sign))
let mut algo_attributes = vec![0x01];
@ -318,19 +334,26 @@ fn rsa_algo_attrs_cmd(key_type: KeyType,
// Import-Format of private key
// (This fn currently assumes import_format "00 = standard (e, p, q)")
if algo_attrs.import_format != 0 {
return Err(
anyhow!("Unexpected RSA input format (only 0 is supported)"));
return Err(anyhow!(
"Unexpected RSA input format (only 0 is supported)"
));
}
algo_attributes.push(algo_attrs.import_format);
// Command to PUT the algorithm attributes
Ok(commands::put_data(&[key_type.get_algorithm_tag()], algo_attributes))
Ok(commands::put_data(
&[key_type.get_algorithm_tag()],
algo_attributes,
))
}
/// Set algorithm attributes [4.4.3.9 Algorithm Attributes]
fn ecc_algo_attrs_cmd(key_type: KeyType, oid: &[u8], ecc_type: EccType)
-> Command {
fn ecc_algo_attrs_cmd(
key_type: KeyType,
oid: &[u8],
ecc_type: EccType,
) -> Command {
let algo_id = match ecc_type {
EccType::EdDSA => 0x16,
EccType::ECDH => 0x12,
@ -345,15 +368,15 @@ fn ecc_algo_attrs_cmd(key_type: KeyType, oid: &[u8], ecc_type: EccType)
commands::put_data(&[key_type.get_algorithm_tag()], algo_attributes)
}
fn copy_key_to_card(oca: &OpenPGPCardAdmin,
key_type: KeyType,
ts: u64,
fp: Vec<u8>,
algo_cmd: Command,
key_cmd: Command)
-> Result<(), OpenpgpCardError> {
let fp_cmd =
commands::put_data(&[key_type.get_fingerprint_put_tag()], fp);
fn copy_key_to_card(
oca: &OpenPGPCardAdmin,
key_type: KeyType,
ts: u64,
fp: Vec<u8>,
algo_cmd: Command,
key_cmd: Command,
) -> Result<(), OpenpgpCardError> {
let fp_cmd = commands::put_data(&[key_type.get_fingerprint_put_tag()], fp);
// Timestamp update
let time_value: Vec<u8> = ts
@ -366,7 +389,6 @@ fn copy_key_to_card(oca: &OpenPGPCardAdmin,
let time_cmd =
commands::put_data(&[key_type.get_timestamp_put_tag()], time_value);
// Send all the commands
let ext = Le::None; // FIXME?!

View file

@ -6,17 +6,13 @@ use std::convert::TryFrom;
use anyhow::{anyhow, Result};
use pcsc::*;
use apdu::{commands, Le, response::Response};
use parse::{algo_attrs::Algo,
algo_info::AlgoInfo,
application_id::ApplicationId,
cardholder::CardHolder,
extended_cap::ExtendedCap,
extended_cap::Features,
extended_length_info::ExtendedLengthInfo,
fingerprint,
historical::Historical,
KeySet};
use apdu::{commands, response::Response, Le};
use parse::{
algo_attrs::Algo, algo_info::AlgoInfo, application_id::ApplicationId,
cardholder::CardHolder, extended_cap::ExtendedCap, extended_cap::Features,
extended_length_info::ExtendedLengthInfo, fingerprint,
historical::Historical, KeySet,
};
use tlv::Tlv;
use crate::errors::{OpenpgpCardError, SmartcardError};
@ -24,14 +20,13 @@ use crate::tlv::tag::Tag;
use crate::tlv::TlvEntry;
use std::ops::Deref;
pub mod errors;
mod apdu;
mod card;
pub mod errors;
mod key_upload;
mod parse;
mod tlv;
pub enum Hash<'a> {
SHA256([u8; 0x20]),
SHA384([u8; 0x30]),
@ -42,13 +37,16 @@ pub enum Hash<'a> {
impl Hash<'_> {
fn oid(&self) -> Option<&'static [u8]> {
match self {
Self::SHA256(_) =>
Some(&[0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01]),
Self::SHA384(_) =>
Some(&[0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02]),
Self::SHA512(_) =>
Some(&[0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03]),
Self::EdDSA(_) => None
Self::SHA256(_) => {
Some(&[0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01])
}
Self::SHA384(_) => {
Some(&[0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02])
}
Self::SHA512(_) => {
Some(&[0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03])
}
Self::EdDSA(_) => None,
}
}
@ -57,12 +55,11 @@ impl Hash<'_> {
Self::SHA256(d) => &d[..],
Self::SHA384(d) => &d[..],
Self::SHA512(d) => &d[..],
Self::EdDSA(d) => d
Self::EdDSA(d) => d,
}
}
}
/// A PGP-implementation-agnostic wrapper for private key data, to upload
/// to an OpenPGP card
pub trait CardUploadableKey {
@ -116,9 +113,13 @@ pub enum DecryptMe<'a> {
ECDH(&'a [u8]),
}
#[derive(Debug)]
pub enum Sex { NotKnown, Male, Female, NotApplicable }
pub enum Sex {
NotKnown,
Male,
Female,
NotApplicable,
}
impl Sex {
pub fn as_u8(&self) -> u8 {
@ -142,7 +143,6 @@ impl From<u8> for Sex {
}
}
/// Enum to identify one of the Key-slots on an OpenPGP card
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum KeyType {
@ -219,17 +219,24 @@ impl OpenPGPCard {
/// Get all cards that can be opened as an OpenPGP card applet
pub fn list_cards() -> Result<Vec<Self>> {
let cards = card::get_cards().map_err(|err| anyhow!(err))?;
let ocs: Vec<_> = cards.into_iter().map(Self::open_card)
.map(|oc| oc.ok()).flatten().collect();
let ocs: Vec<_> = cards
.into_iter()
.map(Self::open_card)
.map(|oc| oc.ok())
.flatten()
.collect();
Ok(ocs)
}
/// Find an OpenPGP card by serial number and return it.
pub fn open_by_serial(serial: &str) -> Result<Self, OpenpgpCardError> {
let cards = card::get_cards()
.map_err(|e| OpenpgpCardError::Smartcard(
SmartcardError::Error(format!("{:?}", e))))?;
let cards = card::get_cards().map_err(|e| {
OpenpgpCardError::Smartcard(SmartcardError::Error(format!(
"{:?}",
e
)))
})?;
for card in cards {
let res = Self::open_card(card);
@ -248,9 +255,12 @@ impl OpenPGPCard {
/// Open connection to some card and select the openpgp applet
pub fn open_yolo() -> Result<Self, OpenpgpCardError> {
let mut cards = card::get_cards()
.map_err(|e| OpenpgpCardError::Smartcard(
SmartcardError::Error(format!("{:?}", e))))?;
let mut cards = card::get_cards().map_err(|e| {
OpenpgpCardError::Smartcard(SmartcardError::Error(format!(
"{:?}",
e
)))
})?;
// randomly use the first card in the list
let card = cards.swap_remove(0);
@ -262,8 +272,7 @@ impl OpenPGPCard {
fn open_card(card: Card) -> Result<Self, OpenpgpCardError> {
let select_openpgp = commands::select_openpgp();
let resp =
apdu::send_command(&card, select_openpgp, Le::Short, None)?;
let resp = apdu::send_command(&card, select_openpgp, Le::Short, None)?;
if resp.is_ok() {
// read and cache "application related data"
@ -314,7 +323,9 @@ impl OpenPGPCard {
}
}
pub fn get_extended_length_information(&self) -> Result<Option<ExtendedLengthInfo>> {
pub fn get_extended_length_information(
&self,
) -> Result<Option<ExtendedLengthInfo>> {
// get from cached "application related data"
let eli = self.ard.find(&Tag::from([0x7F, 0x66]));
@ -337,7 +348,9 @@ impl OpenPGPCard {
unimplemented!()
}
pub fn get_extended_capabilities(&self) -> Result<ExtendedCap, OpenpgpCardError> {
pub fn get_extended_capabilities(
&self,
) -> Result<ExtendedCap, OpenpgpCardError> {
// get from cached "application related data"
let ecap = self.ard.find(&Tag::from([0xc0]));
@ -355,8 +368,10 @@ impl OpenPGPCard {
if let Some(aa) = aa {
Algo::try_from(&aa.serialize()[..])
} else {
Err(anyhow!("Failed to get algorithm attributes for {:?}.",
key_type))
Err(anyhow!(
"Failed to get algorithm attributes for {:?}.",
key_type
))
}
}
@ -364,7 +379,9 @@ impl OpenPGPCard {
unimplemented!()
}
pub fn get_fingerprints(&self) -> Result<KeySet<fingerprint::Fingerprint>, OpenpgpCardError> {
pub fn get_fingerprints(
&self,
) -> Result<KeySet<fingerprint::Fingerprint>, OpenpgpCardError> {
// Get from cached "application related data"
let fp = self.ard.find(&Tag::from([0xc5]));
@ -416,10 +433,18 @@ impl OpenPGPCard {
let _eli = self.get_extended_length_information()?;
// FIXME: figure out Le
let resp = apdu::send_command(&self.card, commands::get_url(), Le::Long, Some(self))?;
let resp = apdu::send_command(
&self.card,
commands::get_url(),
Le::Long,
Some(self),
)?;
log::trace!(" final response: {:x?}, data len {}",
resp, resp.raw_data().len());
log::trace!(
" final response: {:x?}, data len {}",
resp,
resp.raw_data().len()
);
Ok(String::from_utf8_lossy(resp.data()?).to_string())
}
@ -443,8 +468,11 @@ impl OpenPGPCard {
let resp = apdu::send_command(&self.card, sst, ext, Some(self))?;
resp.check_ok()?;
log::trace!(" final response: {:x?}, data len {}",
resp, resp.data()?.len());
log::trace!(
" final response: {:x?}, data len {}",
resp,
resp.data()?.len()
);
Tlv::try_from(resp.data()?)
}
@ -460,7 +488,12 @@ impl OpenPGPCard {
return Ok(None);
}
let resp = apdu::send_command(&self.card, commands::get_algo_list(), Le::Short, Some(self))?;
let resp = apdu::send_command(
&self.card,
commands::get_algo_list(),
Le::Short,
Some(self),
)?;
resp.check_ok()?;
let ai = AlgoInfo::try_from(resp.data()?)?;
@ -475,9 +508,11 @@ impl OpenPGPCard {
// [apdu 00 20 00 81 08 40 40 40 40 40 40 40 40]
for _ in 0..4 {
let verify = commands::verify_pw1_81([0x40; 8].to_vec());
let resp = apdu::send_command(&self.card, verify, Le::None, Some(self))?;
let resp =
apdu::send_command(&self.card, verify, Le::None, Some(self))?;
if !(resp.status() == [0x69, 0x82]
|| resp.status() == [0x69, 0x83]) {
|| resp.status() == [0x69, 0x83])
{
return Err(anyhow!("Unexpected status for reset, at pw1."));
}
}
@ -486,10 +521,12 @@ impl OpenPGPCard {
// [apdu 00 20 00 83 08 40 40 40 40 40 40 40 40]
for _ in 0..4 {
let verify = commands::verify_pw3([0x40; 8].to_vec());
let resp = apdu::send_command(&self.card, verify, Le::None, Some(self))?;
let resp =
apdu::send_command(&self.card, verify, Le::None, Some(self))?;
if !(resp.status() == [0x69, 0x82]
|| resp.status() == [0x69, 0x83]) {
|| resp.status() == [0x69, 0x83])
{
return Err(anyhow!("Unexpected status for reset, at pw3."));
}
}
@ -510,8 +547,10 @@ impl OpenPGPCard {
Ok(())
}
pub fn verify_pw1_81(self, pin: &str)
-> Result<OpenPGPCardUser, OpenPGPCard> {
pub fn verify_pw1_81(
self,
pin: &str,
) -> Result<OpenPGPCardUser, OpenPGPCard> {
assert!(pin.len() >= 6); // FIXME: Err
let verify = commands::verify_pw1_81(pin.as_bytes().to_vec());
@ -527,8 +566,10 @@ impl OpenPGPCard {
Err(self)
}
pub fn verify_pw1_82(self, pin: &str)
-> Result<OpenPGPCardUser, OpenPGPCard> {
pub fn verify_pw1_82(
self,
pin: &str,
) -> Result<OpenPGPCardUser, OpenPGPCard> {
assert!(pin.len() >= 6); // FIXME: Err
let verify = commands::verify_pw1_82(pin.as_bytes().to_vec());
@ -544,7 +585,10 @@ impl OpenPGPCard {
Err(self)
}
pub fn verify_pw3(self, pin: &str) -> Result<OpenPGPCardAdmin, OpenPGPCard> {
pub fn verify_pw3(
self,
pin: &str,
) -> Result<OpenPGPCardAdmin, OpenPGPCard> {
assert!(pin.len() >= 8); // FIXME: Err
let verify = commands::verify_pw3(pin.as_bytes().to_vec());
@ -561,7 +605,6 @@ impl OpenPGPCard {
}
}
/// An OpenPGP card after successful verification of PW1 (needs to be split
/// further to model authentication for signing)
pub struct OpenPGPCardUser {
@ -579,8 +622,7 @@ impl Deref for OpenPGPCardUser {
impl OpenPGPCardUser {
/// Decrypt the ciphertext in `dm`, on the card.
pub fn decrypt(&self, dm: DecryptMe)
-> Result<Vec<u8>, OpenpgpCardError> {
pub fn decrypt(&self, dm: DecryptMe) -> Result<Vec<u8>, OpenpgpCardError> {
match dm {
DecryptMe::RSA(message) => {
let mut data = vec![0x0];
@ -591,16 +633,13 @@ impl OpenPGPCardUser {
}
DecryptMe::ECDH(eph) => {
// External Public Key
let epk = Tlv(Tag(vec![0x86]),
TlvEntry::S(eph.to_vec()));
let epk = Tlv(Tag(vec![0x86]), TlvEntry::S(eph.to_vec()));
// Public Key DO
let pkdo = Tlv(Tag(vec![0x7f, 0x49]),
TlvEntry::C(vec![epk]));
let pkdo = Tlv(Tag(vec![0x7f, 0x49]), TlvEntry::C(vec![epk]));
// Cipher DO
let cdo = Tlv(Tag(vec![0xa6]),
TlvEntry::C(vec![pkdo]));
let cdo = Tlv(Tag(vec![0xa6]), TlvEntry::C(vec![pkdo]));
self.pso_decipher(cdo.serialize())
}
@ -609,57 +648,69 @@ impl OpenPGPCardUser {
/// Run decryption operation on the smartcard
/// (7.2.11 PSO: DECIPHER)
pub(crate) fn pso_decipher(&self, data: Vec<u8>)
-> Result<Vec<u8>, OpenpgpCardError> {
pub(crate) fn pso_decipher(
&self,
data: Vec<u8>,
) -> Result<Vec<u8>, OpenpgpCardError> {
// The OpenPGP card is already connected and PW1 82 has been verified
let dec_cmd = commands::decryption(data);
let resp = apdu::send_command(&self.card, dec_cmd, Le::Short, Some(self))?;
let resp =
apdu::send_command(&self.card, dec_cmd, Le::Short, Some(self))?;
resp.check_ok()?;
Ok(resp.data().map(|d| d.to_vec())?)
}
/// Sign the message in `hash`, on the card.
pub fn signature_for_hash(&self, hash: Hash)
-> Result<Vec<u8>, OpenpgpCardError> {
pub fn signature_for_hash(
&self,
hash: Hash,
) -> Result<Vec<u8>, OpenpgpCardError> {
match hash {
Hash::SHA256(_) | Hash::SHA384(_) | Hash::SHA512(_) => {
let tlv = Tlv(Tag(vec![0x30]),
TlvEntry::C(
vec![Tlv(Tag(vec![0x30]),
TlvEntry::C(
vec![Tlv(Tag(vec![0x06]),
// unwrapping is
// ok, for SHA*
TlvEntry::S(hash.oid().unwrap().to_vec())),
Tlv(Tag(vec![0x05]), TlvEntry::S(vec![]))
])),
Tlv(Tag(vec!(0x04)), TlvEntry::S(hash.digest().to_vec()))
]
));
let tlv = Tlv(
Tag(vec![0x30]),
TlvEntry::C(vec![
Tlv(
Tag(vec![0x30]),
TlvEntry::C(vec![
Tlv(
Tag(vec![0x06]),
// unwrapping is
// ok, for SHA*
TlvEntry::S(hash.oid().unwrap().to_vec()),
),
Tlv(Tag(vec![0x05]), TlvEntry::S(vec![])),
]),
),
Tlv(
Tag(vec![0x04]),
TlvEntry::S(hash.digest().to_vec()),
),
]),
);
Ok(self.compute_digital_signature(tlv.serialize())?)
}
Hash::EdDSA(d) => {
Ok(self.compute_digital_signature(d.to_vec())?)
}
Hash::EdDSA(d) => Ok(self.compute_digital_signature(d.to_vec())?),
}
}
/// Run signing operation on the smartcard
/// (7.2.10 PSO: COMPUTE DIGITAL SIGNATURE)
pub(crate) fn compute_digital_signature(&self, data: Vec<u8>)
-> Result<Vec<u8>, OpenpgpCardError> {
pub(crate) fn compute_digital_signature(
&self,
data: Vec<u8>,
) -> Result<Vec<u8>, OpenpgpCardError> {
let dec_cmd = commands::signature(data);
let resp = apdu::send_command(&self.card, dec_cmd, Le::Short, Some(self))?;
let resp =
apdu::send_command(&self.card, dec_cmd, Le::Short, Some(self))?;
Ok(resp.data().map(|d| d.to_vec())?)
}
}
/// An OpenPGP card after successful verification of PW3
pub struct OpenPGPCardAdmin {
oc: OpenPGPCard,
@ -732,26 +783,26 @@ impl OpenPGPCardAdmin {
}
}
#[cfg(test)]
mod test {
use super::tlv::{Tlv, TlvEntry};
use super::tlv::tag::Tag;
use super::tlv::{Tlv, TlvEntry};
#[test]
fn test_tlv() {
let cpkt =
Tlv(Tag(vec![0x7F, 0x48]),
TlvEntry::S(vec![0x91, 0x03,
0x92, 0x82, 0x01, 0x00,
0x93, 0x82, 0x01, 0x00]));
let cpkt = Tlv(
Tag(vec![0x7F, 0x48]),
TlvEntry::S(vec![
0x91, 0x03, 0x92, 0x82, 0x01, 0x00, 0x93, 0x82, 0x01, 0x00,
]),
);
assert_eq!(cpkt.serialize(),
vec![0x7F, 0x48,
0x0A,
0x91, 0x03,
0x92, 0x82, 0x01, 0x00,
0x93, 0x82, 0x01, 0x00,
]);
assert_eq!(
cpkt.serialize(),
vec![
0x7F, 0x48, 0x0A, 0x91, 0x03, 0x92, 0x82, 0x01, 0x00, 0x93,
0x82, 0x01, 0x00,
]
);
}
}

View file

@ -4,10 +4,10 @@
use std::convert::TryFrom;
use anyhow::Result;
use nom::{branch, bytes::complete as bytes, number::complete as number};
use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::combinator::map;
use nom::{branch, bytes::complete as bytes, number::complete as number};
use crate::parse;
@ -35,7 +35,11 @@ pub struct EcdsaAttrs {
impl EcdsaAttrs {
pub fn new(curve: Curve, import_format: Option<u8>) -> Self {
Self { curve, oid: curve.oid().to_vec(), import_format }
Self {
curve,
oid: curve.oid().to_vec(),
import_format,
}
}
}
@ -48,7 +52,11 @@ pub struct EddsaAttrs {
impl EddsaAttrs {
pub fn new(curve: Curve, import_format: Option<u8>) -> Self {
Self { curve, oid: curve.oid().to_vec(), import_format }
Self {
curve,
oid: curve.oid().to_vec(),
import_format,
}
}
}
@ -61,7 +69,11 @@ pub struct EcdhAttrs {
impl EcdhAttrs {
pub fn new(curve: Curve, import_format: Option<u8>) -> Self {
Self { curve, oid: curve.oid().to_vec(), import_format }
Self {
curve,
oid: curve.oid().to_vec(),
import_format,
}
}
}
@ -87,24 +99,26 @@ impl Curve {
NistP256r1 => &[0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07],
NistP384r1 => &[0x2B, 0x81, 0x04, 0x00, 0x22],
NistP521r1 => &[0x2B, 0x81, 0x04, 0x00, 0x23],
BrainpoolP256r1 =>
&[0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x07],
BrainpoolP384r1 =>
&[0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0b],
BrainpoolP512r1 =>
&[0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0d],
BrainpoolP256r1 => {
&[0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x07]
}
BrainpoolP384r1 => {
&[0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0b]
}
BrainpoolP512r1 => {
&[0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0d]
}
Secp256k1 => &[0x2B, 0x81, 0x04, 0x00, 0x0A],
Ed25519 =>
&[0x2B, 0x06, 0x01, 0x04, 0x01, 0xDA, 0x47, 0x0F, 0x01],
Cv25519 =>
&[0x2b, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01],
Ed25519 => &[0x2B, 0x06, 0x01, 0x04, 0x01, 0xDA, 0x47, 0x0F, 0x01],
Cv25519 => {
&[0x2b, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01]
}
Ed448 => &[0x2b, 0x65, 0x71],
X448 => &[0x2b, 0x65, 0x6f],
}
}
}
fn parse_oid_cv25519(input: &[u8]) -> nom::IResult<&[u8], Curve> {
map(tag(Curve::Cv25519.oid()), |_| Curve::Cv25519)(input)
}
@ -130,15 +144,21 @@ fn parse_oid_nist521(input: &[u8]) -> nom::IResult<&[u8], Curve> {
}
fn parse_oid_brainpool_p256r1(input: &[u8]) -> nom::IResult<&[u8], Curve> {
map(tag(Curve::BrainpoolP256r1.oid()), |_| Curve::BrainpoolP256r1)(input)
map(tag(Curve::BrainpoolP256r1.oid()), |_| {
Curve::BrainpoolP256r1
})(input)
}
fn parse_oid_brainpool_p384r1(input: &[u8]) -> nom::IResult<&[u8], Curve> {
map(tag(Curve::BrainpoolP384r1.oid()), |_| Curve::BrainpoolP384r1)(input)
map(tag(Curve::BrainpoolP384r1.oid()), |_| {
Curve::BrainpoolP384r1
})(input)
}
fn parse_oid_brainpool_p512r1(input: &[u8]) -> nom::IResult<&[u8], Curve> {
map(tag(Curve::BrainpoolP512r1.oid()), |_| Curve::BrainpoolP512r1)(input)
map(tag(Curve::BrainpoolP512r1.oid()), |_| {
Curve::BrainpoolP512r1
})(input)
}
fn parse_oid_ed448(input: &[u8]) -> nom::IResult<&[u8], Curve> {
@ -150,12 +170,19 @@ fn parse_oid_x448(input: &[u8]) -> nom::IResult<&[u8], Curve> {
}
fn parse_oid(input: &[u8]) -> nom::IResult<&[u8], Curve> {
alt((parse_oid_nist256, parse_oid_nist384, parse_oid_nist521,
parse_oid_brainpool_p256r1, parse_oid_brainpool_p384r1,
parse_oid_brainpool_p512r1,
parse_oid_secp256k1,
parse_oid_ed25519, parse_oid_cv25519,
parse_oid_ed448, parse_oid_x448))(input)
alt((
parse_oid_nist256,
parse_oid_nist384,
parse_oid_nist521,
parse_oid_brainpool_p256r1,
parse_oid_brainpool_p384r1,
parse_oid_brainpool_p512r1,
parse_oid_secp256k1,
parse_oid_ed25519,
parse_oid_cv25519,
parse_oid_ed448,
parse_oid_x448,
))(input)
}
fn parse_rsa(input: &[u8]) -> nom::IResult<&[u8], Algo> {
@ -165,7 +192,14 @@ fn parse_rsa(input: &[u8]) -> nom::IResult<&[u8], Algo> {
let (input, len_e) = number::be_u16(input)?;
let (input, import_format) = number::u8(input)?;
Ok((input, Algo::Rsa(RsaAttrs { len_n, len_e, import_format })))
Ok((
input,
Algo::Rsa(RsaAttrs {
len_n,
len_e,
import_format,
}),
))
}
fn parse_import_format(input: &[u8]) -> nom::IResult<&[u8], Option<u8>> {
@ -208,9 +242,7 @@ fn parse_eddsa(input: &[u8]) -> nom::IResult<&[u8], Algo> {
}
pub(crate) fn parse(input: &[u8]) -> nom::IResult<&[u8], Algo> {
branch::alt(
(parse_rsa, parse_ecdsa, parse_eddsa, parse_ecdh)
)(input)
branch::alt((parse_rsa, parse_ecdsa, parse_eddsa, parse_ecdh))(input)
}
impl TryFrom<&[u8]> for Algo {

View file

@ -4,18 +4,16 @@
use std::convert::TryFrom;
use anyhow::Result;
use nom::{branch, bytes::complete as bytes, combinator, multi, sequence};
use nom::branch::alt;
use nom::combinator::map;
use nom::{branch, bytes::complete as bytes, combinator, multi, sequence};
use crate::KeyType;
use crate::parse::algo_attrs;
use crate::parse::algo_attrs::Algo;
use crate::KeyType;
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct AlgoInfo(
Vec<(KeyType, Algo)>
);
pub struct AlgoInfo(Vec<(KeyType, Algo)>);
impl AlgoInfo {
pub fn get_by_keytype(&self, kt: KeyType) -> Vec<&Algo> {
@ -54,11 +52,11 @@ fn parse_list(input: &[u8]) -> nom::IResult<&[u8], Vec<(KeyType, Algo)>> {
}
fn parse_tl_list(input: &[u8]) -> nom::IResult<&[u8], Vec<(KeyType, Algo)>> {
let (input, (_, _, list)) =
sequence::tuple((
bytes::tag([0xfa]),
crate::tlv::length::length,
parse_list))(input)?;
let (input, (_, _, list)) = sequence::tuple((
bytes::tag([0xfa]),
crate::tlv::length::length,
parse_list,
))(input)?;
Ok((input, list))
}
@ -70,10 +68,10 @@ pub(self) fn parse(input: &[u8]) -> nom::IResult<&[u8], Vec<(KeyType, Algo)>> {
// -- Gnuk: do_alg_info (uint16_t tag, int with_tag)
branch::alt(
(combinator::all_consuming(parse_list),
combinator::all_consuming(parse_tl_list))
)(input)
branch::alt((
combinator::all_consuming(parse_list),
combinator::all_consuming(parse_tl_list),
))(input)
}
impl TryFrom<&[u8]> for AlgoInfo {
@ -84,228 +82,431 @@ impl TryFrom<&[u8]> for AlgoInfo {
}
}
// test
#[cfg(test)]
mod test {
use std::convert::TryFrom;
use crate::KeyType::*;
use crate::parse::algo_attrs::*;
use crate::parse::algo_attrs::Algo::*;
use crate::parse::algo_attrs::Curve::*;
use crate::parse::algo_attrs::*;
use crate::parse::algo_info::AlgoInfo;
use crate::KeyType::*;
#[test]
fn test_gnuk() {
let data = [0xc1, 0x6, 0x1, 0x8, 0x0, 0x0, 0x20, 0x0, 0xc1, 0x6,
0x1, 0x10, 0x0, 0x0, 0x20, 0x0, 0xc1, 0x9, 0x13, 0x2a, 0x86,
0x48, 0xce, 0x3d, 0x3, 0x1, 0x7, 0xc1, 0x6, 0x13, 0x2b, 0x81,
0x4, 0x0, 0xa, 0xc1, 0xa, 0x16, 0x2b, 0x6, 0x1, 0x4, 0x1,
0xda, 0x47, 0xf, 0x1, 0xc2, 0x6, 0x1, 0x8, 0x0, 0x0, 0x20,
0x0, 0xc2, 0x6, 0x1, 0x10, 0x0, 0x0, 0x20, 0x0, 0xc2, 0x9,
0x13, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x3, 0x1, 0x7, 0xc2, 0x6,
0x13, 0x2b, 0x81, 0x4, 0x0, 0xa, 0xc2, 0xb, 0x12, 0x2b, 0x6,
0x1, 0x4, 0x1, 0x97, 0x55, 0x1, 0x5, 0x1, 0xc3, 0x6, 0x1, 0x8,
0x0, 0x0, 0x20, 0x0, 0xc3, 0x6, 0x1, 0x10, 0x0, 0x0, 0x20,
0x0, 0xc3, 0x9, 0x13, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x3, 0x1,
0x7, 0xc3, 0x6, 0x13, 0x2b, 0x81, 0x4, 0x0, 0xa, 0xc3, 0xa,
0x16, 0x2b, 0x6, 0x1, 0x4, 0x1, 0xda, 0x47, 0xf, 0x1];
let data = [
0xc1, 0x6, 0x1, 0x8, 0x0, 0x0, 0x20, 0x0, 0xc1, 0x6, 0x1, 0x10,
0x0, 0x0, 0x20, 0x0, 0xc1, 0x9, 0x13, 0x2a, 0x86, 0x48, 0xce,
0x3d, 0x3, 0x1, 0x7, 0xc1, 0x6, 0x13, 0x2b, 0x81, 0x4, 0x0, 0xa,
0xc1, 0xa, 0x16, 0x2b, 0x6, 0x1, 0x4, 0x1, 0xda, 0x47, 0xf, 0x1,
0xc2, 0x6, 0x1, 0x8, 0x0, 0x0, 0x20, 0x0, 0xc2, 0x6, 0x1, 0x10,
0x0, 0x0, 0x20, 0x0, 0xc2, 0x9, 0x13, 0x2a, 0x86, 0x48, 0xce,
0x3d, 0x3, 0x1, 0x7, 0xc2, 0x6, 0x13, 0x2b, 0x81, 0x4, 0x0, 0xa,
0xc2, 0xb, 0x12, 0x2b, 0x6, 0x1, 0x4, 0x1, 0x97, 0x55, 0x1, 0x5,
0x1, 0xc3, 0x6, 0x1, 0x8, 0x0, 0x0, 0x20, 0x0, 0xc3, 0x6, 0x1,
0x10, 0x0, 0x0, 0x20, 0x0, 0xc3, 0x9, 0x13, 0x2a, 0x86, 0x48,
0xce, 0x3d, 0x3, 0x1, 0x7, 0xc3, 0x6, 0x13, 0x2b, 0x81, 0x4, 0x0,
0xa, 0xc3, 0xa, 0x16, 0x2b, 0x6, 0x1, 0x4, 0x1, 0xda, 0x47, 0xf,
0x1,
];
let ai = AlgoInfo::try_from(data.to_vec()).unwrap();
assert_eq!(
ai, AlgoInfo(
vec![
(Signing, Rsa(RsaAttrs { len_n: 2048, len_e: 32, import_format: 0 })),
(Signing, Rsa(RsaAttrs { len_n: 4096, len_e: 32, import_format: 0 })),
(Signing, Ecdsa(EcdsaAttrs::new(NistP256r1, None))),
(Signing, Ecdsa(EcdsaAttrs::new(Secp256k1, None))),
(Signing, Eddsa(EddsaAttrs::new(Ed25519, None))),
(Decryption, Rsa(RsaAttrs { len_n: 2048, len_e: 32, import_format: 0 })),
(Decryption, Rsa(RsaAttrs { len_n: 4096, len_e: 32, import_format: 0 })),
(Decryption, Ecdsa(EcdsaAttrs::new(NistP256r1, None))),
(Decryption, Ecdsa(EcdsaAttrs::new(Secp256k1, None))),
(Decryption, Ecdh(EcdhAttrs::new(Cv25519, None))),
(Authentication, Rsa(RsaAttrs { len_n: 2048, len_e: 32, import_format: 0 })),
(Authentication, Rsa(RsaAttrs { len_n: 4096, len_e: 32, import_format: 0 })),
(Authentication, Ecdsa(EcdsaAttrs::new(NistP256r1, None))),
(Authentication, Ecdsa(EcdsaAttrs::new(Secp256k1, None))),
(Authentication, Eddsa(EddsaAttrs::new(Ed25519, None)))
]
)
ai,
AlgoInfo(vec![
(
Signing,
Rsa(RsaAttrs {
len_n: 2048,
len_e: 32,
import_format: 0
})
),
(
Signing,
Rsa(RsaAttrs {
len_n: 4096,
len_e: 32,
import_format: 0
})
),
(Signing, Ecdsa(EcdsaAttrs::new(NistP256r1, None))),
(Signing, Ecdsa(EcdsaAttrs::new(Secp256k1, None))),
(Signing, Eddsa(EddsaAttrs::new(Ed25519, None))),
(
Decryption,
Rsa(RsaAttrs {
len_n: 2048,
len_e: 32,
import_format: 0
})
),
(
Decryption,
Rsa(RsaAttrs {
len_n: 4096,
len_e: 32,
import_format: 0
})
),
(Decryption, Ecdsa(EcdsaAttrs::new(NistP256r1, None))),
(Decryption, Ecdsa(EcdsaAttrs::new(Secp256k1, None))),
(Decryption, Ecdh(EcdhAttrs::new(Cv25519, None))),
(
Authentication,
Rsa(RsaAttrs {
len_n: 2048,
len_e: 32,
import_format: 0
})
),
(
Authentication,
Rsa(RsaAttrs {
len_n: 4096,
len_e: 32,
import_format: 0
})
),
(Authentication, Ecdsa(EcdsaAttrs::new(NistP256r1, None))),
(Authentication, Ecdsa(EcdsaAttrs::new(Secp256k1, None))),
(Authentication, Eddsa(EddsaAttrs::new(Ed25519, None)))
])
);
}
#[test]
fn test_opgp_card_34() {
let data = [0xc1, 0x6, 0x1, 0x8, 0x0, 0x0, 0x20, 0x0, 0xc1, 0x6,
0x1, 0xc, 0x0, 0x0, 0x20, 0x0, 0xc1, 0x6, 0x1, 0x10, 0x0,
0x0, 0x20, 0x0, 0xc1, 0x9, 0x13, 0x2a, 0x86, 0x48, 0xce,
0x3d, 0x3, 0x1, 0x7, 0xc1, 0x6, 0x13, 0x2b, 0x81, 0x4, 0x0,
0x22, 0xc1, 0x6, 0x13, 0x2b, 0x81, 0x4, 0x0, 0x23, 0xc1, 0xa,
0x13, 0x2b, 0x24, 0x3, 0x3, 0x2, 0x8, 0x1, 0x1, 0x7, 0xc1,
0xa, 0x13, 0x2b, 0x24, 0x3, 0x3, 0x2, 0x8, 0x1, 0x1, 0xb,
0xc1, 0xa, 0x13, 0x2b, 0x24, 0x3, 0x3, 0x2, 0x8, 0x1, 0x1,
0xd, 0xc2, 0x6, 0x1, 0x8, 0x0, 0x0, 0x20, 0x0, 0xc2, 0x6,
0x1, 0xc, 0x0, 0x0, 0x20, 0x0, 0xc2, 0x6, 0x1, 0x10, 0x0,
0x0, 0x20, 0x0, 0xc2, 0x9, 0x12, 0x2a, 0x86, 0x48, 0xce,
0x3d, 0x3, 0x1, 0x7, 0xc2, 0x6, 0x12, 0x2b, 0x81, 0x4, 0x0,
0x22, 0xc2, 0x6, 0x12, 0x2b, 0x81, 0x4, 0x0, 0x23, 0xc2, 0xa,
0x12, 0x2b, 0x24, 0x3, 0x3, 0x2, 0x8, 0x1, 0x1, 0x7, 0xc2,
0xa, 0x12, 0x2b, 0x24, 0x3, 0x3, 0x2, 0x8, 0x1, 0x1, 0xb,
0xc2, 0xa, 0x12, 0x2b, 0x24, 0x3, 0x3, 0x2, 0x8, 0x1, 0x1,
0xd, 0xc3, 0x6, 0x1, 0x8, 0x0, 0x0, 0x20, 0x0, 0xc3, 0x6, 0x1,
0xc, 0x0, 0x0, 0x20, 0x0, 0xc3, 0x6, 0x1, 0x10, 0x0, 0x0,
0x20, 0x0, 0xc3, 0x9, 0x13, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x3,
0x1, 0x7, 0xc3, 0x6, 0x13, 0x2b, 0x81, 0x4, 0x0, 0x22, 0xc3,
0x6, 0x13, 0x2b, 0x81, 0x4, 0x0, 0x23, 0xc3, 0xa, 0x13, 0x2b,
0x24, 0x3, 0x3, 0x2, 0x8, 0x1, 0x1, 0x7, 0xc3, 0xa, 0x13,
0x2b, 0x24, 0x3, 0x3, 0x2, 0x8, 0x1, 0x1, 0xb, 0xc3, 0xa,
0x13, 0x2b, 0x24, 0x3, 0x3, 0x2, 0x8, 0x1, 0x1, 0xd];
let data = [
0xc1, 0x6, 0x1, 0x8, 0x0, 0x0, 0x20, 0x0, 0xc1, 0x6, 0x1, 0xc,
0x0, 0x0, 0x20, 0x0, 0xc1, 0x6, 0x1, 0x10, 0x0, 0x0, 0x20, 0x0,
0xc1, 0x9, 0x13, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x3, 0x1, 0x7,
0xc1, 0x6, 0x13, 0x2b, 0x81, 0x4, 0x0, 0x22, 0xc1, 0x6, 0x13,
0x2b, 0x81, 0x4, 0x0, 0x23, 0xc1, 0xa, 0x13, 0x2b, 0x24, 0x3, 0x3,
0x2, 0x8, 0x1, 0x1, 0x7, 0xc1, 0xa, 0x13, 0x2b, 0x24, 0x3, 0x3,
0x2, 0x8, 0x1, 0x1, 0xb, 0xc1, 0xa, 0x13, 0x2b, 0x24, 0x3, 0x3,
0x2, 0x8, 0x1, 0x1, 0xd, 0xc2, 0x6, 0x1, 0x8, 0x0, 0x0, 0x20, 0x0,
0xc2, 0x6, 0x1, 0xc, 0x0, 0x0, 0x20, 0x0, 0xc2, 0x6, 0x1, 0x10,
0x0, 0x0, 0x20, 0x0, 0xc2, 0x9, 0x12, 0x2a, 0x86, 0x48, 0xce,
0x3d, 0x3, 0x1, 0x7, 0xc2, 0x6, 0x12, 0x2b, 0x81, 0x4, 0x0, 0x22,
0xc2, 0x6, 0x12, 0x2b, 0x81, 0x4, 0x0, 0x23, 0xc2, 0xa, 0x12,
0x2b, 0x24, 0x3, 0x3, 0x2, 0x8, 0x1, 0x1, 0x7, 0xc2, 0xa, 0x12,
0x2b, 0x24, 0x3, 0x3, 0x2, 0x8, 0x1, 0x1, 0xb, 0xc2, 0xa, 0x12,
0x2b, 0x24, 0x3, 0x3, 0x2, 0x8, 0x1, 0x1, 0xd, 0xc3, 0x6, 0x1,
0x8, 0x0, 0x0, 0x20, 0x0, 0xc3, 0x6, 0x1, 0xc, 0x0, 0x0, 0x20,
0x0, 0xc3, 0x6, 0x1, 0x10, 0x0, 0x0, 0x20, 0x0, 0xc3, 0x9, 0x13,
0x2a, 0x86, 0x48, 0xce, 0x3d, 0x3, 0x1, 0x7, 0xc3, 0x6, 0x13,
0x2b, 0x81, 0x4, 0x0, 0x22, 0xc3, 0x6, 0x13, 0x2b, 0x81, 0x4, 0x0,
0x23, 0xc3, 0xa, 0x13, 0x2b, 0x24, 0x3, 0x3, 0x2, 0x8, 0x1, 0x1,
0x7, 0xc3, 0xa, 0x13, 0x2b, 0x24, 0x3, 0x3, 0x2, 0x8, 0x1, 0x1,
0xb, 0xc3, 0xa, 0x13, 0x2b, 0x24, 0x3, 0x3, 0x2, 0x8, 0x1, 0x1,
0xd,
];
let ai = AlgoInfo::try_from(data.to_vec()).unwrap();
assert_eq!(
ai, AlgoInfo(
vec![
(Signing, Rsa(RsaAttrs { len_n: 2048, len_e: 32, import_format: 0 })),
(Signing, Rsa(RsaAttrs { len_n: 3072, len_e: 32, import_format: 0 })),
(Signing, Rsa(RsaAttrs { len_n: 4096, len_e: 32, import_format: 0 })),
(Signing, Ecdsa(EcdsaAttrs::new(NistP256r1, None))),
(Signing, Ecdsa(EcdsaAttrs::new(NistP384r1, None))),
(Signing, Ecdsa(EcdsaAttrs::new(NistP521r1, None))),
(Signing, Ecdsa(EcdsaAttrs::new(BrainpoolP256r1, None))),
(Signing, Ecdsa(EcdsaAttrs::new(BrainpoolP384r1, None))),
(Signing, Ecdsa(EcdsaAttrs::new(BrainpoolP512r1, None))),
(Decryption, Rsa(RsaAttrs { len_n: 2048, len_e: 32, import_format: 0 })),
(Decryption, Rsa(RsaAttrs { len_n: 3072, len_e: 32, import_format: 0 })),
(Decryption, Rsa(RsaAttrs { len_n: 4096, len_e: 32, import_format: 0 })),
(Decryption, Ecdh(EcdhAttrs::new(NistP256r1, None))),
(Decryption, Ecdh(EcdhAttrs::new(NistP384r1, None))),
(Decryption, Ecdh(EcdhAttrs::new(NistP521r1, None))),
(Decryption, Ecdh(EcdhAttrs::new(BrainpoolP256r1, None))),
(Decryption, Ecdh(EcdhAttrs::new(BrainpoolP384r1, None))),
(Decryption, Ecdh(EcdhAttrs::new(BrainpoolP512r1, None))),
(Authentication, Rsa(RsaAttrs { len_n: 2048, len_e: 32, import_format: 0 })),
(Authentication, Rsa(RsaAttrs { len_n: 3072, len_e: 32, import_format: 0 })),
(Authentication, Rsa(RsaAttrs { len_n: 4096, len_e: 32, import_format: 0 })),
(Authentication, Ecdsa(EcdsaAttrs::new(NistP256r1, None))),
(Authentication, Ecdsa(EcdsaAttrs::new(NistP384r1, None))),
(Authentication, Ecdsa(EcdsaAttrs::new(NistP521r1, None))),
(Authentication, Ecdsa(EcdsaAttrs::new(BrainpoolP256r1, None))),
(Authentication, Ecdsa(EcdsaAttrs::new(BrainpoolP384r1, None))),
(Authentication, Ecdsa(EcdsaAttrs::new(BrainpoolP512r1, None)))
]
)
ai,
AlgoInfo(vec![
(
Signing,
Rsa(RsaAttrs {
len_n: 2048,
len_e: 32,
import_format: 0
})
),
(
Signing,
Rsa(RsaAttrs {
len_n: 3072,
len_e: 32,
import_format: 0
})
),
(
Signing,
Rsa(RsaAttrs {
len_n: 4096,
len_e: 32,
import_format: 0
})
),
(Signing, Ecdsa(EcdsaAttrs::new(NistP256r1, None))),
(Signing, Ecdsa(EcdsaAttrs::new(NistP384r1, None))),
(Signing, Ecdsa(EcdsaAttrs::new(NistP521r1, None))),
(Signing, Ecdsa(EcdsaAttrs::new(BrainpoolP256r1, None))),
(Signing, Ecdsa(EcdsaAttrs::new(BrainpoolP384r1, None))),
(Signing, Ecdsa(EcdsaAttrs::new(BrainpoolP512r1, None))),
(
Decryption,
Rsa(RsaAttrs {
len_n: 2048,
len_e: 32,
import_format: 0
})
),
(
Decryption,
Rsa(RsaAttrs {
len_n: 3072,
len_e: 32,
import_format: 0
})
),
(
Decryption,
Rsa(RsaAttrs {
len_n: 4096,
len_e: 32,
import_format: 0
})
),
(Decryption, Ecdh(EcdhAttrs::new(NistP256r1, None))),
(Decryption, Ecdh(EcdhAttrs::new(NistP384r1, None))),
(Decryption, Ecdh(EcdhAttrs::new(NistP521r1, None))),
(Decryption, Ecdh(EcdhAttrs::new(BrainpoolP256r1, None))),
(Decryption, Ecdh(EcdhAttrs::new(BrainpoolP384r1, None))),
(Decryption, Ecdh(EcdhAttrs::new(BrainpoolP512r1, None))),
(
Authentication,
Rsa(RsaAttrs {
len_n: 2048,
len_e: 32,
import_format: 0
})
),
(
Authentication,
Rsa(RsaAttrs {
len_n: 3072,
len_e: 32,
import_format: 0
})
),
(
Authentication,
Rsa(RsaAttrs {
len_n: 4096,
len_e: 32,
import_format: 0
})
),
(Authentication, Ecdsa(EcdsaAttrs::new(NistP256r1, None))),
(Authentication, Ecdsa(EcdsaAttrs::new(NistP384r1, None))),
(Authentication, Ecdsa(EcdsaAttrs::new(NistP521r1, None))),
(
Authentication,
Ecdsa(EcdsaAttrs::new(BrainpoolP256r1, None))
),
(
Authentication,
Ecdsa(EcdsaAttrs::new(BrainpoolP384r1, None))
),
(
Authentication,
Ecdsa(EcdsaAttrs::new(BrainpoolP512r1, None))
)
])
);
}
#[test]
fn test_yk5() {
let data = [0xfa, 0x82, 0x1, 0xe2, 0xc1, 0x6, 0x1, 0x8, 0x0, 0x0,
0x11, 0x0, 0xc1, 0x6, 0x1, 0xc, 0x0, 0x0, 0x11, 0x0, 0xc1,
0x6, 0x1, 0x10, 0x0, 0x0, 0x11, 0x0, 0xc1, 0x9, 0x13, 0x2a,
0x86, 0x48, 0xce, 0x3d, 0x3, 0x1, 0x7, 0xc1, 0x6, 0x13, 0x2b,
0x81, 0x4, 0x0, 0x22, 0xc1, 0x6, 0x13, 0x2b, 0x81, 0x4, 0x0,
0x23, 0xc1, 0x6, 0x13, 0x2b, 0x81, 0x4, 0x0, 0xa, 0xc1, 0xa,
0x13, 0x2b, 0x24, 0x3, 0x3, 0x2, 0x8, 0x1, 0x1, 0x7, 0xc1,
0xa, 0x13, 0x2b, 0x24, 0x3, 0x3, 0x2, 0x8, 0x1, 0x1, 0xb,
0xc1, 0xa, 0x13, 0x2b, 0x24, 0x3, 0x3, 0x2, 0x8, 0x1, 0x1,
0xd, 0xc1, 0xa, 0x16, 0x2b, 0x6, 0x1, 0x4, 0x1, 0xda, 0x47,
0xf, 0x1, 0xc1, 0xb, 0x16, 0x2b, 0x6, 0x1, 0x4, 0x1, 0x97,
0x55, 0x1, 0x5, 0x1, 0xc2, 0x6, 0x1, 0x8, 0x0, 0x0, 0x11,
0x0, 0xc2, 0x6, 0x1, 0xc, 0x0, 0x0, 0x11, 0x0, 0xc2, 0x6,
0x1, 0x10, 0x0, 0x0, 0x11, 0x0, 0xc2, 0x9, 0x12, 0x2a, 0x86,
0x48, 0xce, 0x3d, 0x3, 0x1, 0x7, 0xc2, 0x6, 0x12, 0x2b, 0x81,
0x4, 0x0, 0x22, 0xc2, 0x6, 0x12, 0x2b, 0x81, 0x4, 0x0, 0x23,
0xc2, 0x6, 0x12, 0x2b, 0x81, 0x4, 0x0, 0xa, 0xc2, 0xa, 0x12,
0x2b, 0x24, 0x3, 0x3, 0x2, 0x8, 0x1, 0x1, 0x7, 0xc2, 0xa,
0x12, 0x2b, 0x24, 0x3, 0x3, 0x2, 0x8, 0x1, 0x1, 0xb, 0xc2,
0xa, 0x12, 0x2b, 0x24, 0x3, 0x3, 0x2, 0x8, 0x1, 0x1, 0xd,
0xc2, 0xa, 0x16, 0x2b, 0x6, 0x1, 0x4, 0x1, 0xda, 0x47, 0xf,
0x1, 0xc2, 0xb, 0x16, 0x2b, 0x6, 0x1, 0x4, 0x1, 0x97, 0x55,
0x1, 0x5, 0x1, 0xc3, 0x6, 0x1, 0x8, 0x0, 0x0, 0x11, 0x0,
0xc3, 0x6, 0x1, 0xc, 0x0, 0x0, 0x11, 0x0, 0xc3, 0x6, 0x1,
0x10, 0x0, 0x0, 0x11, 0x0, 0xc3, 0x9, 0x13, 0x2a, 0x86, 0x48,
0xce, 0x3d, 0x3, 0x1, 0x7, 0xc3, 0x6, 0x13, 0x2b, 0x81, 0x4,
0x0, 0x22, 0xc3, 0x6, 0x13, 0x2b, 0x81, 0x4, 0x0, 0x23, 0xc3,
0x6, 0x13, 0x2b, 0x81, 0x4, 0x0, 0xa, 0xc3, 0xa, 0x13, 0x2b,
0x24, 0x3, 0x3, 0x2, 0x8, 0x1, 0x1, 0x7, 0xc3, 0xa, 0x13,
0x2b, 0x24, 0x3, 0x3, 0x2, 0x8, 0x1, 0x1, 0xb, 0xc3, 0xa,
0x13, 0x2b, 0x24, 0x3, 0x3, 0x2, 0x8, 0x1, 0x1, 0xd, 0xc3,
0xa, 0x16, 0x2b, 0x6, 0x1, 0x4, 0x1, 0xda, 0x47, 0xf, 0x1,
0xc3, 0xb, 0x16, 0x2b, 0x6, 0x1, 0x4, 0x1, 0x97, 0x55, 0x1,
0x5, 0x1, 0xda, 0x6, 0x1, 0x8, 0x0, 0x0, 0x11, 0x0, 0xda,
0x6, 0x1, 0xc, 0x0, 0x0, 0x11, 0x0, 0xda, 0x6, 0x1, 0x10,
0x0, 0x0, 0x11, 0x0, 0xda, 0x9, 0x13, 0x2a, 0x86, 0x48, 0xce,
0x3d, 0x3, 0x1, 0x7, 0xda, 0x6, 0x13, 0x2b, 0x81, 0x4, 0x0,
0x22, 0xda, 0x6, 0x13, 0x2b, 0x81, 0x4, 0x0, 0x23, 0xda, 0x6,
0x13, 0x2b, 0x81, 0x4, 0x0, 0xa, 0xda, 0xa, 0x13, 0x2b, 0x24,
0x3, 0x3, 0x2, 0x8, 0x1, 0x1, 0x7, 0xda, 0xa, 0x13, 0x2b,
0x24, 0x3, 0x3, 0x2, 0x8, 0x1, 0x1, 0xb, 0xda, 0xa, 0x13,
0x2b, 0x24, 0x3, 0x3, 0x2, 0x8, 0x1, 0x1, 0xd, 0xda, 0xa,
0x16, 0x2b, 0x6, 0x1, 0x4, 0x1, 0xda, 0x47, 0xf, 0x1, 0xda,
0xb, 0x16, 0x2b, 0x6, 0x1, 0x4, 0x1, 0x97, 0x55, 0x1, 0x5, 0x1];
let data = [
0xfa, 0x82, 0x1, 0xe2, 0xc1, 0x6, 0x1, 0x8, 0x0, 0x0, 0x11, 0x0,
0xc1, 0x6, 0x1, 0xc, 0x0, 0x0, 0x11, 0x0, 0xc1, 0x6, 0x1, 0x10,
0x0, 0x0, 0x11, 0x0, 0xc1, 0x9, 0x13, 0x2a, 0x86, 0x48, 0xce,
0x3d, 0x3, 0x1, 0x7, 0xc1, 0x6, 0x13, 0x2b, 0x81, 0x4, 0x0, 0x22,
0xc1, 0x6, 0x13, 0x2b, 0x81, 0x4, 0x0, 0x23, 0xc1, 0x6, 0x13,
0x2b, 0x81, 0x4, 0x0, 0xa, 0xc1, 0xa, 0x13, 0x2b, 0x24, 0x3, 0x3,
0x2, 0x8, 0x1, 0x1, 0x7, 0xc1, 0xa, 0x13, 0x2b, 0x24, 0x3, 0x3,
0x2, 0x8, 0x1, 0x1, 0xb, 0xc1, 0xa, 0x13, 0x2b, 0x24, 0x3, 0x3,
0x2, 0x8, 0x1, 0x1, 0xd, 0xc1, 0xa, 0x16, 0x2b, 0x6, 0x1, 0x4,
0x1, 0xda, 0x47, 0xf, 0x1, 0xc1, 0xb, 0x16, 0x2b, 0x6, 0x1, 0x4,
0x1, 0x97, 0x55, 0x1, 0x5, 0x1, 0xc2, 0x6, 0x1, 0x8, 0x0, 0x0,
0x11, 0x0, 0xc2, 0x6, 0x1, 0xc, 0x0, 0x0, 0x11, 0x0, 0xc2, 0x6,
0x1, 0x10, 0x0, 0x0, 0x11, 0x0, 0xc2, 0x9, 0x12, 0x2a, 0x86, 0x48,
0xce, 0x3d, 0x3, 0x1, 0x7, 0xc2, 0x6, 0x12, 0x2b, 0x81, 0x4, 0x0,
0x22, 0xc2, 0x6, 0x12, 0x2b, 0x81, 0x4, 0x0, 0x23, 0xc2, 0x6,
0x12, 0x2b, 0x81, 0x4, 0x0, 0xa, 0xc2, 0xa, 0x12, 0x2b, 0x24, 0x3,
0x3, 0x2, 0x8, 0x1, 0x1, 0x7, 0xc2, 0xa, 0x12, 0x2b, 0x24, 0x3,
0x3, 0x2, 0x8, 0x1, 0x1, 0xb, 0xc2, 0xa, 0x12, 0x2b, 0x24, 0x3,
0x3, 0x2, 0x8, 0x1, 0x1, 0xd, 0xc2, 0xa, 0x16, 0x2b, 0x6, 0x1,
0x4, 0x1, 0xda, 0x47, 0xf, 0x1, 0xc2, 0xb, 0x16, 0x2b, 0x6, 0x1,
0x4, 0x1, 0x97, 0x55, 0x1, 0x5, 0x1, 0xc3, 0x6, 0x1, 0x8, 0x0,
0x0, 0x11, 0x0, 0xc3, 0x6, 0x1, 0xc, 0x0, 0x0, 0x11, 0x0, 0xc3,
0x6, 0x1, 0x10, 0x0, 0x0, 0x11, 0x0, 0xc3, 0x9, 0x13, 0x2a, 0x86,
0x48, 0xce, 0x3d, 0x3, 0x1, 0x7, 0xc3, 0x6, 0x13, 0x2b, 0x81, 0x4,
0x0, 0x22, 0xc3, 0x6, 0x13, 0x2b, 0x81, 0x4, 0x0, 0x23, 0xc3, 0x6,
0x13, 0x2b, 0x81, 0x4, 0x0, 0xa, 0xc3, 0xa, 0x13, 0x2b, 0x24, 0x3,
0x3, 0x2, 0x8, 0x1, 0x1, 0x7, 0xc3, 0xa, 0x13, 0x2b, 0x24, 0x3,
0x3, 0x2, 0x8, 0x1, 0x1, 0xb, 0xc3, 0xa, 0x13, 0x2b, 0x24, 0x3,
0x3, 0x2, 0x8, 0x1, 0x1, 0xd, 0xc3, 0xa, 0x16, 0x2b, 0x6, 0x1,
0x4, 0x1, 0xda, 0x47, 0xf, 0x1, 0xc3, 0xb, 0x16, 0x2b, 0x6, 0x1,
0x4, 0x1, 0x97, 0x55, 0x1, 0x5, 0x1, 0xda, 0x6, 0x1, 0x8, 0x0,
0x0, 0x11, 0x0, 0xda, 0x6, 0x1, 0xc, 0x0, 0x0, 0x11, 0x0, 0xda,
0x6, 0x1, 0x10, 0x0, 0x0, 0x11, 0x0, 0xda, 0x9, 0x13, 0x2a, 0x86,
0x48, 0xce, 0x3d, 0x3, 0x1, 0x7, 0xda, 0x6, 0x13, 0x2b, 0x81, 0x4,
0x0, 0x22, 0xda, 0x6, 0x13, 0x2b, 0x81, 0x4, 0x0, 0x23, 0xda, 0x6,
0x13, 0x2b, 0x81, 0x4, 0x0, 0xa, 0xda, 0xa, 0x13, 0x2b, 0x24, 0x3,
0x3, 0x2, 0x8, 0x1, 0x1, 0x7, 0xda, 0xa, 0x13, 0x2b, 0x24, 0x3,
0x3, 0x2, 0x8, 0x1, 0x1, 0xb, 0xda, 0xa, 0x13, 0x2b, 0x24, 0x3,
0x3, 0x2, 0x8, 0x1, 0x1, 0xd, 0xda, 0xa, 0x16, 0x2b, 0x6, 0x1,
0x4, 0x1, 0xda, 0x47, 0xf, 0x1, 0xda, 0xb, 0x16, 0x2b, 0x6, 0x1,
0x4, 0x1, 0x97, 0x55, 0x1, 0x5, 0x1,
];
let ai = AlgoInfo::try_from(data.to_vec()).unwrap();
assert_eq!(
ai, AlgoInfo(
vec![
(Signing, Rsa(RsaAttrs { len_n: 2048, len_e: 17, import_format: 0 })),
(Signing, Rsa(RsaAttrs { len_n: 3072, len_e: 17, import_format: 0 })),
(Signing, Rsa(RsaAttrs { len_n: 4096, len_e: 17, import_format: 0 })),
(Signing, Ecdsa(EcdsaAttrs::new(NistP256r1, None))),
(Signing, Ecdsa(EcdsaAttrs::new(NistP384r1, None))),
(Signing, Ecdsa(EcdsaAttrs::new(NistP521r1, None))),
(Signing, Ecdsa(EcdsaAttrs::new(Secp256k1, None))),
(Signing, Ecdsa(EcdsaAttrs::new(BrainpoolP256r1, None))),
(Signing, Ecdsa(EcdsaAttrs::new(BrainpoolP384r1, None))),
(Signing, Ecdsa(EcdsaAttrs::new(BrainpoolP512r1, None))),
(Signing, Eddsa(EddsaAttrs::new(Ed25519, None))),
(Signing, Eddsa(EddsaAttrs::new(Cv25519, None))),
(Decryption, Rsa(RsaAttrs { len_n: 2048, len_e: 17, import_format: 0 })),
(Decryption, Rsa(RsaAttrs { len_n: 3072, len_e: 17, import_format: 0 })),
(Decryption, Rsa(RsaAttrs { len_n: 4096, len_e: 17, import_format: 0 })),
(Decryption, Ecdh(EcdhAttrs::new(NistP256r1, None))),
(Decryption, Ecdh(EcdhAttrs::new(NistP384r1, None))),
(Decryption, Ecdh(EcdhAttrs::new(NistP521r1, None))),
(Decryption, Ecdh(EcdhAttrs::new(Secp256k1, None))),
(Decryption, Ecdh(EcdhAttrs::new(BrainpoolP256r1, None))),
(Decryption, Ecdh(EcdhAttrs::new(BrainpoolP384r1, None))),
(Decryption, Ecdh(EcdhAttrs::new(BrainpoolP512r1, None))),
(Decryption, Eddsa(EddsaAttrs::new(Ed25519, None))),
(Decryption, Eddsa(EddsaAttrs::new(Cv25519, None))),
(Authentication, Rsa(RsaAttrs { len_n: 2048, len_e: 17, import_format: 0 })),
(Authentication, Rsa(RsaAttrs { len_n: 3072, len_e: 17, import_format: 0 })),
(Authentication, Rsa(RsaAttrs { len_n: 4096, len_e: 17, import_format: 0 })),
(Authentication, Ecdsa(EcdsaAttrs::new(NistP256r1, None))),
(Authentication, Ecdsa(EcdsaAttrs::new(NistP384r1, None))),
(Authentication, Ecdsa(EcdsaAttrs::new(NistP521r1, None))),
(Authentication, Ecdsa(EcdsaAttrs::new(Secp256k1, None))),
(Authentication, Ecdsa(EcdsaAttrs::new(BrainpoolP256r1, None))),
(Authentication, Ecdsa(EcdsaAttrs::new(BrainpoolP384r1, None))),
(Authentication, Ecdsa(EcdsaAttrs::new(BrainpoolP512r1, None))),
(Authentication, Eddsa(EddsaAttrs::new(Ed25519, None))),
(Authentication, Eddsa(EddsaAttrs::new(Cv25519, None))),
(Attestation, Rsa(RsaAttrs { len_n: 2048, len_e: 17, import_format: 0 })),
(Attestation, Rsa(RsaAttrs { len_n: 3072, len_e: 17, import_format: 0 })),
(Attestation, Rsa(RsaAttrs { len_n: 4096, len_e: 17, import_format: 0 })),
(Attestation, Ecdsa(EcdsaAttrs::new(NistP256r1, None))),
(Attestation, Ecdsa(EcdsaAttrs::new(NistP384r1, None))),
(Attestation, Ecdsa(EcdsaAttrs::new(NistP521r1, None))),
(Attestation, Ecdsa(EcdsaAttrs::new(Secp256k1, None))),
(Attestation, Ecdsa(EcdsaAttrs::new(BrainpoolP256r1, None))),
(Attestation, Ecdsa(EcdsaAttrs::new(BrainpoolP384r1, None))),
(Attestation, Ecdsa(EcdsaAttrs::new(BrainpoolP512r1, None))),
(Attestation, Eddsa(EddsaAttrs::new(Ed25519, None))),
(Attestation, Eddsa(EddsaAttrs::new(Cv25519, None)))
]
)
ai,
AlgoInfo(vec![
(
Signing,
Rsa(RsaAttrs {
len_n: 2048,
len_e: 17,
import_format: 0
})
),
(
Signing,
Rsa(RsaAttrs {
len_n: 3072,
len_e: 17,
import_format: 0
})
),
(
Signing,
Rsa(RsaAttrs {
len_n: 4096,
len_e: 17,
import_format: 0
})
),
(Signing, Ecdsa(EcdsaAttrs::new(NistP256r1, None))),
(Signing, Ecdsa(EcdsaAttrs::new(NistP384r1, None))),
(Signing, Ecdsa(EcdsaAttrs::new(NistP521r1, None))),
(Signing, Ecdsa(EcdsaAttrs::new(Secp256k1, None))),
(Signing, Ecdsa(EcdsaAttrs::new(BrainpoolP256r1, None))),
(Signing, Ecdsa(EcdsaAttrs::new(BrainpoolP384r1, None))),
(Signing, Ecdsa(EcdsaAttrs::new(BrainpoolP512r1, None))),
(Signing, Eddsa(EddsaAttrs::new(Ed25519, None))),
(Signing, Eddsa(EddsaAttrs::new(Cv25519, None))),
(
Decryption,
Rsa(RsaAttrs {
len_n: 2048,
len_e: 17,
import_format: 0
})
),
(
Decryption,
Rsa(RsaAttrs {
len_n: 3072,
len_e: 17,
import_format: 0
})
),
(
Decryption,
Rsa(RsaAttrs {
len_n: 4096,
len_e: 17,
import_format: 0
})
),
(Decryption, Ecdh(EcdhAttrs::new(NistP256r1, None))),
(Decryption, Ecdh(EcdhAttrs::new(NistP384r1, None))),
(Decryption, Ecdh(EcdhAttrs::new(NistP521r1, None))),
(Decryption, Ecdh(EcdhAttrs::new(Secp256k1, None))),
(Decryption, Ecdh(EcdhAttrs::new(BrainpoolP256r1, None))),
(Decryption, Ecdh(EcdhAttrs::new(BrainpoolP384r1, None))),
(Decryption, Ecdh(EcdhAttrs::new(BrainpoolP512r1, None))),
(Decryption, Eddsa(EddsaAttrs::new(Ed25519, None))),
(Decryption, Eddsa(EddsaAttrs::new(Cv25519, None))),
(
Authentication,
Rsa(RsaAttrs {
len_n: 2048,
len_e: 17,
import_format: 0
})
),
(
Authentication,
Rsa(RsaAttrs {
len_n: 3072,
len_e: 17,
import_format: 0
})
),
(
Authentication,
Rsa(RsaAttrs {
len_n: 4096,
len_e: 17,
import_format: 0
})
),
(Authentication, Ecdsa(EcdsaAttrs::new(NistP256r1, None))),
(Authentication, Ecdsa(EcdsaAttrs::new(NistP384r1, None))),
(Authentication, Ecdsa(EcdsaAttrs::new(NistP521r1, None))),
(Authentication, Ecdsa(EcdsaAttrs::new(Secp256k1, None))),
(
Authentication,
Ecdsa(EcdsaAttrs::new(BrainpoolP256r1, None))
),
(
Authentication,
Ecdsa(EcdsaAttrs::new(BrainpoolP384r1, None))
),
(
Authentication,
Ecdsa(EcdsaAttrs::new(BrainpoolP512r1, None))
),
(Authentication, Eddsa(EddsaAttrs::new(Ed25519, None))),
(Authentication, Eddsa(EddsaAttrs::new(Cv25519, None))),
(
Attestation,
Rsa(RsaAttrs {
len_n: 2048,
len_e: 17,
import_format: 0
})
),
(
Attestation,
Rsa(RsaAttrs {
len_n: 3072,
len_e: 17,
import_format: 0
})
),
(
Attestation,
Rsa(RsaAttrs {
len_n: 4096,
len_e: 17,
import_format: 0
})
),
(Attestation, Ecdsa(EcdsaAttrs::new(NistP256r1, None))),
(Attestation, Ecdsa(EcdsaAttrs::new(NistP384r1, None))),
(Attestation, Ecdsa(EcdsaAttrs::new(NistP521r1, None))),
(Attestation, Ecdsa(EcdsaAttrs::new(Secp256k1, None))),
(Attestation, Ecdsa(EcdsaAttrs::new(BrainpoolP256r1, None))),
(Attestation, Ecdsa(EcdsaAttrs::new(BrainpoolP384r1, None))),
(Attestation, Ecdsa(EcdsaAttrs::new(BrainpoolP512r1, None))),
(Attestation, Eddsa(EddsaAttrs::new(Ed25519, None))),
(Attestation, Eddsa(EddsaAttrs::new(Cv25519, None)))
])
);
}
}

View file

@ -1,8 +1,8 @@
// SPDX-FileCopyrightText: 2021 Heiko Schaefer <heiko@schaefer.name>
// SPDX-License-Identifier: MIT OR Apache-2.0
use nom::{bytes::complete as bytes, number::complete as number};
use anyhow::Result;
use nom::{bytes::complete as bytes, number::complete as number};
use std::convert::TryFrom;
use crate::parse;
@ -24,8 +24,7 @@ pub struct ApplicationId {
pub serial: u32,
}
fn parse(input: &[u8])
-> nom::IResult<&[u8], ApplicationId> {
fn parse(input: &[u8]) -> nom::IResult<&[u8], ApplicationId> {
let (input, _) = bytes::tag([0xd2, 0x76, 0x0, 0x1, 0x24])(input)?;
let (input, application) = number::u8(input)?;
@ -36,8 +35,15 @@ fn parse(input: &[u8])
let (input, _) =
nom::combinator::all_consuming(bytes::tag([0x0, 0x0]))(input)?;
Ok((input,
ApplicationId { application, version, manufacturer, serial }))
Ok((
input,
ApplicationId {
application,
version,
manufacturer,
serial,
},
))
}
impl TryFrom<&[u8]> for ApplicationId {
@ -53,4 +59,3 @@ impl ApplicationId {
format!("{:08X}", self.serial)
}
}

View file

@ -5,9 +5,9 @@ use std::convert::TryFrom;
use anyhow::Result;
use crate::Sex;
use crate::tlv::tag::Tag;
use crate::tlv::{TlvEntry, Tlv};
use crate::tlv::{Tlv, TlvEntry};
use crate::Sex;
#[derive(Debug)]
pub struct CardHolder {
@ -23,14 +23,17 @@ impl TryFrom<&[u8]> for CardHolder {
let entry = TlvEntry::from(&data, true)?;
let tlv = Tlv(Tag(vec![0x65]), entry);
let name: Option<String> = tlv.find(&Tag::from(&[0x5b][..]))
let name: Option<String> = tlv
.find(&Tag::from(&[0x5b][..]))
.map(|v| String::from_utf8_lossy(&v.serialize()).to_string());
let lang: Option<Vec<[char; 2]>> = tlv
.find(&Tag::from(&[0x5f, 0x2d][..]))
.map(|v| v.serialize().chunks(2)
.map(|c| [c[0] as char, c[1] as char]).collect()
);
let lang: Option<Vec<[char; 2]>> =
tlv.find(&Tag::from(&[0x5f, 0x2d][..])).map(|v| {
v.serialize()
.chunks(2)
.map(|c| [c[0] as char, c[1] as char])
.collect()
});
let sex = tlv
.find(&Tag::from(&[0x5f, 0x35][..]))

View file

@ -1,11 +1,11 @@
// SPDX-FileCopyrightText: 2021 Heiko Schaefer <heiko@schaefer.name>
// SPDX-License-Identifier: MIT OR Apache-2.0
use nom::{number::complete as number, combinator, sequence};
use anyhow::Result;
use std::collections::HashSet;
use crate::parse;
use crate::errors::OpenpgpCardError;
use crate::parse;
use anyhow::Result;
use nom::{combinator, number::complete as number, sequence};
use std::collections::HashSet;
use std::convert::TryFrom;
#[derive(Debug, Eq, PartialEq)]
@ -35,21 +35,38 @@ fn features(input: &[u8]) -> nom::IResult<&[u8], HashSet<Features>> {
combinator::map(number::u8, |b| {
let mut f = HashSet::new();
if b & 0x80 != 0 { f.insert(Features::SecureMessaging); }
if b & 0x40 != 0 { f.insert(Features::GetChallenge); }
if b & 0x20 != 0 { f.insert(Features::KeyImport); }
if b & 0x10 != 0 { f.insert(Features::PwStatusChange); }
if b & 0x08 != 0 { f.insert(Features::PrivateUseDOs); }
if b & 0x04 != 0 { f.insert(Features::AlgoAttrsChangeable); }
if b & 0x02 != 0 { f.insert(Features::Aes); }
if b & 0x01 != 0 { f.insert(Features::KdfDo); }
if b & 0x80 != 0 {
f.insert(Features::SecureMessaging);
}
if b & 0x40 != 0 {
f.insert(Features::GetChallenge);
}
if b & 0x20 != 0 {
f.insert(Features::KeyImport);
}
if b & 0x10 != 0 {
f.insert(Features::PwStatusChange);
}
if b & 0x08 != 0 {
f.insert(Features::PrivateUseDOs);
}
if b & 0x04 != 0 {
f.insert(Features::AlgoAttrsChangeable);
}
if b & 0x02 != 0 {
f.insert(Features::Aes);
}
if b & 0x01 != 0 {
f.insert(Features::KdfDo);
}
f
})(input)
}
fn parse(input: &[u8])
-> nom::IResult<&[u8], (HashSet<Features>, u8, u16, u16, u16, u8, u8)> {
fn parse(
input: &[u8],
) -> nom::IResult<&[u8], (HashSet<Features>, u8, u16, u16, u16, u8, u8)> {
nom::combinator::all_consuming(sequence::tuple((
features,
number::u8,
@ -57,11 +74,10 @@ fn parse(input: &[u8])
number::be_u16,
number::be_u16,
number::u8,
number::u8)
))(input)
number::u8,
)))(input)
}
impl TryFrom<&[u8]> for ExtendedCap {
type Error = OpenpgpCardError;
@ -75,16 +91,15 @@ impl TryFrom<&[u8]> for ExtendedCap {
max_len_cardholder_cert: ec.3,
max_len_special_do: ec.4,
pin_2_format: ec.5 == 1, // FIXME: error if != 0|1
mse_command: ec.6 == 1, // FIXME: error if != 0|1
mse_command: ec.6 == 1, // FIXME: error if != 0|1
})
}
}
#[cfg(test)]
mod test {
use hex_literal::hex;
use crate::parse::extended_cap::{ExtendedCap, Features};
use hex_literal::hex;
use std::collections::HashSet;
use std::iter::FromIterator;
@ -94,11 +109,16 @@ mod test {
let ec = ExtendedCap::from(&data).unwrap();
assert_eq!(
ec, ExtendedCap {
features: HashSet::from_iter(
vec![Features::GetChallenge, Features::KeyImport,
Features::PwStatusChange, Features::PrivateUseDOs,
Features::AlgoAttrsChangeable, Features::KdfDo]),
ec,
ExtendedCap {
features: HashSet::from_iter(vec![
Features::GetChallenge,
Features::KeyImport,
Features::PwStatusChange,
Features::PrivateUseDOs,
Features::AlgoAttrsChangeable,
Features::KdfDo
]),
sm: 0x0,
max_len_challenge: 0xbfe,
max_len_cardholder_cert: 0x800,

View file

@ -1,9 +1,9 @@
// SPDX-FileCopyrightText: 2021 Heiko Schaefer <heiko@schaefer.name>
// SPDX-License-Identifier: MIT OR Apache-2.0
use nom::{number::complete as number, sequence, bytes::complete::tag};
use anyhow::Result;
use crate::parse;
use anyhow::Result;
use nom::{bytes::complete::tag, number::complete as number, sequence};
#[derive(Debug, Eq, PartialEq)]
pub struct ExtendedLengthInfo {
@ -11,15 +11,14 @@ pub struct ExtendedLengthInfo {
pub max_response_bytes: u16,
}
fn parse(input: &[u8]) -> nom::IResult<&[u8], (u16, u16)> {
let (input, (_, cmd, _, resp)) = nom::combinator::all_consuming
(sequence::tuple((
let (input, (_, cmd, _, resp)) =
nom::combinator::all_consuming(sequence::tuple((
tag([0x2, 0x2]),
number::be_u16,
tag([0x2, 0x2]),
number::be_u16)
))(input)?;
number::be_u16,
)))(input)?;
Ok((input, (cmd, resp)))
}

View file

@ -1,12 +1,12 @@
// SPDX-FileCopyrightText: 2021 Heiko Schaefer <heiko@schaefer.name>
// SPDX-License-Identifier: MIT OR Apache-2.0
use anyhow::anyhow;
use nom::{bytes::complete as bytes, combinator, sequence};
use std::fmt;
use anyhow::anyhow;
use crate::parse::KeySet;
use crate::errors::OpenpgpCardError;
use crate::parse::KeySet;
#[derive(Clone, Eq, PartialEq)]
pub struct Fingerprint([u8; 20]);
@ -60,7 +60,9 @@ fn fingerprint(input: &[u8]) -> nom::IResult<&[u8], Option<Fingerprint>> {
}
fn fingerprints(input: &[u8]) -> nom::IResult<&[u8], KeySet<Fingerprint>> {
combinator::into(sequence::tuple((fingerprint, fingerprint, fingerprint)))(input)
combinator::into(sequence::tuple((fingerprint, fingerprint, fingerprint)))(
input,
)
}
pub fn from(input: &[u8]) -> Result<KeySet<Fingerprint>, OpenpgpCardError> {
@ -72,4 +74,4 @@ pub fn from(input: &[u8]) -> Result<KeySet<Fingerprint>, OpenpgpCardError> {
.map(|res| res.1)
.map_err(|err| anyhow!("Parsing failed: {:?}", err))
.map_err(OpenpgpCardError::InternalError)
}
}

View file

@ -1,8 +1,8 @@
// SPDX-FileCopyrightText: 2021 Heiko Schaefer <heiko@schaefer.name>
// SPDX-License-Identifier: MIT OR Apache-2.0
use anyhow::{anyhow, Result};
use crate::errors::OpenpgpCardError;
use anyhow::{anyhow, Result};
#[derive(Debug)]
pub struct CardCapabilities {
@ -24,7 +24,6 @@ impl CardCapabilities {
self.extended_length_information
}
pub fn from(data: [u8; 3]) -> Self {
let byte3 = data[2];
@ -32,7 +31,11 @@ impl CardCapabilities {
let extended_lc_le = byte3 & 0x40 != 0;
let extended_length_information = byte3 & 0x20 != 0;
Self { command_chaining, extended_lc_le, extended_length_information }
Self {
command_chaining,
extended_lc_le,
extended_length_information,
}
}
}
@ -109,35 +112,39 @@ impl Historical {
cc = Some([ctlv[1], ctlv[2], ctlv[3]]);
ctlv.drain(0..4);
}
0 => { ctlv.drain(0..1); }
_ => unimplemented!("unexpected tlv in historical bytes")
0 => {
ctlv.drain(0..1);
}
_ => unimplemented!("unexpected tlv in historical bytes"),
}
}
let sib =
match data[len - 3] {
0 => {
// Card does not offer life cycle management, commands
// TERMINATE DF and ACTIVATE FILE are not supported
0
}
3 => {
// Initialisation state
// OpenPGP application can be reset to default values with
// an ACTIVATE FILE command
3
}
5 => {
// Operational state (activated)
// Card supports life cycle management, commands TERMINATE
// DF and ACTIVATE FILE are available
5
}
_ => {
return Err(anyhow!("unexpected status indicator in \
historical bytes").into());
}
};
let sib = match data[len - 3] {
0 => {
// Card does not offer life cycle management, commands
// TERMINATE DF and ACTIVATE FILE are not supported
0
}
3 => {
// Initialisation state
// OpenPGP application can be reset to default values with
// an ACTIVATE FILE command
3
}
5 => {
// Operational state (activated)
// Card supports life cycle management, commands TERMINATE
// DF and ACTIVATE FILE are available
5
}
_ => {
return Err(anyhow!(
"unexpected status indicator in \
historical bytes"
)
.into());
}
};
// Ignore final two bytes: according to the spec, they should
// show [0x90, 0x0] - but Yubikey Neo shows [0x0, 0x0].
@ -148,8 +155,8 @@ impl Historical {
Ok(Self { cib, csd, cc, sib })
} else {
Err(anyhow!("Unexpected category indicator in historical \
bytes").into())
Err(anyhow!("Unexpected category indicator in historical bytes")
.into())
}
}
}

View file

@ -6,14 +6,14 @@
pub mod algo_attrs;
pub mod algo_info;
pub mod application_id;
pub mod cardholder;
pub mod historical;
pub mod extended_cap;
pub mod extended_length_info;
pub mod fingerprint;
pub mod application_id;
pub mod historical;
use anyhow::{Error, anyhow};
use anyhow::{anyhow, Error};
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct KeySet<T> {

View file

@ -1,7 +1,10 @@
// SPDX-FileCopyrightText: 2021 Heiko Schaefer <heiko@schaefer.name>
// SPDX-License-Identifier: MIT OR Apache-2.0
use nom::{branch, bytes::complete as bytes, combinator, number::complete as number, sequence};
use nom::{
branch, bytes::complete as bytes, combinator, number::complete as number,
sequence,
};
fn length1(input: &[u8]) -> nom::IResult<&[u8], u8> {
combinator::verify(number::u8, |&c| c < 0x80)(input)

View file

@ -35,7 +35,7 @@ impl Tlv {
let length = crate::tlv::tlv_encode_length(value.len() as u16);
let mut ser = Vec::new();
ser.extend(self.0.0.iter());
ser.extend(self.0 .0.iter());
ser.extend(length.iter());
ser.extend(value.iter());
ser
@ -114,44 +114,45 @@ impl TlvEntry {
#[cfg(test)]
mod test {
use super::{Tag, Tlv};
use hex_literal::hex;
use anyhow::Result;
use crate::tlv::TlvEntry;
use anyhow::Result;
use hex_literal::hex;
#[test]
fn test_tlv() -> Result<()> {
// From OpenPGP card spec § 7.2.6
let data = hex!("5B0B546573743C3C54657374695F2D0264655F350131")
.to_vec();
let data =
hex!("5B0B546573743C3C54657374695F2D0264655F350131").to_vec();
let (input, tlv) = Tlv::parse(&data).unwrap();
assert_eq!(tlv,
Tlv(Tag::from([0x5b]),
TlvEntry::S(hex!("546573743C3C5465737469")
.to_vec())));
assert_eq!(
tlv,
Tlv(
Tag::from([0x5b]),
TlvEntry::S(hex!("546573743C3C5465737469").to_vec())
)
);
let (input, tlv) = Tlv::parse(input).unwrap();
assert_eq!(tlv,
Tlv(Tag::from([0x5f, 0x2d]),
TlvEntry::S(hex!("6465")
.to_vec())));
assert_eq!(
tlv,
Tlv(Tag::from([0x5f, 0x2d]), TlvEntry::S(hex!("6465").to_vec()))
);
let (input, tlv) = Tlv::parse(input).unwrap();
assert_eq!(tlv,
Tlv(Tag::from([0x5f, 0x35]),
TlvEntry::S(hex!("31")
.to_vec())));
assert_eq!(
tlv,
Tlv(Tag::from([0x5f, 0x35]), TlvEntry::S(hex!("31").to_vec()))
);
assert!(input.is_empty());
Ok(())
}
#[test]
fn test_tlv_yubi5() -> Result<()> {
// 'Yubikey 5 NFC' output for GET DATA on "Application Related Data"
@ -168,7 +169,10 @@ mod test {
assert_eq!(entry.serialize(), hex!("7d000bfe080000ff0000"));
let entry = tlv.find(&Tag::from([0x4f])).unwrap();
assert_eq!(entry.serialize(), hex!("d2760001240103040006160191800000"));
assert_eq!(
entry.serialize(),
hex!("d2760001240103040006160191800000")
);
let entry = tlv.find(&Tag::from([0x5f, 0x52])).unwrap();
assert_eq!(entry.serialize(), hex!("00730000e0059000"));
@ -203,9 +207,11 @@ mod test {
let entry = tlv.find(&Tag::from([0xc6])).unwrap();
assert_eq!(entry.serialize(), hex!("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"));
let entry = tlv.find(&Tag::from([0xcd])).unwrap();
assert_eq!(entry.serialize(), hex!("00000000000000000000000000000000"));
assert_eq!(
entry.serialize(),
hex!("00000000000000000000000000000000")
);
let entry = tlv.find(&Tag::from([0xde])).unwrap();
assert_eq!(entry.serialize(), hex!("0100020003008102"));
@ -234,17 +240,22 @@ mod test {
// but has been abridged and changed. It does not represent a
// complete valid OpenPGP card DO!
let a = Tlv(Tag::from(&[0x7F, 0x48][..]),
TlvEntry::S(vec![0x92, 0x03]));
let a =
Tlv(Tag::from(&[0x7F, 0x48][..]), TlvEntry::S(vec![0x92, 0x03]));
let b = Tlv(Tag::from(&[0x5F, 0x48][..]),
TlvEntry::S(vec![0x1, 0x2, 0x3]));
let b = Tlv(
Tag::from(&[0x5F, 0x48][..]),
TlvEntry::S(vec![0x1, 0x2, 0x3]),
);
let tlv = Tlv(Tag::from(&[0x4d][..]),
TlvEntry::C(vec![a, b]));
let tlv = Tlv(Tag::from(&[0x4d][..]), TlvEntry::C(vec![a, b]));
assert_eq!(tlv.serialize(), &[0x4d, 0xb,
0x7f, 0x48, 0x2, 0x92, 0x3,
0x5f, 0x48, 0x3, 0x1, 0x2, 0x3]);
assert_eq!(
tlv.serialize(),
&[
0x4d, 0xb, 0x7f, 0x48, 0x2, 0x92, 0x3, 0x5f, 0x48, 0x3, 0x1,
0x2, 0x3
]
);
}
}
}

View file

@ -1,7 +1,10 @@
// SPDX-FileCopyrightText: 2021 Heiko Schaefer <heiko@schaefer.name>
// SPDX-License-Identifier: MIT OR Apache-2.0
use nom::{branch, bytes::complete as bytes, combinator, number::complete as number, sequence};
use nom::{
branch, bytes::complete as bytes, combinator, number::complete as number,
sequence,
};
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Tag(pub Vec<u8>);
@ -38,9 +41,11 @@ impl From<[u8; 2]> for Tag {
}
}
fn multi_byte_tag(input: &[u8]) -> nom::IResult<&[u8], &[u8]> {
combinator::recognize(sequence::pair(multi_byte_tag_first, multi_byte_tag_rest))(input)
combinator::recognize(sequence::pair(
multi_byte_tag_first,
multi_byte_tag_rest,
))(input)
}
fn multi_byte_tag_first(input: &[u8]) -> nom::IResult<&[u8], u8> {
@ -61,12 +66,9 @@ fn multi_byte_tag_rest(input: &[u8]) -> nom::IResult<&[u8], &[u8]> {
}
fn single_byte_rest(input: &[u8]) -> nom::IResult<&[u8], &[u8]> {
combinator::verify(bytes::take(1u8),
|c: &[u8]| {
c.len() == 1 &&
is_first(&c[0]) &&
is_last(&c[0])
})(input)
combinator::verify(bytes::take(1u8), |c: &[u8]| {
c.len() == 1 && is_first(&c[0]) && is_last(&c[0])
})(input)
}
fn multi_byte_rest(input: &[u8]) -> nom::IResult<&[u8], &[u8]> {
@ -81,19 +83,17 @@ fn multi_byte_tag_rest(input: &[u8]) -> nom::IResult<&[u8], &[u8]> {
}
fn single_byte_tag(input: &[u8]) -> nom::IResult<&[u8], &[u8]> {
combinator::verify(bytes::take(1u8),
|c: &[u8]| {
c.len() == 1 &&
!is_multi_byte_tag_first(&c[0])
})(input)
combinator::verify(bytes::take(1u8), |c: &[u8]| {
c.len() == 1 && !is_multi_byte_tag_first(&c[0])
})(input)
}
pub(super) fn tag(input: &[u8]) -> nom::IResult<&[u8], Tag> {
combinator::map(branch::alt((multi_byte_tag, single_byte_tag)),
Tag::from)(input)
combinator::map(branch::alt((multi_byte_tag, single_byte_tag)), Tag::from)(
input,
)
}
#[cfg(test)]
mod test {
#[test]