Implement methods on the CardClient trait rather than on dyn CardClient
.
This patch is largely based on input and an MR by @nwalfield.
This commit is contained in:
parent
05d5af4c3c
commit
5b2979874b
3 changed files with 165 additions and 183 deletions
|
@ -22,11 +22,14 @@ const MAX_BUFFER_SIZE: usize = 264;
|
||||||
///
|
///
|
||||||
/// 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(
|
pub(crate) fn send_command<C: ?Sized>(
|
||||||
card_client: &mut dyn CardClient,
|
card_client: &mut C,
|
||||||
cmd: Command,
|
cmd: Command,
|
||||||
expect_reply: bool,
|
expect_reply: bool,
|
||||||
) -> Result<RawResponse, Error> {
|
) -> Result<RawResponse, Error>
|
||||||
|
where
|
||||||
|
C: CardClient,
|
||||||
|
{
|
||||||
let mut resp = RawResponse::try_from(send_command_low_level(
|
let mut resp = RawResponse::try_from(send_command_low_level(
|
||||||
card_client,
|
card_client,
|
||||||
cmd.clone(),
|
cmd.clone(),
|
||||||
|
@ -81,11 +84,14 @@ pub(crate) fn send_command(
|
||||||
///
|
///
|
||||||
/// 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 to re-assemble the chained response-parts.
|
/// needs to re-assemble the chained response-parts.
|
||||||
fn send_command_low_level(
|
fn send_command_low_level<C: ?Sized>(
|
||||||
card_client: &mut dyn CardClient,
|
card_client: &mut C,
|
||||||
cmd: Command,
|
cmd: Command,
|
||||||
expect_response: Expect,
|
expect_response: Expect,
|
||||||
) -> Result<Vec<u8>, Error> {
|
) -> Result<Vec<u8>, Error>
|
||||||
|
where
|
||||||
|
C: CardClient,
|
||||||
|
{
|
||||||
let (ext_support, chaining_support, mut max_cmd_bytes, max_rsp_bytes) =
|
let (ext_support, chaining_support, mut max_cmd_bytes, max_rsp_bytes) =
|
||||||
if let Some(caps) = card_client.card_caps() {
|
if let Some(caps) = card_client.card_caps() {
|
||||||
log::debug!("found card caps data!");
|
log::debug!("found card caps data!");
|
||||||
|
|
|
@ -29,8 +29,8 @@ use crate::{CardClient, Error};
|
||||||
///
|
///
|
||||||
/// `fp_from_pub` calculates the fingerprint for a public key data object and
|
/// `fp_from_pub` calculates the fingerprint for a public key data object and
|
||||||
/// creation timestamp
|
/// creation timestamp
|
||||||
pub(crate) fn gen_key_with_metadata(
|
pub(crate) fn gen_key_with_metadata<C: ?Sized>(
|
||||||
card_client: &mut dyn CardClient,
|
card_client: &mut C,
|
||||||
fp_from_pub: fn(
|
fp_from_pub: fn(
|
||||||
&PublicKeyMaterial,
|
&PublicKeyMaterial,
|
||||||
KeyGenerationTime,
|
KeyGenerationTime,
|
||||||
|
@ -38,7 +38,10 @@ pub(crate) fn gen_key_with_metadata(
|
||||||
) -> Result<Fingerprint, Error>,
|
) -> Result<Fingerprint, Error>,
|
||||||
key_type: KeyType,
|
key_type: KeyType,
|
||||||
algo: Option<&Algo>,
|
algo: Option<&Algo>,
|
||||||
) -> Result<(PublicKeyMaterial, KeyGenerationTime), Error> {
|
) -> Result<(PublicKeyMaterial, KeyGenerationTime), Error>
|
||||||
|
where
|
||||||
|
C: CardClient,
|
||||||
|
{
|
||||||
// Set algo on card if it's Some
|
// Set algo on card if it's Some
|
||||||
if let Some(target_algo) = algo {
|
if let Some(target_algo) = algo {
|
||||||
// FIXME: caching
|
// FIXME: caching
|
||||||
|
@ -126,10 +129,13 @@ fn tlv_to_pubkey(tlv: &Tlv, algo: &Algo) -> Result<PublicKeyMaterial> {
|
||||||
///
|
///
|
||||||
/// This runs the low level key generation primitive on the card.
|
/// This runs the low level key generation primitive on the card.
|
||||||
/// (This does not set algorithm attributes, creation time or fingerprint)
|
/// (This does not set algorithm attributes, creation time or fingerprint)
|
||||||
pub(crate) fn generate_asymmetric_key_pair(
|
pub(crate) fn generate_asymmetric_key_pair<C: ?Sized>(
|
||||||
card_client: &mut dyn CardClient,
|
card_client: &mut C,
|
||||||
key_type: KeyType,
|
key_type: KeyType,
|
||||||
) -> Result<Tlv, Error> {
|
) -> Result<Tlv, Error>
|
||||||
|
where
|
||||||
|
C: CardClient,
|
||||||
|
{
|
||||||
// generate key
|
// generate key
|
||||||
let crt = control_reference_template(key_type)?;
|
let crt = control_reference_template(key_type)?;
|
||||||
let gen_key_cmd = commands::gen_key(crt.serialize().to_vec());
|
let gen_key_cmd = commands::gen_key(crt.serialize().to_vec());
|
||||||
|
@ -148,10 +154,13 @@ pub(crate) fn generate_asymmetric_key_pair(
|
||||||
/// in the card or imported")
|
/// in the card or imported")
|
||||||
///
|
///
|
||||||
/// (See 7.2.14 GENERATE ASYMMETRIC KEY PAIR)
|
/// (See 7.2.14 GENERATE ASYMMETRIC KEY PAIR)
|
||||||
pub(crate) fn public_key(
|
pub(crate) fn public_key<C: ?Sized>(
|
||||||
card_client: &mut dyn CardClient,
|
card_client: &mut C,
|
||||||
key_type: KeyType,
|
key_type: KeyType,
|
||||||
) -> Result<PublicKeyMaterial, Error> {
|
) -> Result<PublicKeyMaterial, Error>
|
||||||
|
where
|
||||||
|
C: CardClient,
|
||||||
|
{
|
||||||
// get current algo
|
// get current algo
|
||||||
let ard = card_client.application_related_data()?; // FIXME: caching
|
let ard = card_client.application_related_data()?; // FIXME: caching
|
||||||
let algo = ard.algorithm_attributes(key_type)?;
|
let algo = ard.algorithm_attributes(key_type)?;
|
||||||
|
@ -174,12 +183,15 @@ pub(crate) fn public_key(
|
||||||
/// If the key is suitable for `key_type`, an Error is returned (either
|
/// If the key is suitable for `key_type`, an Error is returned (either
|
||||||
/// caused by checks before attempting to upload the key to the card, or by
|
/// caused by checks before attempting to upload the key to the card, or by
|
||||||
/// an error that the card reports during an attempt to upload the key).
|
/// an error that the card reports during an attempt to upload the key).
|
||||||
pub(crate) fn key_import(
|
pub(crate) fn key_import<C: ?Sized>(
|
||||||
card_client: &mut dyn CardClient,
|
card_client: &mut C,
|
||||||
key: Box<dyn CardUploadableKey>,
|
key: Box<dyn CardUploadableKey>,
|
||||||
key_type: KeyType,
|
key_type: KeyType,
|
||||||
algo_info: Option<AlgoInfo>,
|
algo_info: Option<AlgoInfo>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error>
|
||||||
|
where
|
||||||
|
C: CardClient,
|
||||||
|
{
|
||||||
// FIXME: caching?
|
// FIXME: caching?
|
||||||
let ard = card_client.application_related_data()?;
|
let ard = card_client.application_related_data()?;
|
||||||
|
|
||||||
|
|
|
@ -103,24 +103,9 @@ pub trait CardClient {
|
||||||
|
|
||||||
/// Modify the PIN `id` via the reader pinpad
|
/// Modify the PIN `id` via the reader pinpad
|
||||||
fn pinpad_modify(&mut self, id: u8) -> Result<Vec<u8>>;
|
fn pinpad_modify(&mut self, id: u8) -> Result<Vec<u8>>;
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Deref for dyn CardClient + Send + Sync + 'a {
|
|
||||||
type Target = dyn CardClient + 'a;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<'a> DerefMut for dyn CardClient + Send + Sync + 'a {
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> dyn CardClient + 'a {
|
|
||||||
/// Select the OpenPGP card application
|
/// Select the OpenPGP card application
|
||||||
pub fn select(&mut self) -> Result<Response, Error> {
|
fn select(&mut self) -> Result<Response, Error> {
|
||||||
let select_openpgp = commands::select_openpgp();
|
let select_openpgp = commands::select_openpgp();
|
||||||
apdu::send_command(self, select_openpgp, false)?.try_into()
|
apdu::send_command(self, select_openpgp, false)?.try_into()
|
||||||
}
|
}
|
||||||
|
@ -133,21 +118,12 @@ impl<'a> dyn CardClient + 'a {
|
||||||
/// This fn initializes the CardCaps by requesting
|
/// This fn initializes the CardCaps by requesting
|
||||||
/// application_related_data from the card, and setting the
|
/// application_related_data from the card, and setting the
|
||||||
/// capabilities accordingly.
|
/// capabilities accordingly.
|
||||||
pub fn initialize(&mut self) -> Result<()> {
|
fn initialize(&mut self) -> Result<()> {
|
||||||
let ard = self.application_related_data()?;
|
let ard = self.application_related_data()?;
|
||||||
self.init_caps(&ard)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Initialize the CardCaps settings from the data in `ard`.
|
|
||||||
///
|
|
||||||
/// This should be done at an early point, soon after opening the card.
|
|
||||||
fn init_caps(&mut self, ard: &ApplicationRelatedData) -> Result<()> {
|
|
||||||
// Determine chaining/extended length support from card
|
// Determine chaining/extended length support from card
|
||||||
// metadata and cache this information in CardApp (as a
|
// metadata and cache this information in the CardClient
|
||||||
// CardCaps)
|
// implementation (as a CardCaps)
|
||||||
|
|
||||||
let mut ext_support = false;
|
let mut ext_support = false;
|
||||||
let mut chaining_support = false;
|
let mut chaining_support = false;
|
||||||
|
|
||||||
|
@ -202,9 +178,7 @@ impl<'a> dyn CardClient + 'a {
|
||||||
/// (This data should probably be cached in a higher layer. Some parts of
|
/// (This data should probably be cached in a higher layer. Some parts of
|
||||||
/// it are needed regularly, and it does not usually change during
|
/// it are needed regularly, and it does not usually change during
|
||||||
/// normal use of a card.)
|
/// normal use of a card.)
|
||||||
pub fn application_related_data(
|
fn application_related_data(&mut self) -> Result<ApplicationRelatedData> {
|
||||||
&mut self,
|
|
||||||
) -> Result<ApplicationRelatedData> {
|
|
||||||
let ad = commands::application_related_data();
|
let ad = commands::application_related_data();
|
||||||
let resp = apdu::send_command(self, ad, true)?;
|
let resp = apdu::send_command(self, ad, true)?;
|
||||||
let value = Value::from(resp.data()?, true)?;
|
let value = Value::from(resp.data()?, true)?;
|
||||||
|
@ -214,49 +188,47 @@ impl<'a> dyn CardClient + 'a {
|
||||||
Ok(ApplicationRelatedData(Tlv::new(Tag::from([0x6E]), value)))
|
Ok(ApplicationRelatedData(Tlv::new(Tag::from([0x6E]), value)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
// #[allow(dead_code)]
|
||||||
fn ca_fingerprints() {
|
// fn ca_fingerprints() {
|
||||||
unimplemented!()
|
// unimplemented!()
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
#[allow(dead_code)]
|
// #[allow(dead_code)]
|
||||||
fn key_information() {
|
// fn key_information() {
|
||||||
unimplemented!()
|
// unimplemented!()
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
#[allow(dead_code)]
|
// #[allow(dead_code)]
|
||||||
fn uif_pso_cds() {
|
// fn uif_pso_cds() {
|
||||||
unimplemented!()
|
// unimplemented!()
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
#[allow(dead_code)]
|
// #[allow(dead_code)]
|
||||||
fn uif_pso_dec() {
|
// fn uif_pso_dec() {
|
||||||
unimplemented!()
|
// unimplemented!()
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
#[allow(dead_code)]
|
// #[allow(dead_code)]
|
||||||
fn uif_pso_aut() {
|
// fn uif_pso_aut() {
|
||||||
unimplemented!()
|
// unimplemented!()
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
#[allow(dead_code)]
|
// #[allow(dead_code)]
|
||||||
fn uif_attestation() {
|
// fn uif_attestation() {
|
||||||
unimplemented!()
|
// unimplemented!()
|
||||||
}
|
// }
|
||||||
|
|
||||||
// --- login data (5e) ---
|
// --- login data (5e) ---
|
||||||
|
|
||||||
/// Get URL (5f50)
|
/// Get URL (5f50)
|
||||||
pub fn url(&mut self) -> Result<Vec<u8>> {
|
fn url(&mut self) -> Result<Vec<u8>> {
|
||||||
let resp = apdu::send_command(self, commands::url(), true)?;
|
let resp = apdu::send_command(self, commands::url(), true)?;
|
||||||
|
|
||||||
Ok(resp.data()?.to_vec())
|
Ok(resp.data()?.to_vec())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get cardholder related data (65)
|
/// Get cardholder related data (65)
|
||||||
pub fn cardholder_related_data(
|
fn cardholder_related_data(&mut self) -> Result<CardholderRelatedData> {
|
||||||
&mut self,
|
|
||||||
) -> Result<CardholderRelatedData> {
|
|
||||||
let crd = commands::cardholder_related_data();
|
let crd = commands::cardholder_related_data();
|
||||||
let resp = apdu::send_command(self, crd, true)?;
|
let resp = apdu::send_command(self, crd, true)?;
|
||||||
resp.check_ok()?;
|
resp.check_ok()?;
|
||||||
|
@ -265,7 +237,7 @@ impl<'a> dyn CardClient + 'a {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get security support template (7a)
|
/// Get security support template (7a)
|
||||||
pub fn security_support_template(
|
fn security_support_template(
|
||||||
&mut self,
|
&mut self,
|
||||||
) -> Result<SecuritySupportTemplate> {
|
) -> Result<SecuritySupportTemplate> {
|
||||||
let sst = commands::security_support_template();
|
let sst = commands::security_support_template();
|
||||||
|
@ -302,7 +274,7 @@ impl<'a> dyn CardClient + 'a {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get "Algorithm Information"
|
/// Get "Algorithm Information"
|
||||||
pub fn algorithm_information(&mut self) -> Result<Option<AlgoInfo>> {
|
fn algorithm_information(&mut self) -> Result<Option<AlgoInfo>> {
|
||||||
let resp = apdu::send_command(self, commands::algo_info(), true)?;
|
let resp = apdu::send_command(self, commands::algo_info(), true)?;
|
||||||
resp.check_ok()?;
|
resp.check_ok()?;
|
||||||
|
|
||||||
|
@ -311,7 +283,7 @@ impl<'a> dyn CardClient + 'a {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Firmware Version (YubiKey specific (?))
|
/// Firmware Version (YubiKey specific (?))
|
||||||
pub fn firmware_version(&mut self) -> Result<Vec<u8>> {
|
fn firmware_version(&mut self) -> Result<Vec<u8>> {
|
||||||
let resp =
|
let resp =
|
||||||
apdu::send_command(self, commands::firmware_version(), true)?;
|
apdu::send_command(self, commands::firmware_version(), true)?;
|
||||||
|
|
||||||
|
@ -322,7 +294,7 @@ impl<'a> dyn CardClient + 'a {
|
||||||
/// [see:
|
/// [see:
|
||||||
/// <https://docs.nitrokey.com/start/linux/multiple-identities.html>
|
/// <https://docs.nitrokey.com/start/linux/multiple-identities.html>
|
||||||
/// <https://github.com/Nitrokey/nitrokey-start-firmware/pull/33/>]
|
/// <https://github.com/Nitrokey/nitrokey-start-firmware/pull/33/>]
|
||||||
pub fn set_identity(&mut self, id: u8) -> Result<Vec<u8>> {
|
fn set_identity(&mut self, id: u8) -> Result<Vec<u8>> {
|
||||||
let resp = apdu::send_command(self, commands::set_identity(id), false);
|
let resp = apdu::send_command(self, commands::set_identity(id), false);
|
||||||
|
|
||||||
// Apparently it's normal to get "NotTransacted" from pcsclite when
|
// Apparently it's normal to get "NotTransacted" from pcsclite when
|
||||||
|
@ -336,11 +308,7 @@ impl<'a> dyn CardClient + 'a {
|
||||||
|
|
||||||
/// SELECT DATA ("select a DO in the current template",
|
/// SELECT DATA ("select a DO in the current template",
|
||||||
/// e.g. for cardholder certificate)
|
/// e.g. for cardholder certificate)
|
||||||
pub fn select_data(
|
fn select_data(&mut self, num: u8, tag: &[u8]) -> Result<Response, Error> {
|
||||||
&mut self,
|
|
||||||
num: u8,
|
|
||||||
tag: &[u8],
|
|
||||||
) -> Result<Response, Error> {
|
|
||||||
let tlv = Tlv::new(
|
let tlv = Tlv::new(
|
||||||
[0x60],
|
[0x60],
|
||||||
Value::C(vec![Tlv::new([0x5c], Value::S(tag.to_vec()))]),
|
Value::C(vec![Tlv::new([0x5c], Value::S(tag.to_vec()))]),
|
||||||
|
@ -357,7 +325,7 @@ impl<'a> dyn CardClient + 'a {
|
||||||
/// Get data from "private use" DO.
|
/// Get data from "private use" DO.
|
||||||
///
|
///
|
||||||
/// `num` must be between 1 and 4.
|
/// `num` must be between 1 and 4.
|
||||||
pub fn private_use_do(&mut self, num: u8) -> Result<Vec<u8>> {
|
fn private_use_do(&mut self, num: u8) -> Result<Vec<u8>> {
|
||||||
assert!((1..=4).contains(&num));
|
assert!((1..=4).contains(&num));
|
||||||
|
|
||||||
let cmd = commands::private_use_do(num);
|
let cmd = commands::private_use_do(num);
|
||||||
|
@ -373,7 +341,7 @@ impl<'a> dyn CardClient + 'a {
|
||||||
/// Access condition:
|
/// Access condition:
|
||||||
/// - 1/3 need PW1 (82)
|
/// - 1/3 need PW1 (82)
|
||||||
/// - 2/4 need PW3
|
/// - 2/4 need PW3
|
||||||
pub fn set_private_use_do(
|
fn set_private_use_do(
|
||||||
&mut self,
|
&mut self,
|
||||||
num: u8,
|
num: u8,
|
||||||
data: Vec<u8>,
|
data: Vec<u8>,
|
||||||
|
@ -403,7 +371,7 @@ impl<'a> dyn CardClient + 'a {
|
||||||
/// (However, e.g. vanilla Gnuk doesn't support this functionality.
|
/// (However, e.g. vanilla Gnuk doesn't support this functionality.
|
||||||
/// Gnuk needs to be built with the `--enable-factory-reset`
|
/// Gnuk needs to be built with the `--enable-factory-reset`
|
||||||
/// option to the `configure` script to enable this functionality).
|
/// option to the `configure` script to enable this functionality).
|
||||||
pub fn factory_reset(&mut self) -> Result<()> {
|
fn factory_reset(&mut self) -> Result<()> {
|
||||||
// send 4 bad requests to verify pw1
|
// send 4 bad requests to verify pw1
|
||||||
// [apdu 00 20 00 81 08 40 40 40 40 40 40 40 40]
|
// [apdu 00 20 00 81 08 40 40 40 40 40 40 40 40]
|
||||||
for _ in 0..4 {
|
for _ in 0..4 {
|
||||||
|
@ -446,22 +414,12 @@ impl<'a> dyn CardClient + 'a {
|
||||||
|
|
||||||
// --- verify/modify ---
|
// --- verify/modify ---
|
||||||
|
|
||||||
/// Does the cardreader support direct pinpad verify?
|
|
||||||
pub fn feature_pinpad_verify(card_client: &mut dyn CardClient) -> bool {
|
|
||||||
card_client.feature_pinpad_verify()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Does the cardreader support direct pinpad modify?
|
|
||||||
pub fn feature_pinpad_modify(card_client: &mut dyn CardClient) -> bool {
|
|
||||||
card_client.feature_pinpad_modify()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Verify pw1 (user) for signing operation (mode 81).
|
/// Verify pw1 (user) for signing operation (mode 81).
|
||||||
///
|
///
|
||||||
/// Depending on the PW1 status byte (see Extended Capabilities) this
|
/// Depending on the PW1 status byte (see Extended Capabilities) this
|
||||||
/// access condition is only valid for one PSO:CDS command or remains
|
/// access condition is only valid for one PSO:CDS command or remains
|
||||||
/// valid for several attempts.
|
/// valid for several attempts.
|
||||||
pub fn verify_pw1_for_signing(
|
fn verify_pw1_for_signing(
|
||||||
&mut self,
|
&mut self,
|
||||||
pin: &str,
|
pin: &str,
|
||||||
) -> Result<Response, Error> {
|
) -> Result<Response, Error> {
|
||||||
|
@ -476,9 +434,7 @@ impl<'a> dyn CardClient + 'a {
|
||||||
/// Depending on the PW1 status byte (see Extended Capabilities) this
|
/// Depending on the PW1 status byte (see Extended Capabilities) this
|
||||||
/// access condition is only valid for one PSO:CDS command or remains
|
/// access condition is only valid for one PSO:CDS command or remains
|
||||||
/// valid for several attempts.
|
/// valid for several attempts.
|
||||||
pub fn verify_pw1_for_signing_pinpad(
|
fn verify_pw1_for_signing_pinpad(&mut self) -> Result<Response, Error> {
|
||||||
&mut self,
|
|
||||||
) -> Result<Response, Error> {
|
|
||||||
let res = self.pinpad_verify(0x81)?;
|
let res = self.pinpad_verify(0x81)?;
|
||||||
RawResponse::try_from(res)?.try_into()
|
RawResponse::try_from(res)?.try_into()
|
||||||
}
|
}
|
||||||
|
@ -489,14 +445,14 @@ impl<'a> dyn CardClient + 'a {
|
||||||
///
|
///
|
||||||
/// (Note: some cards don't correctly implement this feature,
|
/// (Note: some cards don't correctly implement this feature,
|
||||||
/// e.g. YubiKey 5)
|
/// e.g. YubiKey 5)
|
||||||
pub fn check_pw1_for_signing(&mut self) -> Result<Response, Error> {
|
fn check_pw1_for_signing(&mut self) -> Result<Response, Error> {
|
||||||
let verify = commands::verify_pw1_81(vec![]);
|
let verify = commands::verify_pw1_81(vec![]);
|
||||||
apdu::send_command(self, verify, false)?.try_into()
|
apdu::send_command(self, verify, false)?.try_into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Verify PW1 (user).
|
/// Verify PW1 (user).
|
||||||
/// (For operations except signing, mode 82).
|
/// (For operations except signing, mode 82).
|
||||||
pub fn verify_pw1(&mut self, pin: &str) -> Result<Response, Error> {
|
fn verify_pw1(&mut self, pin: &str) -> Result<Response, Error> {
|
||||||
let verify = commands::verify_pw1_82(pin.as_bytes().to_vec());
|
let verify = commands::verify_pw1_82(pin.as_bytes().to_vec());
|
||||||
apdu::send_command(self, verify, false)?.try_into()
|
apdu::send_command(self, verify, false)?.try_into()
|
||||||
}
|
}
|
||||||
|
@ -505,7 +461,7 @@ impl<'a> dyn CardClient + 'a {
|
||||||
/// using a pinpad on the card reader. If no usable pinpad is found,
|
/// using a pinpad on the card reader. If no usable pinpad is found,
|
||||||
/// an error is returned.
|
/// an error is returned.
|
||||||
|
|
||||||
pub fn verify_pw1_pinpad(&mut self) -> Result<Response, Error> {
|
fn verify_pw1_pinpad(&mut self) -> Result<Response, Error> {
|
||||||
let res = self.pinpad_verify(0x82)?;
|
let res = self.pinpad_verify(0x82)?;
|
||||||
RawResponse::try_from(res)?.try_into()
|
RawResponse::try_from(res)?.try_into()
|
||||||
}
|
}
|
||||||
|
@ -517,20 +473,20 @@ impl<'a> dyn CardClient + 'a {
|
||||||
///
|
///
|
||||||
/// (Note: some cards don't correctly implement this feature,
|
/// (Note: some cards don't correctly implement this feature,
|
||||||
/// e.g. YubiKey 5)
|
/// e.g. YubiKey 5)
|
||||||
pub fn check_pw1(&mut self) -> Result<Response, Error> {
|
fn check_pw1(&mut self) -> Result<Response, Error> {
|
||||||
let verify = commands::verify_pw1_82(vec![]);
|
let verify = commands::verify_pw1_82(vec![]);
|
||||||
apdu::send_command(self, verify, false)?.try_into()
|
apdu::send_command(self, verify, false)?.try_into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Verify PW3 (admin).
|
/// Verify PW3 (admin).
|
||||||
pub fn verify_pw3(&mut self, pin: &str) -> Result<Response, Error> {
|
fn verify_pw3(&mut self, pin: &str) -> Result<Response, Error> {
|
||||||
let verify = commands::verify_pw3(pin.as_bytes().to_vec());
|
let verify = commands::verify_pw3(pin.as_bytes().to_vec());
|
||||||
apdu::send_command(self, verify, false)?.try_into()
|
apdu::send_command(self, verify, false)?.try_into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Verify PW3 (admin) using a pinpad on the card reader. If no usable
|
/// Verify PW3 (admin) using a pinpad on the card reader. If no usable
|
||||||
/// pinpad is found, an error is returned.
|
/// pinpad is found, an error is returned.
|
||||||
pub fn verify_pw3_pinpad(&mut self) -> Result<Response, Error> {
|
fn verify_pw3_pinpad(&mut self) -> Result<Response, Error> {
|
||||||
let res = self.pinpad_verify(0x83)?;
|
let res = self.pinpad_verify(0x83)?;
|
||||||
RawResponse::try_from(res)?.try_into()
|
RawResponse::try_from(res)?.try_into()
|
||||||
}
|
}
|
||||||
|
@ -541,7 +497,7 @@ impl<'a> dyn CardClient + 'a {
|
||||||
///
|
///
|
||||||
/// (Note: some cards don't correctly implement this feature,
|
/// (Note: some cards don't correctly implement this feature,
|
||||||
/// e.g. YubiKey 5)
|
/// e.g. YubiKey 5)
|
||||||
pub fn check_pw3(&mut self) -> Result<Response, Error> {
|
fn check_pw3(&mut self) -> Result<Response, Error> {
|
||||||
let verify = commands::verify_pw3(vec![]);
|
let verify = commands::verify_pw3(vec![]);
|
||||||
apdu::send_command(self, verify, false)?.try_into()
|
apdu::send_command(self, verify, false)?.try_into()
|
||||||
}
|
}
|
||||||
|
@ -549,11 +505,7 @@ impl<'a> dyn CardClient + 'a {
|
||||||
/// Change the value of PW1 (user password).
|
/// Change the value of PW1 (user password).
|
||||||
///
|
///
|
||||||
/// The current value of PW1 must be presented in `old` for authorization.
|
/// The current value of PW1 must be presented in `old` for authorization.
|
||||||
pub fn change_pw1(
|
fn change_pw1(&mut self, old: &str, new: &str) -> Result<Response, Error> {
|
||||||
&mut self,
|
|
||||||
old: &str,
|
|
||||||
new: &str,
|
|
||||||
) -> Result<Response, Error> {
|
|
||||||
let mut data = vec![];
|
let mut data = vec![];
|
||||||
data.extend(old.as_bytes());
|
data.extend(old.as_bytes());
|
||||||
data.extend(new.as_bytes());
|
data.extend(new.as_bytes());
|
||||||
|
@ -564,7 +516,7 @@ impl<'a> dyn CardClient + 'a {
|
||||||
|
|
||||||
/// Change the value of PW1 (user password) using a pinpad on the
|
/// Change the value of PW1 (user password) using a pinpad on the
|
||||||
/// card reader. If no usable pinpad is found, an error is returned.
|
/// card reader. If no usable pinpad is found, an error is returned.
|
||||||
pub fn change_pw1_pinpad(&mut self) -> Result<Response, Error> {
|
fn change_pw1_pinpad(&mut self) -> Result<Response, Error> {
|
||||||
let res = self.pinpad_modify(0x81)?;
|
let res = self.pinpad_modify(0x81)?;
|
||||||
RawResponse::try_from(res)?.try_into()
|
RawResponse::try_from(res)?.try_into()
|
||||||
}
|
}
|
||||||
|
@ -572,11 +524,7 @@ impl<'a> dyn CardClient + 'a {
|
||||||
/// Change the value of PW3 (admin password).
|
/// Change the value of PW3 (admin password).
|
||||||
///
|
///
|
||||||
/// The current value of PW3 must be presented in `old` for authorization.
|
/// The current value of PW3 must be presented in `old` for authorization.
|
||||||
pub fn change_pw3(
|
fn change_pw3(&mut self, old: &str, new: &str) -> Result<Response, Error> {
|
||||||
&mut self,
|
|
||||||
old: &str,
|
|
||||||
new: &str,
|
|
||||||
) -> Result<Response, Error> {
|
|
||||||
let mut data = vec![];
|
let mut data = vec![];
|
||||||
data.extend(old.as_bytes());
|
data.extend(old.as_bytes());
|
||||||
data.extend(new.as_bytes());
|
data.extend(new.as_bytes());
|
||||||
|
@ -587,7 +535,7 @@ impl<'a> dyn CardClient + 'a {
|
||||||
|
|
||||||
/// Change the value of PW3 (admin password) using a pinpad on the
|
/// Change the value of PW3 (admin password) using a pinpad on the
|
||||||
/// card reader. If no usable pinpad is found, an error is returned.
|
/// card reader. If no usable pinpad is found, an error is returned.
|
||||||
pub fn change_pw3_pinpad(&mut self) -> Result<Response, Error> {
|
fn change_pw3_pinpad(&mut self) -> Result<Response, Error> {
|
||||||
let res = self.pinpad_modify(0x83)?;
|
let res = self.pinpad_modify(0x83)?;
|
||||||
RawResponse::try_from(res)?.try_into()
|
RawResponse::try_from(res)?.try_into()
|
||||||
}
|
}
|
||||||
|
@ -599,7 +547,7 @@ impl<'a> dyn CardClient + 'a {
|
||||||
/// - PW3 must have been verified previously,
|
/// - PW3 must have been verified previously,
|
||||||
/// - secure messaging must be currently used,
|
/// - secure messaging must be currently used,
|
||||||
/// - the resetting_code must be presented.
|
/// - the resetting_code must be presented.
|
||||||
pub fn reset_retry_counter_pw1(
|
fn reset_retry_counter_pw1(
|
||||||
&mut self,
|
&mut self,
|
||||||
new_pw1: Vec<u8>,
|
new_pw1: Vec<u8>,
|
||||||
resetting_code: Option<Vec<u8>>,
|
resetting_code: Option<Vec<u8>>,
|
||||||
|
@ -614,7 +562,7 @@ impl<'a> dyn CardClient + 'a {
|
||||||
///
|
///
|
||||||
/// (This is a wrapper around the low-level pso_decipher
|
/// (This is a wrapper around the low-level pso_decipher
|
||||||
/// operation, it builds the required `data` field from `dm`)
|
/// operation, it builds the required `data` field from `dm`)
|
||||||
pub fn decipher(&mut self, dm: Cryptogram) -> Result<Vec<u8>, Error> {
|
fn decipher(&mut self, dm: Cryptogram) -> Result<Vec<u8>, Error> {
|
||||||
match dm {
|
match dm {
|
||||||
Cryptogram::RSA(message) => {
|
Cryptogram::RSA(message) => {
|
||||||
// "Padding indicator byte (00) for RSA" (pg. 69)
|
// "Padding indicator byte (00) for RSA" (pg. 69)
|
||||||
|
@ -647,6 +595,9 @@ impl<'a> dyn CardClient + 'a {
|
||||||
|
|
||||||
/// Run decryption operation on the smartcard (low level operation)
|
/// Run decryption operation on the smartcard (low level operation)
|
||||||
/// (7.2.11 PSO: DECIPHER)
|
/// (7.2.11 PSO: DECIPHER)
|
||||||
|
///
|
||||||
|
/// (consider using the `decipher()` method if you don't want to create
|
||||||
|
/// the data field manually)
|
||||||
fn pso_decipher(&mut self, data: Vec<u8>) -> Result<Vec<u8>, Error> {
|
fn pso_decipher(&mut self, data: Vec<u8>) -> Result<Vec<u8>, Error> {
|
||||||
// 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);
|
||||||
|
@ -658,34 +609,6 @@ impl<'a> dyn CardClient + 'a {
|
||||||
|
|
||||||
// --- sign ---
|
// --- sign ---
|
||||||
|
|
||||||
fn digestinfo(hash: Hash) -> Vec<u8> {
|
|
||||||
match hash {
|
|
||||||
Hash::SHA256(_) | Hash::SHA384(_) | Hash::SHA512(_) => {
|
|
||||||
let tlv = Tlv::new(
|
|
||||||
[0x30],
|
|
||||||
Value::C(vec![
|
|
||||||
Tlv::new(
|
|
||||||
[0x30],
|
|
||||||
Value::C(vec![
|
|
||||||
Tlv::new(
|
|
||||||
[0x06],
|
|
||||||
// unwrapping is ok, for SHA*
|
|
||||||
Value::S(hash.oid().unwrap().to_vec()),
|
|
||||||
),
|
|
||||||
Tlv::new([0x05], Value::S(vec![])),
|
|
||||||
]),
|
|
||||||
),
|
|
||||||
Tlv::new([0x04], Value::S(hash.digest().to_vec())),
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
|
|
||||||
tlv.serialize()
|
|
||||||
}
|
|
||||||
Hash::EdDSA(d) => d.to_vec(),
|
|
||||||
Hash::ECDSA(d) => d.to_vec(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sign `hash`, on the card.
|
/// Sign `hash`, on the card.
|
||||||
///
|
///
|
||||||
/// This is a wrapper around the low-level
|
/// This is a wrapper around the low-level
|
||||||
|
@ -697,16 +620,16 @@ impl<'a> dyn CardClient + 'a {
|
||||||
///
|
///
|
||||||
/// With ECC the hash data is processed as is, using
|
/// With ECC the hash data is processed as is, using
|
||||||
/// pso_compute_digital_signature.
|
/// pso_compute_digital_signature.
|
||||||
pub fn signature_for_hash(
|
fn signature_for_hash(&mut self, hash: Hash) -> Result<Vec<u8>, Error> {
|
||||||
&mut self,
|
self.pso_compute_digital_signature(digestinfo(hash))
|
||||||
hash: Hash,
|
|
||||||
) -> Result<Vec<u8>, Error> {
|
|
||||||
self.pso_compute_digital_signature(Self::digestinfo(hash))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Run signing operation on the smartcard (low level operation)
|
/// Run signing operation on the smartcard (low level operation)
|
||||||
/// (7.2.10 PSO: COMPUTE DIGITAL SIGNATURE)
|
/// (7.2.10 PSO: COMPUTE DIGITAL SIGNATURE)
|
||||||
pub fn pso_compute_digital_signature(
|
///
|
||||||
|
/// (consider using the `signature_for_hash()` method if you don't
|
||||||
|
/// want to create the data field manually)
|
||||||
|
fn pso_compute_digital_signature(
|
||||||
&mut self,
|
&mut self,
|
||||||
data: Vec<u8>,
|
data: Vec<u8>,
|
||||||
) -> Result<Vec<u8>, Error> {
|
) -> Result<Vec<u8>, Error> {
|
||||||
|
@ -729,16 +652,16 @@ impl<'a> dyn CardClient + 'a {
|
||||||
/// (see 7.2.10.2 DigestInfo for RSA).
|
/// (see 7.2.10.2 DigestInfo for RSA).
|
||||||
///
|
///
|
||||||
/// With ECC the hash data is processed as is.
|
/// With ECC the hash data is processed as is.
|
||||||
pub fn authenticate_for_hash(
|
fn authenticate_for_hash(&mut self, hash: Hash) -> Result<Vec<u8>, Error> {
|
||||||
&mut self,
|
self.internal_authenticate(digestinfo(hash))
|
||||||
hash: Hash,
|
|
||||||
) -> Result<Vec<u8>, Error> {
|
|
||||||
self.internal_authenticate(Self::digestinfo(hash))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Run signing operation on the smartcard (low level operation)
|
/// Run signing operation on the smartcard (low level operation)
|
||||||
/// (7.2.13 INTERNAL AUTHENTICATE)
|
/// (7.2.13 INTERNAL AUTHENTICATE)
|
||||||
pub fn internal_authenticate(
|
///
|
||||||
|
/// (consider using the `authenticate_for_hash()` method if you don't
|
||||||
|
/// want to create the data field manually)
|
||||||
|
fn internal_authenticate(
|
||||||
&mut self,
|
&mut self,
|
||||||
data: Vec<u8>,
|
data: Vec<u8>,
|
||||||
) -> Result<Vec<u8>, Error> {
|
) -> Result<Vec<u8>, Error> {
|
||||||
|
@ -750,12 +673,12 @@ impl<'a> dyn CardClient + 'a {
|
||||||
|
|
||||||
// --- admin ---
|
// --- admin ---
|
||||||
|
|
||||||
pub fn set_name(&mut self, name: &[u8]) -> Result<Response, Error> {
|
fn set_name(&mut self, name: &[u8]) -> Result<Response, Error> {
|
||||||
let put_name = commands::put_name(name.to_vec());
|
let put_name = commands::put_name(name.to_vec());
|
||||||
apdu::send_command(self, put_name, false)?.try_into()
|
apdu::send_command(self, put_name, false)?.try_into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_lang(&mut self, lang: &[Lang]) -> Result<Response, Error> {
|
fn set_lang(&mut self, lang: &[Lang]) -> Result<Response, Error> {
|
||||||
let bytes: Vec<u8> = lang
|
let bytes: Vec<u8> = lang
|
||||||
.iter()
|
.iter()
|
||||||
.map(|&l| Into::<Vec<u8>>::into(l))
|
.map(|&l| Into::<Vec<u8>>::into(l))
|
||||||
|
@ -766,17 +689,17 @@ impl<'a> dyn CardClient + 'a {
|
||||||
apdu::send_command(self, put_lang, false)?.try_into()
|
apdu::send_command(self, put_lang, false)?.try_into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_sex(&mut self, sex: Sex) -> Result<Response, Error> {
|
fn set_sex(&mut self, sex: Sex) -> Result<Response, Error> {
|
||||||
let put_sex = commands::put_sex((&sex).into());
|
let put_sex = commands::put_sex((&sex).into());
|
||||||
apdu::send_command(self, put_sex, false)?.try_into()
|
apdu::send_command(self, put_sex, false)?.try_into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_url(&mut self, url: &[u8]) -> Result<Response, Error> {
|
fn set_url(&mut self, url: &[u8]) -> Result<Response, Error> {
|
||||||
let put_url = commands::put_url(url.to_vec());
|
let put_url = commands::put_url(url.to_vec());
|
||||||
apdu::send_command(self, put_url, false)?.try_into()
|
apdu::send_command(self, put_url, false)?.try_into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_creation_time(
|
fn set_creation_time(
|
||||||
&mut self,
|
&mut self,
|
||||||
time: KeyGenerationTime,
|
time: KeyGenerationTime,
|
||||||
key_type: KeyType,
|
key_type: KeyType,
|
||||||
|
@ -796,7 +719,7 @@ impl<'a> dyn CardClient + 'a {
|
||||||
apdu::send_command(self, time_cmd, false)?.try_into()
|
apdu::send_command(self, time_cmd, false)?.try_into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_fingerprint(
|
fn set_fingerprint(
|
||||||
&mut self,
|
&mut self,
|
||||||
fp: Fingerprint,
|
fp: Fingerprint,
|
||||||
key_type: KeyType,
|
key_type: KeyType,
|
||||||
|
@ -820,7 +743,7 @@ impl<'a> dyn CardClient + 'a {
|
||||||
/// can also be changed.
|
/// can also be changed.
|
||||||
///
|
///
|
||||||
/// (See OpenPGP card spec, pg. 28)
|
/// (See OpenPGP card spec, pg. 28)
|
||||||
pub fn set_pw_status_bytes(
|
fn set_pw_status_bytes(
|
||||||
&mut self,
|
&mut self,
|
||||||
pw_status: &PWStatusBytes,
|
pw_status: &PWStatusBytes,
|
||||||
long: bool,
|
long: bool,
|
||||||
|
@ -835,7 +758,7 @@ impl<'a> dyn CardClient + 'a {
|
||||||
///
|
///
|
||||||
/// Call select_data() before calling this fn, to select a particular
|
/// Call select_data() before calling this fn, to select a particular
|
||||||
/// certificate (if the card supports multiple certificates).
|
/// certificate (if the card supports multiple certificates).
|
||||||
pub fn set_cardholder_certificate(
|
fn set_cardholder_certificate(
|
||||||
&mut self,
|
&mut self,
|
||||||
data: Vec<u8>,
|
data: Vec<u8>,
|
||||||
) -> Result<Response, Error> {
|
) -> Result<Response, Error> {
|
||||||
|
@ -845,7 +768,7 @@ impl<'a> dyn CardClient + 'a {
|
||||||
|
|
||||||
/// Set algorithm attributes
|
/// Set algorithm attributes
|
||||||
/// (4.4.3.9 Algorithm Attributes)
|
/// (4.4.3.9 Algorithm Attributes)
|
||||||
pub fn set_algorithm_attributes(
|
fn set_algorithm_attributes(
|
||||||
&mut self,
|
&mut self,
|
||||||
key_type: KeyType,
|
key_type: KeyType,
|
||||||
algo: &Algo,
|
algo: &Algo,
|
||||||
|
@ -861,7 +784,7 @@ impl<'a> dyn CardClient + 'a {
|
||||||
|
|
||||||
/// Set resetting code
|
/// Set resetting code
|
||||||
/// (4.3.4 Resetting Code)
|
/// (4.3.4 Resetting Code)
|
||||||
pub fn set_resetting_code(
|
fn set_resetting_code(
|
||||||
&mut self,
|
&mut self,
|
||||||
resetting_code: Vec<u8>,
|
resetting_code: Vec<u8>,
|
||||||
) -> Result<Response, Error> {
|
) -> Result<Response, Error> {
|
||||||
|
@ -871,7 +794,7 @@ impl<'a> dyn CardClient + 'a {
|
||||||
|
|
||||||
/// Import an existing private key to the card.
|
/// Import an existing private key to the card.
|
||||||
/// (This implicitly sets the algorithm info, fingerprint and timestamp)
|
/// (This implicitly sets the algorithm info, fingerprint and timestamp)
|
||||||
pub fn key_import(
|
fn key_import(
|
||||||
&mut self,
|
&mut self,
|
||||||
key: Box<dyn CardUploadableKey>,
|
key: Box<dyn CardUploadableKey>,
|
||||||
key_type: KeyType,
|
key_type: KeyType,
|
||||||
|
@ -894,7 +817,7 @@ impl<'a> dyn CardClient + 'a {
|
||||||
/// Note: `algo` needs to precisely specify the RSA bitsize of e (if
|
/// Note: `algo` needs to precisely specify the RSA bitsize of e (if
|
||||||
/// applicable), and import format, with values that the current card
|
/// applicable), and import format, with values that the current card
|
||||||
/// supports.
|
/// supports.
|
||||||
pub fn generate_key(
|
fn generate_key(
|
||||||
&mut self,
|
&mut self,
|
||||||
fp_from_pub: fn(
|
fp_from_pub: fn(
|
||||||
&PublicKeyMaterial,
|
&PublicKeyMaterial,
|
||||||
|
@ -916,7 +839,7 @@ impl<'a> dyn CardClient + 'a {
|
||||||
/// Note: AlgoSimple doesn't specify card specific details (such as
|
/// Note: AlgoSimple doesn't specify card specific details (such as
|
||||||
/// bitsize of e for RSA, and import format). This function determines
|
/// bitsize of e for RSA, and import format). This function determines
|
||||||
/// these values based on information from the card.
|
/// these values based on information from the card.
|
||||||
pub fn generate_key_simple(
|
fn generate_key_simple(
|
||||||
&mut self,
|
&mut self,
|
||||||
fp_from_pub: fn(
|
fp_from_pub: fn(
|
||||||
&PublicKeyMaterial,
|
&PublicKeyMaterial,
|
||||||
|
@ -946,7 +869,7 @@ impl<'a> dyn CardClient + 'a {
|
||||||
/// Note also that the information from the card is insufficient to
|
/// Note also that the information from the card is insufficient to
|
||||||
/// reconstruct a pre-existing OpenPGP public key that corresponds to
|
/// reconstruct a pre-existing OpenPGP public key that corresponds to
|
||||||
/// the private key on the card.
|
/// the private key on the card.
|
||||||
pub fn public_key(
|
fn public_key(
|
||||||
&mut self,
|
&mut self,
|
||||||
key_type: KeyType,
|
key_type: KeyType,
|
||||||
) -> Result<PublicKeyMaterial, Error> {
|
) -> Result<PublicKeyMaterial, Error> {
|
||||||
|
@ -954,6 +877,47 @@ impl<'a> dyn CardClient + 'a {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> Deref for dyn CardClient + Send + Sync + 'a {
|
||||||
|
type Target = dyn CardClient + 'a;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'a> DerefMut for dyn CardClient + Send + Sync + 'a {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn digestinfo(hash: Hash) -> Vec<u8> {
|
||||||
|
match hash {
|
||||||
|
Hash::SHA256(_) | Hash::SHA384(_) | Hash::SHA512(_) => {
|
||||||
|
let tlv = Tlv::new(
|
||||||
|
[0x30],
|
||||||
|
Value::C(vec![
|
||||||
|
Tlv::new(
|
||||||
|
[0x30],
|
||||||
|
Value::C(vec![
|
||||||
|
Tlv::new(
|
||||||
|
[0x06],
|
||||||
|
// unwrapping is ok, for SHA*
|
||||||
|
Value::S(hash.oid().unwrap().to_vec()),
|
||||||
|
),
|
||||||
|
Tlv::new([0x05], Value::S(vec![])),
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
Tlv::new([0x04], Value::S(hash.digest().to_vec())),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
|
||||||
|
tlv.serialize()
|
||||||
|
}
|
||||||
|
Hash::EdDSA(d) => d.to_vec(),
|
||||||
|
Hash::ECDSA(d) => d.to_vec(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Configuration of the capabilities of a card.
|
/// Configuration of the capabilities of a card.
|
||||||
///
|
///
|
||||||
/// This configuration is used to determine e.g. if chaining or extended
|
/// This configuration is used to determine e.g. if chaining or extended
|
||||||
|
|
Loading…
Reference in a new issue