rustfmt
This commit is contained in:
parent
959342bb5a
commit
f00865ab75
25 changed files with 1225 additions and 794 deletions
9
.rustfmt.toml
Normal file
9
.rustfmt.toml
Normal 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"
|
||||||
|
|
|
@ -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<()> {
|
||||||
|
|
|
@ -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")
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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?!
|
||||||
|
|
|
@ -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,
|
]
|
||||||
]);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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)))
|
||||||
]
|
])
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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][..]))
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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)))
|
||||||
}
|
}
|
||||||
|
|
|
@ -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> {
|
||||||
|
|
|
@ -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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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> {
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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]
|
||||||
|
|
Loading…
Reference in a new issue