use std::convert::TryFrom;
use std::time::{SystemTime, UNIX_EPOCH};
use crate::algorithm::{
AlgorithmAttributes, AlgorithmInformation, Curve, EccAttributes, RsaAttributes,
};
use crate::apdu::command::Command;
use crate::card_do::{Fingerprint, KeyGenerationTime};
use crate::commands;
use crate::crypto_data::{
CardUploadableKey, EccKey, EccPub, EccType, PrivateKeyMaterial, PublicKeyMaterial, RSAKey,
RSAPub,
};
use crate::tags::Tags;
use crate::tlv::{length::tlv_encode_length, value::Value, Tlv};
use crate::{Error, KeyType, Tag, Transaction};
pub(crate) fn gen_key_set_metadata(
card_tx: &mut Transaction,
fp_from_pub: fn(&PublicKeyMaterial, KeyGenerationTime, KeyType) -> Result<Fingerprint, Error>,
algorithm_attributes: &AlgorithmAttributes,
key_type: KeyType,
) -> Result<(PublicKeyMaterial, KeyGenerationTime), Error> {
let time = SystemTime::now();
let ts = time
.duration_since(UNIX_EPOCH)
.map_err(|e| Error::InternalError(format!("This should never happen {e}")))?
.as_secs() as u32;
let tlv = generate_asymmetric_key_pair(card_tx, key_type)?;
let pubkey = tlv_to_pubkey(&tlv, algorithm_attributes)?;
log::trace!("public {:x?}", pubkey);
let ts = ts.into();
card_tx.set_creation_time(ts, key_type)?;
let fp = fp_from_pub(&pubkey, ts, key_type)?;
card_tx.set_fingerprint(fp, key_type)?;
Ok((pubkey, ts))
}
fn tlv_to_pubkey(tlv: &Tlv, algo: &AlgorithmAttributes) -> Result<PublicKeyMaterial, crate::Error> {
let n = tlv.find(Tags::PublicKeyDataRsaModulus);
let v = tlv.find(Tags::PublicKeyDataRsaExponent);
let ec = tlv.find(Tags::PublicKeyDataEccPoint);
match (n, v, ec) {
(Some(n), Some(v), None) => {
let rsa = RSAPub::new(n.serialize(), v.serialize());
Ok(PublicKeyMaterial::R(rsa))
}
(None, None, Some(ec)) => {
let data = ec.serialize();
log::trace!("EC --- len {}, data {:x?}", data.len(), data);
let ecc = EccPub::new(data, algo.clone());
Ok(PublicKeyMaterial::E(ecc))
}
(_, _, _) => Err(Error::UnsupportedAlgo(format!(
"Unexpected public key material from card {tlv:?}"
))),
}
}
pub(crate) fn generate_asymmetric_key_pair(
card_tx: &mut Transaction,
key_type: KeyType,
) -> Result<Tlv, Error> {
log::info!("OpenPgpTransaction: generate_asymmetric_key_pair");
let crt = control_reference_template(key_type)?;
let gen_key_cmd = commands::gen_key(crt.serialize().to_vec());
let resp = card_tx.send_command(gen_key_cmd, true)?;
resp.check_ok()?;
let tlv = Tlv::try_from(resp.data()?)?;
Ok(tlv)
}
pub(crate) fn public_key(
card_tx: &mut Transaction,
key_type: KeyType,
) -> Result<PublicKeyMaterial, Error> {
log::info!("OpenPgpTransaction: public_key");
let ard = card_tx.application_related_data()?; let algo = ard.algorithm_attributes(key_type)?;
let crt = control_reference_template(key_type)?;
let get_pub_key_cmd = commands::get_pub_key(crt.serialize().to_vec());
let resp = card_tx.send_command(get_pub_key_cmd, true)?;
resp.check_ok()?;
let tlv = Tlv::try_from(resp.data()?)?;
let pubkey = tlv_to_pubkey(&tlv, &algo)?;
Ok(pubkey)
}
pub(crate) fn key_import(
card_tx: &mut Transaction,
key: Box<dyn CardUploadableKey>,
key_type: KeyType,
) -> Result<(), Error> {
log::info!("OpenPgpTransaction: key_import");
match key.private_key()? {
PrivateKeyMaterial::R(rsa_key) => key_import_rsa(
card_tx,
key_type,
rsa_key,
key.fingerprint()?,
key.timestamp(),
),
PrivateKeyMaterial::E(ecc_key) => key_import_ecc(
card_tx,
key_type,
ecc_key,
key.fingerprint()?,
key.timestamp(),
),
}
}
fn key_import_rsa(
card_tx: &mut Transaction,
key_type: KeyType,
rsa_key: Box<dyn RSAKey>,
fp: Fingerprint,
ts: KeyGenerationTime,
) -> Result<(), Error> {
let algo_info = card_tx.algorithm_information_cached().ok().flatten();
let rsa_bits = (((rsa_key.n().len() * 8 + 31) / 32) * 32) as u16;
let ard = card_tx.application_related_data()?;
let algo_attr = ard.algorithm_attributes(key_type)?;
let rsa_attrs = determine_rsa_attrs(rsa_bits, key_type, algo_attr, algo_info)?;
let import_cmd = rsa_key_import_cmd(key_type, rsa_key, &rsa_attrs)?;
import_key_set_metadata(
card_tx,
key_type,
import_cmd,
fp,
ts,
AlgorithmAttributes::Rsa(rsa_attrs),
)
}
fn key_import_ecc(
card_tx: &mut Transaction,
key_type: KeyType,
ecc_key: Box<dyn EccKey>,
fp: Fingerprint,
ts: KeyGenerationTime,
) -> Result<(), Error> {
let algo_info = card_tx.algorithm_information_cached().ok().flatten();
let ecc_attrs = determine_ecc_attrs(ecc_key.oid(), ecc_key.ecc_type(), key_type, algo_info)?;
let import_cmd = ecc_key_import_cmd(key_type, ecc_key, &ecc_attrs)?;
import_key_set_metadata(
card_tx,
key_type,
import_cmd,
fp,
ts,
AlgorithmAttributes::Ecc(ecc_attrs),
)
}
fn import_key_set_metadata(
card_tx: &mut Transaction,
key_type: KeyType,
import_cmd: Command,
fp: Fingerprint,
ts: KeyGenerationTime,
algorithm_attributes: AlgorithmAttributes,
) -> Result<(), Error> {
log::info!("Import key material");
if card_tx.extended_capabilities()?.algo_attrs_changeable() {
card_tx.set_algorithm_attributes(key_type, &algorithm_attributes)?;
}
card_tx.send_command(import_cmd, false)?.check_ok()?;
card_tx.set_fingerprint(fp, key_type)?;
card_tx.set_creation_time(ts, key_type)?;
Ok(())
}
pub(crate) fn determine_rsa_attrs(
rsa_bits: u16,
key_type: KeyType,
algo_attr: AlgorithmAttributes,
algo_info: Option<AlgorithmInformation>,
) -> Result<RsaAttributes, Error> {
let rsa_attrs = if let Some(algo_info) = algo_info {
card_algo_rsa(algo_info, key_type, rsa_bits)?
} else {
if let AlgorithmAttributes::Rsa(rsa) = algo_attr {
RsaAttributes::new(rsa_bits, rsa.len_e(), rsa.import_format())
} else {
RsaAttributes::new(rsa_bits, 32, 0)
}
};
Ok(rsa_attrs)
}
pub(crate) fn determine_ecc_attrs(
oid: &[u8],
ecc_type: EccType,
key_type: KeyType,
algo_info: Option<AlgorithmInformation>,
) -> Result<EccAttributes, crate::Error> {
if let Some(algo_info) = algo_info {
let algos = check_card_algo_ecc(algo_info, key_type, oid);
if algos.is_empty() {
return Err(Error::UnsupportedAlgo(format!(
"Oid {oid:?} unsupported according to algo_info"
)));
}
if !algos.is_empty() {
return Ok(EccAttributes::new(
ecc_type,
Curve::try_from(oid)?,
algos[0].import_format(),
));
}
}
Ok(EccAttributes::new(ecc_type, Curve::try_from(oid)?, None))
}
fn card_algo_rsa(
algo_info: AlgorithmInformation,
key_type: KeyType,
rsa_bits: u16,
) -> Result<RsaAttributes, Error> {
let keytype_algos: Vec<_> = algo_info.for_keytype(key_type);
let rsa_algos: Vec<_> = keytype_algos
.iter()
.filter_map(|a| {
if let AlgorithmAttributes::Rsa(r) = a {
Some(r)
} else {
None
}
})
.collect();
let algo: Vec<_> = rsa_algos
.iter()
.filter(|&a| a.len_n() == rsa_bits)
.collect();
if !algo.is_empty() {
Ok((**algo.last().unwrap()).clone())
} else {
Err(Error::UnsupportedAlgo(format!(
"RSA {rsa_bits} unsupported according to algo_info"
)))
}
}
fn check_card_algo_ecc(
algo_info: AlgorithmInformation,
key_type: KeyType,
oid: &[u8],
) -> Vec<EccAttributes> {
let keytype_algos: Vec<_> = algo_info.for_keytype(key_type);
let ecc_algos: Vec<_> = keytype_algos
.iter()
.filter_map(|a| {
if let AlgorithmAttributes::Ecc(e) = a {
Some(e)
} else {
None
}
})
.collect();
ecc_algos
.iter()
.filter(|e| e.oid() == oid)
.cloned()
.cloned()
.collect()
}
fn rsa_key_import_cmd(
key_type: KeyType,
rsa_key: Box<dyn RSAKey>,
rsa_attrs: &RsaAttributes,
) -> Result<Command, Error> {
let mut cpkt_data: Vec<u8> = vec![];
let mut key_data = Vec::new();
cpkt_data.extend(Vec::from(Tags::PrivateKeyDataRsaPublicExponent));
let len_e_bytes = ((rsa_attrs.len_e() + 7) / 8) as u8;
cpkt_data.push(len_e_bytes);
let e_as_bytes = rsa_key.e();
if len_e_bytes as usize > e_as_bytes.len() {
key_data.extend(vec![0; len_e_bytes as usize - e_as_bytes.len()]);
}
key_data.extend(e_as_bytes);
let len_p_bytes: u16 = rsa_attrs.len_n() / 2 / 8;
let len_q_bytes: u16 = rsa_attrs.len_n() / 2 / 8;
cpkt_data.extend(Vec::from(Tags::PrivateKeyDataRsaPrime1));
cpkt_data.extend_from_slice(&tlv_encode_length(len_p_bytes));
cpkt_data.extend(Vec::from(Tags::PrivateKeyDataRsaPrime2));
cpkt_data.extend_from_slice(&tlv_encode_length(len_q_bytes));
key_data.extend(rsa_key.p().iter());
key_data.extend(rsa_key.q().iter());
if rsa_attrs.import_format() == 2 || rsa_attrs.import_format() == 3 {
let pq = rsa_key.pq();
cpkt_data.extend(Vec::from(Tags::PrivateKeyDataRsaPq));
cpkt_data.extend(&tlv_encode_length(pq.len() as u16));
key_data.extend(pq.iter());
let dp1 = rsa_key.dp1();
cpkt_data.extend(Vec::from(Tags::PrivateKeyDataRsaDp1));
cpkt_data.extend(&tlv_encode_length(dp1.len() as u16));
key_data.extend(dp1.iter());
let dq1 = rsa_key.dq1();
cpkt_data.extend(Vec::from(Tags::PrivateKeyDataRsaDq1));
cpkt_data.extend(&tlv_encode_length(dq1.len() as u16));
key_data.extend(dq1.iter());
}
if rsa_attrs.import_format() == 1 || rsa_attrs.import_format() == 3 {
let n = rsa_key.n();
cpkt_data.extend(Vec::from(Tags::PrivateKeyDataRsaModulus));
cpkt_data.extend(&tlv_encode_length(n.len() as u16));
key_data.extend(n.iter());
}
let cpkt = Tlv::new(Tags::CardholderPrivateKeyTemplate, Value::S(cpkt_data));
let cpk = Tlv::new(Tags::ConcatenatedKeyData, Value::S(key_data));
let crt = control_reference_template(key_type)?;
let ehl = Tlv::new(Tags::ExtendedHeaderList, Value::C(vec![crt, cpkt, cpk]));
Ok(commands::key_import(ehl.serialize().to_vec()))
}
fn ecc_key_import_cmd(
key_type: KeyType,
ecc_key: Box<dyn EccKey>,
ecc_attrs: &EccAttributes,
) -> Result<Command, Error> {
let private = ecc_key.private();
let mut cpkt_data = vec![];
let mut key_data = Vec::new();
cpkt_data.extend(Vec::from(Tags::PrivateKeyDataEccPrivateKey));
cpkt_data.extend_from_slice(&tlv_encode_length(private.len() as u16));
key_data.extend(private);
if ecc_attrs.import_format() == Some(0xff) {
let p = ecc_key.public();
cpkt_data.extend(Vec::from(Tags::PrivateKeyDataEccPublicKey));
cpkt_data.extend_from_slice(&tlv_encode_length(p.len() as u16));
key_data.extend(p);
}
let cpkt = Tlv::new(Tags::CardholderPrivateKeyTemplate, Value::S(cpkt_data));
let cpk = Tlv::new(Tags::ConcatenatedKeyData, Value::S(key_data));
let crt = control_reference_template(key_type)?;
let ehl = Tlv::new(Tags::ExtendedHeaderList, Value::C(vec![crt, cpkt, cpk]));
Ok(commands::key_import(ehl.serialize().to_vec()))
}
fn control_reference_template(key_type: KeyType) -> Result<Tlv, Error> {
let tag = match key_type {
KeyType::Decryption => Tags::CrtKeyConfidentiality,
KeyType::Signing => Tags::CrtKeySignature,
KeyType::Authentication => Tags::CrtKeyAuthentication,
KeyType::Attestation => {
let tlv = Tlv::new(
Tags::CrtKeySignature,
Value::C(vec![Tlv::new(
Tag::from([0x84]),
Value::S(vec![0x81]),
)]),
);
return Ok(tlv);
}
};
Ok(Tlv::new(tag, Value::S(vec![])))
}