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

View file

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

View file

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

View file

@ -10,13 +10,12 @@ use openpgp::policy::Policy;
use openpgp::types::PublicKeyAlgorithm; use openpgp::types::PublicKeyAlgorithm;
use sequoia_openpgp as openpgp; use sequoia_openpgp as openpgp;
use openpgp_card::errors::OpenpgpCardError;
use openpgp_card::Hash; use openpgp_card::Hash;
use openpgp_card::OpenPGPCardUser; use openpgp_card::OpenPGPCardUser;
use openpgp_card::errors::OpenpgpCardError;
use crate::PublicKey; use crate::PublicKey;
pub(crate) struct CardSigner<'a> { pub(crate) struct CardSigner<'a> {
/// The OpenPGP card (authenticated to allow signing operations) /// The OpenPGP card (authenticated to allow signing operations)
ocu: &'a OpenPGPCardUser, ocu: &'a OpenPGPCardUser,
@ -30,11 +29,11 @@ impl<'a> CardSigner<'a> {
/// ///
/// An Error is returned if no match between the card's signing /// An Error is returned if no match between the card's signing
/// key and a (sub)key of `cert` can be made. /// key and a (sub)key of `cert` can be made.
pub fn new(ocu: &'a OpenPGPCardUser, pub fn new(
ocu: &'a OpenPGPCardUser,
cert: &openpgp::Cert, cert: &openpgp::Cert,
policy: &dyn Policy) policy: &dyn Policy,
-> Result<CardSigner<'a>, OpenpgpCardError> { ) -> Result<CardSigner<'a>, OpenpgpCardError> {
// Get the fingerprint for the signing key from the card. // Get the fingerprint for the signing key from the card.
let fps = ocu.get_fingerprints()?; let fps = ocu.get_fingerprints()?;
let fp = fps.signature(); let fp = fps.signature();
@ -44,8 +43,7 @@ impl<'a> CardSigner<'a> {
let fp = openpgp::Fingerprint::from_bytes(fp.as_bytes()); let fp = openpgp::Fingerprint::from_bytes(fp.as_bytes());
// Find the matching signing-capable (sub)key in `cert` // Find the matching signing-capable (sub)key in `cert`
let keys: Vec<_> = let keys: Vec<_> = cert
cert
.keys() .keys()
.with_policy(policy, None) .with_policy(policy, None)
.alive() .alive()
@ -64,13 +62,15 @@ impl<'a> CardSigner<'a> {
public: public.role_as_unspecified().clone(), public: public.role_as_unspecified().clone(),
}) })
} else { } else {
Err(OpenpgpCardError::InternalError( Err(OpenpgpCardError::InternalError(anyhow!(
anyhow!("Failed to find a matching (sub)key in cert"))) "Failed to find a matching (sub)key in cert"
)))
} }
} else { } else {
Err(OpenpgpCardError::InternalError( Err(OpenpgpCardError::InternalError(anyhow!(
anyhow!("Failed to get the signing key's Fingerprint \ "Failed to get the signing key's Fingerprint \
from the card"))) from the card"
)))
} }
} }
} }
@ -86,34 +86,45 @@ impl<'a> crypto::Signer for CardSigner<'a> {
/// perform the signing operation. /// perform the signing operation.
/// ///
/// (7.2.10 PSO: COMPUTE DIGITAL SIGNATURE) /// (7.2.10 PSO: COMPUTE DIGITAL SIGNATURE)
fn sign(&mut self, fn sign(
&mut self,
hash_algo: openpgp::types::HashAlgorithm, hash_algo: openpgp::types::HashAlgorithm,
digest: &[u8], digest: &[u8],
) -> openpgp::Result<mpi::Signature> { ) -> openpgp::Result<mpi::Signature> {
match (self.public.pk_algo(), self.public.mpis()) { match (self.public.pk_algo(), self.public.mpis()) {
#[allow(deprecated)] #[allow(deprecated)]
(PublicKeyAlgorithm::RSASign, mpi::PublicKey::RSA { .. }) | (PublicKeyAlgorithm::RSASign, mpi::PublicKey::RSA { .. })
(PublicKeyAlgorithm::RSAEncryptSign, mpi::PublicKey::RSA { .. }) => { | (
PublicKeyAlgorithm::RSAEncryptSign,
mpi::PublicKey::RSA { .. },
) => {
let sig = match hash_algo { let sig = match hash_algo {
openpgp::types::HashAlgorithm::SHA256 => { openpgp::types::HashAlgorithm::SHA256 => {
let hash = Hash::SHA256(digest.try_into() let hash =
.map_err(|_| anyhow!("invalid slice length"))?); Hash::SHA256(digest.try_into().map_err(|_| {
anyhow!("invalid slice length")
})?);
self.ocu.signature_for_hash(hash)? self.ocu.signature_for_hash(hash)?
} }
openpgp::types::HashAlgorithm::SHA384 => { openpgp::types::HashAlgorithm::SHA384 => {
let hash = Hash::SHA384(digest.try_into() let hash =
.map_err(|_| anyhow!("invalid slice length"))?); Hash::SHA384(digest.try_into().map_err(|_| {
anyhow!("invalid slice length")
})?);
self.ocu.signature_for_hash(hash)? self.ocu.signature_for_hash(hash)?
} }
openpgp::types::HashAlgorithm::SHA512 => { openpgp::types::HashAlgorithm::SHA512 => {
let hash = Hash::SHA512(digest.try_into() let hash =
.map_err(|_| anyhow!("invalid slice length"))?); Hash::SHA512(digest.try_into().map_err(|_| {
anyhow!("invalid slice length")
})?);
self.ocu.signature_for_hash(hash)? self.ocu.signature_for_hash(hash)?
} }
_ => { _ => {
return Err( return Err(anyhow!(
anyhow!("Unsupported hash algorithm for RSA {:?}", "Unsupported hash algorithm for RSA {:?}",
hash_algo)); hash_algo
));
} }
}; };
@ -133,7 +144,8 @@ impl<'a> crypto::Signer for CardSigner<'a> {
// FIXME: implement NIST etc // FIXME: implement NIST etc
(pk_algo, _) => Err(anyhow!( (pk_algo, _) => Err(anyhow!(
"Unsupported combination of algorithm {:?} and pubkey {:?}", "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-FileCopyrightText: 2021 Heiko Schaefer <heiko@schaefer.name>
// SPDX-License-Identifier: MIT OR Apache-2.0 // SPDX-License-Identifier: MIT OR Apache-2.0
use anyhow::Result;
use crate::apdu::Le; use crate::apdu::Le;
use anyhow::Result;
#[allow(clippy::upper_case_acronyms)] #[allow(clippy::upper_case_acronyms)]
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -22,7 +22,13 @@ pub struct Command {
impl Command { impl Command {
pub fn new(cla: u8, ins: u8, p1: u8, p2: u8, data: Vec<u8>) -> Self { 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>> { pub(crate) fn serialize(&self, ext: Le) -> Result<Vec<u8>> {
@ -34,9 +40,11 @@ impl Command {
// (must be the same) // (must be the same)
let data_len = if self.data.len() as u16 > 0xff || ext == Le::Long { let data_len = if self.data.len() as u16 > 0xff || ext == Le::Long {
vec![0, vec![
0,
(self.data.len() as u16 >> 8) as u8, (self.data.len() as u16 >> 8) as u8,
(self.data.len() as u16 & 255) as u8] (self.data.len() as u16 & 255) as u8,
]
} else { } else {
vec![self.data.len() as u8] vec![self.data.len() as u8]
}; };
@ -56,7 +64,6 @@ impl Command {
// thus disable Le in this case. */ // thus disable Le in this case. */
// if (reader_table[slot].is_t0) // if (reader_table[slot].is_t0)
// le = -1; // le = -1;
Le::None => (), Le::None => (),
Le::Short => buf.push(0), Le::Short => buf.push(0),

View file

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

View file

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

View file

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

View file

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

View file

@ -100,17 +100,16 @@ pub enum OcErrorStatus {
#[error("Unexpected response length: {0}")] #[error("Unexpected response length: {0}")]
ResponseLength(usize), ResponseLength(usize),
} }
impl From<(u8, u8)> for OcErrorStatus { impl From<(u8, u8)> for OcErrorStatus {
fn from(status: (u8, u8)) -> Self { fn from(status: (u8, u8)) -> Self {
match (status.0, status.1) { match (status.0, status.1) {
(0x62, 0x85) => OcErrorStatus::TerminationState, (0x62, 0x85) => OcErrorStatus::TerminationState,
(0x63, 0xC0..=0xCF) => (0x63, 0xC0..=0xCF) => {
OcErrorStatus::PasswordNotChecked(status.1 & 0xf), OcErrorStatus::PasswordNotChecked(status.1 & 0xf)
(0x64, 0x02..=0x80) => }
OcErrorStatus::TriggeringByCard(status.1), (0x64, 0x02..=0x80) => OcErrorStatus::TriggeringByCard(status.1),
(0x65, 0x01) => OcErrorStatus::MemoryFailure, (0x65, 0x01) => OcErrorStatus::MemoryFailure,
(0x66, 0x00) => OcErrorStatus::SecurityRelatedIssues, (0x66, 0x00) => OcErrorStatus::SecurityRelatedIssues,
(0x67, 0x00) => OcErrorStatus::WrongLength, (0x67, 0x00) => OcErrorStatus::WrongLength,
@ -130,7 +129,7 @@ impl From<(u8, u8)> for OcErrorStatus {
(0x6D, 0x00) => OcErrorStatus::INSNotSupported, (0x6D, 0x00) => OcErrorStatus::INSNotSupported,
(0x6E, 0x00) => OcErrorStatus::CLANotSupported, (0x6E, 0x00) => OcErrorStatus::CLANotSupported,
(0x6F, 0x00) => OcErrorStatus::NoPreciseDiagnosis, (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 anyhow::{anyhow, Result};
use crate::{KeyType, OpenPGPCardAdmin, tlv,
CardUploadableKey, EccKey, RSAKey, EccType, PrivateKeyMaterial};
use crate::apdu; use crate::apdu;
use crate::apdu::commands;
use crate::apdu::command::Command; use crate::apdu::command::Command;
use crate::apdu::commands;
use crate::apdu::Le; use crate::apdu::Le;
use crate::errors::OpenpgpCardError;
use crate::parse::algo_attrs::{Algo, RsaAttrs}; use crate::parse::algo_attrs::{Algo, RsaAttrs};
use crate::parse::algo_info::AlgoInfo; use crate::parse::algo_info::AlgoInfo;
use crate::tlv::{Tlv, TlvEntry, tag::Tag}; use crate::tlv::{tag::Tag, Tlv, TlvEntry};
use crate::errors::OpenpgpCardError; use crate::{
tlv, CardUploadableKey, EccKey, EccType, KeyType, OpenPGPCardAdmin,
PrivateKeyMaterial, RSAKey,
};
/// Upload an explicitly selected Key to the card as a specific KeyType. /// Upload an explicitly selected Key to the card as a specific KeyType.
/// ///
@ -27,8 +28,7 @@ pub(crate) fn upload_key(
// be cached // be cached
let algo_list = oca.list_supported_algo()?; let algo_list = oca.list_supported_algo()?;
let (algo_cmd, key_cmd) = let (algo_cmd, key_cmd) = match key.get_key()? {
match key.get_key()? {
PrivateKeyMaterial::R(rsa_key) => { PrivateKeyMaterial::R(rsa_key) => {
// RSA bitsize // RSA bitsize
// (round up to 4-bytes, in case the key has 8+ leading zeros) // (round up to 4-bytes, in case the key has 8+ leading zeros)
@ -37,7 +37,8 @@ pub(crate) fn upload_key(
// FIXME: deal with absence of algo list (unwrap!) // FIXME: deal with absence of algo list (unwrap!)
// Get suitable algorithm from card's list // Get suitable algorithm from card's list
let algo = get_card_algo_rsa(algo_list.unwrap(), key_type, rsa_bits); 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 algo_cmd = rsa_algo_attrs_cmd(key_type, rsa_bits, &algo)?;
let key_cmd = rsa_key_cmd(key_type, rsa_key, &algo)?; let key_cmd = rsa_key_cmd(key_type, rsa_key, &algo)?;
@ -57,9 +58,11 @@ pub(crate) fn upload_key(
// // Error // // Error
// } // }
let algo_cmd = ecc_algo_attrs_cmd(key_type, let algo_cmd = ecc_algo_attrs_cmd(
key_type,
ecc_key.get_oid(), ecc_key.get_oid(),
ecc_key.get_type()); ecc_key.get_type(),
);
let key_cmd = ecc_key_cmd(ecc_key, key_type)?; let key_cmd = ecc_key_cmd(ecc_key, key_type)?;
@ -67,16 +70,24 @@ pub(crate) fn upload_key(
} }
}; };
copy_key_to_card(oca, key_type, key.get_ts(), key.get_fp(), algo_cmd, copy_key_to_card(
key_cmd)?; oca,
key_type,
key.get_ts(),
key.get_fp(),
algo_cmd,
key_cmd,
)?;
Ok(()) Ok(())
} }
// FIXME: refactor, these checks currently pointlessly duplicate code // FIXME: refactor, these checks currently pointlessly duplicate code
fn get_card_algo_rsa(algo_list: AlgoInfo, key_type: KeyType, rsa_bits: u16) fn get_card_algo_rsa(
-> RsaAttrs { algo_list: AlgoInfo,
key_type: KeyType,
rsa_bits: u16,
) -> RsaAttrs {
// Find suitable algorithm parameters (from card's list of algorithms). // Find suitable algorithm parameters (from card's list of algorithms).
// FIXME: handle "no list available" (older cards?) // FIXME: handle "no list available" (older cards?)
// (Current algo parameters of the key slot should be used, then (?)) // (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 // Get Algos for this keytype
let keytype_algos: Vec<_> = algo_list.get_by_keytype(key_type); let keytype_algos: Vec<_> = algo_list.get_by_keytype(key_type);
// Get RSA algo attributes // Get RSA algo attributes
let rsa_algos: Vec<_> = keytype_algos.iter() let rsa_algos: Vec<_> = keytype_algos
.map(|a| .iter()
{ .map(|a| if let Algo::Rsa(r) = a { Some(r) } else { None })
if let Algo::Rsa(r) = a {
Some(r)
} else {
None
}
})
.flatten() .flatten()
.collect(); .collect();
// Filter card algorithms by rsa bitlength of the key we want to upload // Filter card algorithms by rsa bitlength of the key we want to upload
let algo: Vec<_> = rsa_algos.iter() let algo: Vec<_> =
.filter(|&a| a.len_n == rsa_bits) rsa_algos.iter().filter(|&a| a.len_n == rsa_bits).collect();
.collect();
// FIXME: handle error if no algo found // FIXME: handle error if no algo found
let algo = *algo[0]; 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 // 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). // Find suitable algorithm parameters (from card's list of algorithms).
// FIXME: handle "no list available" (older cards?) // FIXME: handle "no list available" (older cards?)
// (Current algo parameters of the key slot should be used, then (?)) // (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); let keytype_algos: Vec<_> = algo_list.get_by_keytype(key_type);
// Get attributes // Get attributes
let ecdh_algos: Vec<_> = keytype_algos.iter() let ecdh_algos: Vec<_> = keytype_algos
.map(|a| .iter()
{ .map(|a| if let Algo::Ecdh(e) = a { Some(e) } else { None })
if let Algo::Ecdh(e) = a {
Some(e)
} else {
None
}
})
.flatten() .flatten()
.collect(); .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 // FIXME: refactor, these checks currently pointlessly duplicate code
fn check_card_algo_ecdsa(algo_list: AlgoInfo, fn check_card_algo_ecdsa(
key_type: KeyType, oid: &[u8]) -> bool { algo_list: AlgoInfo,
key_type: KeyType,
oid: &[u8],
) -> bool {
// Find suitable algorithm parameters (from card's list of algorithms). // Find suitable algorithm parameters (from card's list of algorithms).
// FIXME: handle "no list available" (older cards?) // FIXME: handle "no list available" (older cards?)
// (Current algo parameters of the key slot should be used, then (?)) // (Current algo parameters of the key slot should be used, then (?))
@ -144,9 +149,9 @@ fn check_card_algo_ecdsa(algo_list: AlgoInfo,
let keytype_algos: Vec<_> = algo_list.get_by_keytype(key_type); let keytype_algos: Vec<_> = algo_list.get_by_keytype(key_type);
// Get attributes // Get attributes
let ecdsa_algos: Vec<_> = keytype_algos.iter() let ecdsa_algos: Vec<_> = keytype_algos
.map(|a| .iter()
{ .map(|a| {
if let Algo::Ecdsa(e) = a { if let Algo::Ecdsa(e) = a {
Some(e) Some(e)
} else { } else {
@ -161,8 +166,11 @@ fn check_card_algo_ecdsa(algo_list: AlgoInfo,
} }
// FIXME: refactor, these checks currently pointlessly duplicate code // FIXME: refactor, these checks currently pointlessly duplicate code
fn check_card_algo_eddsa(algo_list: AlgoInfo, fn check_card_algo_eddsa(
key_type: KeyType, oid: &[u8]) -> bool { algo_list: AlgoInfo,
key_type: KeyType,
oid: &[u8],
) -> bool {
// Find suitable algorithm parameters (from card's list of algorithms). // Find suitable algorithm parameters (from card's list of algorithms).
// FIXME: handle "no list available" (older cards?) // FIXME: handle "no list available" (older cards?)
// (Current algo parameters of the key slot should be used, then (?)) // (Current algo parameters of the key slot should be used, then (?))
@ -171,9 +179,9 @@ fn check_card_algo_eddsa(algo_list: AlgoInfo,
let keytype_algos: Vec<_> = algo_list.get_by_keytype(key_type); let keytype_algos: Vec<_> = algo_list.get_by_keytype(key_type);
// Get attributes // Get attributes
let eddsa_algos: Vec<_> = keytype_algos.iter() let eddsa_algos: Vec<_> = keytype_algos
.map(|a| .iter()
{ .map(|a| {
if let Algo::Eddsa(e) = a { if let Algo::Eddsa(e) = a {
Some(e) Some(e)
} else { } else {
@ -187,8 +195,10 @@ fn check_card_algo_eddsa(algo_list: AlgoInfo,
eddsa_algos.iter().any(|e| e.oid == oid) eddsa_algos.iter().any(|e| e.oid == oid)
} }
fn ecc_key_cmd(ecc_key: Box<dyn EccKey>, key_type: KeyType) fn ecc_key_cmd(
-> Result<Command, OpenpgpCardError> { ecc_key: Box<dyn EccKey>,
key_type: KeyType,
) -> Result<Command, OpenpgpCardError> {
let scalar_data = ecc_key.get_scalar(); let scalar_data = ecc_key.get_scalar();
let scalar_len = scalar_data.len() as u8; 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)?; let crt = get_crt(key_type)?;
// 2) "Cardholder private key template" (7F48) // 2) "Cardholder private key template" (7F48)
let cpkt = Tlv(Tag(vec![0x7F, 0x48]), let cpkt = Tlv(Tag(vec![0x7F, 0x48]), TlvEntry::S(vec![0x92, scalar_len]));
TlvEntry::S(vec![0x92, scalar_len]));
// 3) "Cardholder private key" (5F48) // 3) "Cardholder private key" (5F48)
let cpk = Tlv(Tag(vec![0x5F, 0x48]), TlvEntry::S(scalar_data.to_vec())); let cpk = Tlv(Tag(vec![0x5F, 0x48]), TlvEntry::S(scalar_data.to_vec()));
// "Extended header list (DO 4D)" (contains the three inner TLV) // "Extended header list (DO 4D)" (contains the three inner TLV)
let ehl = Tlv(Tag(vec![0x4d]), let ehl = Tlv(Tag(vec![0x4d]), TlvEntry::C(vec![crt, cpkt, cpk]));
TlvEntry::C(vec![crt, cpkt, cpk]));
// The key import uses a PUT DATA command with odd INS (DB) and an // 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 // Extended header list (DO 4D) as described in ISO 7816-8
Ok(Command::new(0x00, 0xDB, 0x3F, 0xFF, Ok(Command::new(
ehl.serialize().to_vec())) 0x00,
0xDB,
0x3F,
0xFF,
ehl.serialize().to_vec(),
))
} }
fn get_crt(key_type: KeyType) -> Result<Tlv, OpenpgpCardError> { 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::Decryption => 0xB8,
KeyType::Signing => 0xB6, KeyType::Signing => 0xB6,
KeyType::Authentication => 0xA4, 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![]))) Ok(Tlv(Tag(vec![tag]), TlvEntry::S(vec![])))
} }
fn rsa_key_cmd(key_type: KeyType, fn rsa_key_cmd(
key_type: KeyType,
rsa_key: Box<dyn RSAKey>, rsa_key: Box<dyn RSAKey>,
algo_attrs: &RsaAttrs) -> Result<Command, OpenpgpCardError> { algo_attrs: &RsaAttrs,
) -> Result<Command, OpenpgpCardError> {
// Assemble key command, which contains three sub-TLV: // Assemble key command, which contains three sub-TLV:
// 1) "Control Reference Template" // 1) "Control Reference Template"
let crt = get_crt(key_type)?; let crt = get_crt(key_type)?;
// 2) "Cardholder private key template" (7F48) // 2) "Cardholder private key template" (7F48)
// "describes the input and the length of the content of the following DO" // "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)); let cpkt = Tlv(Tag(vec![0x7F, 0x48]), TlvEntry::S(value));
// 3) "Cardholder private key" (5F48) // 3) "Cardholder private key" (5F48)
// //
// "represents a concatenation of the key data elements according to // "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)); let cpk = Tlv(Tag(vec![0x5F, 0x48]), TlvEntry::S(keydata));
// "Extended header list (DO 4D)" // "Extended header list (DO 4D)"
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 // 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 // Extended header list (DO 4D) as described in ISO 7816-8
Ok(Command::new(0x00, 0xDB, 0x3F, 0xFF, Ok(Command::new(
ehl.serialize().to_vec())) 0x00,
0xDB,
0x3F,
0xFF,
ehl.serialize().to_vec(),
))
} }
/// Set algorithm attributes [4.4.3.9 Algorithm Attributes] /// Set algorithm attributes [4.4.3.9 Algorithm Attributes]
fn rsa_algo_attrs_cmd(key_type: KeyType, fn rsa_algo_attrs_cmd(
key_type: KeyType,
rsa_bits: u16, rsa_bits: u16,
algo_attrs: &RsaAttrs) -> algo_attrs: &RsaAttrs,
Result<Command> { ) -> Result<Command> {
// Algorithm ID (01 = RSA (Encrypt or Sign)) // Algorithm ID (01 = RSA (Encrypt or Sign))
let mut algo_attributes = vec![0x01]; let mut algo_attributes = vec![0x01];
@ -318,19 +334,26 @@ fn rsa_algo_attrs_cmd(key_type: KeyType,
// Import-Format of private key // Import-Format of private key
// (This fn currently assumes import_format "00 = standard (e, p, q)") // (This fn currently assumes import_format "00 = standard (e, p, q)")
if algo_attrs.import_format != 0 { if algo_attrs.import_format != 0 {
return Err( return Err(anyhow!(
anyhow!("Unexpected RSA input format (only 0 is supported)")); "Unexpected RSA input format (only 0 is supported)"
));
} }
algo_attributes.push(algo_attrs.import_format); algo_attributes.push(algo_attrs.import_format);
// Command to PUT the algorithm attributes // 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] /// Set algorithm attributes [4.4.3.9 Algorithm Attributes]
fn ecc_algo_attrs_cmd(key_type: KeyType, oid: &[u8], ecc_type: EccType) fn ecc_algo_attrs_cmd(
-> Command { key_type: KeyType,
oid: &[u8],
ecc_type: EccType,
) -> Command {
let algo_id = match ecc_type { let algo_id = match ecc_type {
EccType::EdDSA => 0x16, EccType::EdDSA => 0x16,
EccType::ECDH => 0x12, 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) commands::put_data(&[key_type.get_algorithm_tag()], algo_attributes)
} }
fn copy_key_to_card(oca: &OpenPGPCardAdmin, fn copy_key_to_card(
oca: &OpenPGPCardAdmin,
key_type: KeyType, key_type: KeyType,
ts: u64, ts: u64,
fp: Vec<u8>, fp: Vec<u8>,
algo_cmd: Command, algo_cmd: Command,
key_cmd: Command) key_cmd: Command,
-> Result<(), OpenpgpCardError> { ) -> Result<(), OpenpgpCardError> {
let fp_cmd = let fp_cmd = commands::put_data(&[key_type.get_fingerprint_put_tag()], fp);
commands::put_data(&[key_type.get_fingerprint_put_tag()], fp);
// Timestamp update // Timestamp update
let time_value: Vec<u8> = ts let time_value: Vec<u8> = ts
@ -366,7 +389,6 @@ fn copy_key_to_card(oca: &OpenPGPCardAdmin,
let time_cmd = let time_cmd =
commands::put_data(&[key_type.get_timestamp_put_tag()], time_value); commands::put_data(&[key_type.get_timestamp_put_tag()], time_value);
// Send all the commands // Send all the commands
let ext = Le::None; // FIXME?! let ext = Le::None; // FIXME?!

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,12 +1,12 @@
// SPDX-FileCopyrightText: 2021 Heiko Schaefer <heiko@schaefer.name> // SPDX-FileCopyrightText: 2021 Heiko Schaefer <heiko@schaefer.name>
// SPDX-License-Identifier: MIT OR Apache-2.0 // SPDX-License-Identifier: MIT OR Apache-2.0
use anyhow::anyhow;
use nom::{bytes::complete as bytes, combinator, sequence}; use nom::{bytes::complete as bytes, combinator, sequence};
use std::fmt; use std::fmt;
use anyhow::anyhow;
use crate::parse::KeySet;
use crate::errors::OpenpgpCardError; use crate::errors::OpenpgpCardError;
use crate::parse::KeySet;
#[derive(Clone, Eq, PartialEq)] #[derive(Clone, Eq, PartialEq)]
pub struct Fingerprint([u8; 20]); 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>> { 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> { pub fn from(input: &[u8]) -> Result<KeySet<Fingerprint>, OpenpgpCardError> {

View file

@ -1,8 +1,8 @@
// SPDX-FileCopyrightText: 2021 Heiko Schaefer <heiko@schaefer.name> // SPDX-FileCopyrightText: 2021 Heiko Schaefer <heiko@schaefer.name>
// SPDX-License-Identifier: MIT OR Apache-2.0 // SPDX-License-Identifier: MIT OR Apache-2.0
use anyhow::{anyhow, Result};
use crate::errors::OpenpgpCardError; use crate::errors::OpenpgpCardError;
use anyhow::{anyhow, Result};
#[derive(Debug)] #[derive(Debug)]
pub struct CardCapabilities { pub struct CardCapabilities {
@ -24,7 +24,6 @@ impl CardCapabilities {
self.extended_length_information self.extended_length_information
} }
pub fn from(data: [u8; 3]) -> Self { pub fn from(data: [u8; 3]) -> Self {
let byte3 = data[2]; let byte3 = data[2];
@ -32,7 +31,11 @@ impl CardCapabilities {
let extended_lc_le = byte3 & 0x40 != 0; let extended_lc_le = byte3 & 0x40 != 0;
let extended_length_information = byte3 & 0x20 != 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,13 +112,14 @@ impl Historical {
cc = Some([ctlv[1], ctlv[2], ctlv[3]]); cc = Some([ctlv[1], ctlv[2], ctlv[3]]);
ctlv.drain(0..4); ctlv.drain(0..4);
} }
0 => { ctlv.drain(0..1); } 0 => {
_ => unimplemented!("unexpected tlv in historical bytes") ctlv.drain(0..1);
}
_ => unimplemented!("unexpected tlv in historical bytes"),
} }
} }
let sib = let sib = match data[len - 3] {
match data[len - 3] {
0 => { 0 => {
// Card does not offer life cycle management, commands // Card does not offer life cycle management, commands
// TERMINATE DF and ACTIVATE FILE are not supported // TERMINATE DF and ACTIVATE FILE are not supported
@ -134,8 +138,11 @@ impl Historical {
5 5
} }
_ => { _ => {
return Err(anyhow!("unexpected status indicator in \ return Err(anyhow!(
historical bytes").into()); "unexpected status indicator in \
historical bytes"
)
.into());
} }
}; };
@ -148,8 +155,8 @@ impl Historical {
Ok(Self { cib, csd, cc, sib }) Ok(Self { cib, csd, cc, sib })
} else { } else {
Err(anyhow!("Unexpected category indicator in historical \ Err(anyhow!("Unexpected category indicator in historical bytes")
bytes").into()) .into())
} }
} }
} }

View file

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

View file

@ -1,7 +1,10 @@
// SPDX-FileCopyrightText: 2021 Heiko Schaefer <heiko@schaefer.name> // SPDX-FileCopyrightText: 2021 Heiko Schaefer <heiko@schaefer.name>
// SPDX-License-Identifier: MIT OR Apache-2.0 // 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> { fn length1(input: &[u8]) -> nom::IResult<&[u8], u8> {
combinator::verify(number::u8, |&c| c < 0x80)(input) 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 length = crate::tlv::tlv_encode_length(value.len() as u16);
let mut ser = Vec::new(); let mut ser = Vec::new();
ser.extend(self.0.0.iter()); ser.extend(self.0 .0.iter());
ser.extend(length.iter()); ser.extend(length.iter());
ser.extend(value.iter()); ser.extend(value.iter());
ser ser
@ -114,44 +114,45 @@ impl TlvEntry {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::{Tag, Tlv}; use super::{Tag, Tlv};
use hex_literal::hex;
use anyhow::Result;
use crate::tlv::TlvEntry; use crate::tlv::TlvEntry;
use anyhow::Result;
use hex_literal::hex;
#[test] #[test]
fn test_tlv() -> Result<()> { fn test_tlv() -> Result<()> {
// From OpenPGP card spec § 7.2.6 // From OpenPGP card spec § 7.2.6
let data = hex!("5B0B546573743C3C54657374695F2D0264655F350131") let data =
.to_vec(); hex!("5B0B546573743C3C54657374695F2D0264655F350131").to_vec();
let (input, tlv) = Tlv::parse(&data).unwrap(); let (input, tlv) = Tlv::parse(&data).unwrap();
assert_eq!(tlv, assert_eq!(
Tlv(Tag::from([0x5b]), tlv,
TlvEntry::S(hex!("546573743C3C5465737469") Tlv(
.to_vec()))); Tag::from([0x5b]),
TlvEntry::S(hex!("546573743C3C5465737469").to_vec())
)
);
let (input, tlv) = Tlv::parse(input).unwrap(); let (input, tlv) = Tlv::parse(input).unwrap();
assert_eq!(tlv, assert_eq!(
Tlv(Tag::from([0x5f, 0x2d]), tlv,
TlvEntry::S(hex!("6465") Tlv(Tag::from([0x5f, 0x2d]), TlvEntry::S(hex!("6465").to_vec()))
.to_vec()))); );
let (input, tlv) = Tlv::parse(input).unwrap(); let (input, tlv) = Tlv::parse(input).unwrap();
assert_eq!(tlv, assert_eq!(
Tlv(Tag::from([0x5f, 0x35]), tlv,
TlvEntry::S(hex!("31") Tlv(Tag::from([0x5f, 0x35]), TlvEntry::S(hex!("31").to_vec()))
.to_vec()))); );
assert!(input.is_empty()); assert!(input.is_empty());
Ok(()) Ok(())
} }
#[test] #[test]
fn test_tlv_yubi5() -> Result<()> { fn test_tlv_yubi5() -> Result<()> {
// 'Yubikey 5 NFC' output for GET DATA on "Application Related Data" // 'Yubikey 5 NFC' output for GET DATA on "Application Related Data"
@ -168,7 +169,10 @@ mod test {
assert_eq!(entry.serialize(), hex!("7d000bfe080000ff0000")); assert_eq!(entry.serialize(), hex!("7d000bfe080000ff0000"));
let entry = tlv.find(&Tag::from([0x4f])).unwrap(); 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(); let entry = tlv.find(&Tag::from([0x5f, 0x52])).unwrap();
assert_eq!(entry.serialize(), hex!("00730000e0059000")); assert_eq!(entry.serialize(), hex!("00730000e0059000"));
@ -203,9 +207,11 @@ mod test {
let entry = tlv.find(&Tag::from([0xc6])).unwrap(); let entry = tlv.find(&Tag::from([0xc6])).unwrap();
assert_eq!(entry.serialize(), hex!("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")); assert_eq!(entry.serialize(), hex!("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"));
let entry = tlv.find(&Tag::from([0xcd])).unwrap(); 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(); let entry = tlv.find(&Tag::from([0xde])).unwrap();
assert_eq!(entry.serialize(), hex!("0100020003008102")); assert_eq!(entry.serialize(), hex!("0100020003008102"));
@ -234,17 +240,22 @@ mod test {
// but has been abridged and changed. It does not represent a // but has been abridged and changed. It does not represent a
// complete valid OpenPGP card DO! // complete valid OpenPGP card DO!
let a = Tlv(Tag::from(&[0x7F, 0x48][..]), let a =
TlvEntry::S(vec![0x92, 0x03])); Tlv(Tag::from(&[0x7F, 0x48][..]), TlvEntry::S(vec![0x92, 0x03]));
let b = Tlv(Tag::from(&[0x5F, 0x48][..]), let b = Tlv(
TlvEntry::S(vec![0x1, 0x2, 0x3])); Tag::from(&[0x5F, 0x48][..]),
TlvEntry::S(vec![0x1, 0x2, 0x3]),
);
let tlv = Tlv(Tag::from(&[0x4d][..]), let tlv = Tlv(Tag::from(&[0x4d][..]), TlvEntry::C(vec![a, b]));
TlvEntry::C(vec![a, b]));
assert_eq!(tlv.serialize(), &[0x4d, 0xb, assert_eq!(
0x7f, 0x48, 0x2, 0x92, 0x3, tlv.serialize(),
0x5f, 0x48, 0x3, 0x1, 0x2, 0x3]); &[
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-FileCopyrightText: 2021 Heiko Schaefer <heiko@schaefer.name>
// SPDX-License-Identifier: MIT OR Apache-2.0 // 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)] #[derive(Clone, Debug, Eq, PartialEq)]
pub struct Tag(pub Vec<u8>); 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]> { 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> { fn multi_byte_tag_first(input: &[u8]) -> nom::IResult<&[u8], u8> {
@ -61,11 +66,8 @@ fn multi_byte_tag_rest(input: &[u8]) -> nom::IResult<&[u8], &[u8]> {
} }
fn single_byte_rest(input: &[u8]) -> nom::IResult<&[u8], &[u8]> { fn single_byte_rest(input: &[u8]) -> nom::IResult<&[u8], &[u8]> {
combinator::verify(bytes::take(1u8), combinator::verify(bytes::take(1u8), |c: &[u8]| {
|c: &[u8]| { c.len() == 1 && is_first(&c[0]) && is_last(&c[0])
c.len() == 1 &&
is_first(&c[0]) &&
is_last(&c[0])
})(input) })(input)
} }
@ -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]> { fn single_byte_tag(input: &[u8]) -> nom::IResult<&[u8], &[u8]> {
combinator::verify(bytes::take(1u8), combinator::verify(bytes::take(1u8), |c: &[u8]| {
|c: &[u8]| { c.len() == 1 && !is_multi_byte_tag_first(&c[0])
c.len() == 1 &&
!is_multi_byte_tag_first(&c[0])
})(input) })(input)
} }
pub(super) fn tag(input: &[u8]) -> nom::IResult<&[u8], Tag> { pub(super) fn tag(input: &[u8]) -> nom::IResult<&[u8], Tag> {
combinator::map(branch::alt((multi_byte_tag, single_byte_tag)), combinator::map(branch::alt((multi_byte_tag, single_byte_tag)), Tag::from)(
Tag::from)(input) input,
)
} }
#[cfg(test)] #[cfg(test)]
mod test { mod test {
#[test] #[test]