Implement support for alternate ECC import format (which includes public key data)
This commit is contained in:
parent
200d7e60d1
commit
5417fde8ca
4 changed files with 111 additions and 33 deletions
|
@ -80,33 +80,39 @@ impl CardUploadableKey for SequoiaKey {
|
||||||
Ok(PrivateKeyMaterial::R(Box::new(sq_rsa)))
|
Ok(PrivateKeyMaterial::R(Box::new(sq_rsa)))
|
||||||
}
|
}
|
||||||
(
|
(
|
||||||
mpi::PublicKey::ECDH { curve, .. },
|
mpi::PublicKey::ECDH { curve, q, .. },
|
||||||
mpi::SecretKeyMaterial::ECDH { scalar },
|
mpi::SecretKeyMaterial::ECDH { scalar },
|
||||||
) => {
|
) => {
|
||||||
let sq_ecc =
|
let sq_ecc = SqEccKey::new(
|
||||||
SqEccKey::new(curve.oid().to_vec(), scalar, EccType::ECDH);
|
curve.oid().to_vec(),
|
||||||
|
scalar,
|
||||||
|
q,
|
||||||
|
EccType::ECDH,
|
||||||
|
);
|
||||||
|
|
||||||
Ok(PrivateKeyMaterial::E(Box::new(sq_ecc)))
|
Ok(PrivateKeyMaterial::E(Box::new(sq_ecc)))
|
||||||
}
|
}
|
||||||
(
|
(
|
||||||
mpi::PublicKey::ECDSA { curve, .. },
|
mpi::PublicKey::ECDSA { curve, q, .. },
|
||||||
mpi::SecretKeyMaterial::ECDSA { scalar },
|
mpi::SecretKeyMaterial::ECDSA { scalar },
|
||||||
) => {
|
) => {
|
||||||
let sq_ecc = SqEccKey::new(
|
let sq_ecc = SqEccKey::new(
|
||||||
curve.oid().to_vec(),
|
curve.oid().to_vec(),
|
||||||
scalar,
|
scalar,
|
||||||
|
q,
|
||||||
EccType::ECDSA,
|
EccType::ECDSA,
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(PrivateKeyMaterial::E(Box::new(sq_ecc)))
|
Ok(PrivateKeyMaterial::E(Box::new(sq_ecc)))
|
||||||
}
|
}
|
||||||
(
|
(
|
||||||
mpi::PublicKey::EdDSA { curve, .. },
|
mpi::PublicKey::EdDSA { curve, q, .. },
|
||||||
mpi::SecretKeyMaterial::EdDSA { scalar },
|
mpi::SecretKeyMaterial::EdDSA { scalar },
|
||||||
) => {
|
) => {
|
||||||
let sq_ecc = SqEccKey::new(
|
let sq_ecc = SqEccKey::new(
|
||||||
curve.oid().to_vec(),
|
curve.oid().to_vec(),
|
||||||
scalar,
|
scalar,
|
||||||
|
q,
|
||||||
EccType::EdDSA,
|
EccType::EdDSA,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -203,15 +209,22 @@ impl RSAKey for SqRSA {
|
||||||
/// with the `openpgp-card` crate.
|
/// with the `openpgp-card` crate.
|
||||||
struct SqEccKey {
|
struct SqEccKey {
|
||||||
oid: Vec<u8>,
|
oid: Vec<u8>,
|
||||||
scalar: ProtectedMPI,
|
private: ProtectedMPI,
|
||||||
|
public: MPI,
|
||||||
ecc_type: EccType,
|
ecc_type: EccType,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SqEccKey {
|
impl SqEccKey {
|
||||||
fn new(oid: Vec<u8>, scalar: ProtectedMPI, ecc_type: EccType) -> Self {
|
fn new(
|
||||||
|
oid: Vec<u8>,
|
||||||
|
private: ProtectedMPI,
|
||||||
|
public: MPI,
|
||||||
|
ecc_type: EccType,
|
||||||
|
) -> Self {
|
||||||
SqEccKey {
|
SqEccKey {
|
||||||
oid,
|
oid,
|
||||||
scalar,
|
private,
|
||||||
|
public,
|
||||||
ecc_type,
|
ecc_type,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -222,8 +235,12 @@ impl EccKey for SqEccKey {
|
||||||
&self.oid
|
&self.oid
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_scalar(&self) -> &[u8] {
|
fn get_private(&self) -> &[u8] {
|
||||||
self.scalar.value()
|
self.private.value()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_public(&self) -> &[u8] {
|
||||||
|
self.public.value()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_type(&self) -> EccType {
|
fn get_type(&self) -> EccType {
|
||||||
|
|
|
@ -170,7 +170,17 @@ impl fmt::Display for Algo {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Self::Ecc(ecc) => {
|
Self::Ecc(ecc) => {
|
||||||
write!(f, "{:?} ({:?})", ecc.curve, ecc.ecc_type)
|
write!(
|
||||||
|
f,
|
||||||
|
"{:?} ({:?}){}",
|
||||||
|
ecc.curve,
|
||||||
|
ecc.ecc_type,
|
||||||
|
if ecc.import_format == Some(0xff) {
|
||||||
|
" with pub"
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
Self::Unknown(u) => {
|
Self::Unknown(u) => {
|
||||||
write!(f, "Unknown: {:?}", u)
|
write!(f, "Unknown: {:?}", u)
|
||||||
|
@ -288,6 +298,10 @@ impl EccAttrs {
|
||||||
pub fn oid(&self) -> &[u8] {
|
pub fn oid(&self) -> &[u8] {
|
||||||
self.curve.oid()
|
self.curve.oid()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn import_format(&self) -> Option<u8> {
|
||||||
|
self.import_format
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||||
|
|
|
@ -100,7 +100,8 @@ pub trait RSAKey {
|
||||||
/// card.
|
/// card.
|
||||||
pub trait EccKey {
|
pub trait EccKey {
|
||||||
fn get_oid(&self) -> &[u8];
|
fn get_oid(&self) -> &[u8];
|
||||||
fn get_scalar(&self) -> &[u8];
|
fn get_private(&self) -> &[u8];
|
||||||
|
fn get_public(&self) -> &[u8];
|
||||||
fn get_type(&self) -> EccType;
|
fn get_type(&self) -> EccType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -200,7 +200,7 @@ pub(crate) fn key_import(
|
||||||
let ecc_attrs =
|
let ecc_attrs =
|
||||||
determine_ecc_attrs(&*ecc_key, key_type, algo_list)?;
|
determine_ecc_attrs(&*ecc_key, key_type, algo_list)?;
|
||||||
|
|
||||||
let key_cmd = ecc_key_import_cmd(ecc_key, key_type)?;
|
let key_cmd = ecc_key_import_cmd(key_type, ecc_key, &ecc_attrs)?;
|
||||||
|
|
||||||
(Algo::Ecc(ecc_attrs), key_cmd)
|
(Algo::Ecc(ecc_attrs), key_cmd)
|
||||||
}
|
}
|
||||||
|
@ -284,21 +284,33 @@ fn determine_ecc_attrs(
|
||||||
// If we have an algo_list, refuse upload if oid is not listed
|
// If we have an algo_list, refuse upload if oid is not listed
|
||||||
if let Some(algo_list) = algo_list {
|
if let Some(algo_list) = algo_list {
|
||||||
let oid = ecc_key.get_oid();
|
let oid = ecc_key.get_oid();
|
||||||
if !check_card_algo_ecc(algo_list, key_type, oid) {
|
let algos = check_card_algo_ecc(algo_list, key_type, oid);
|
||||||
|
if algos.is_empty() {
|
||||||
// If oid is not in algo_list, return error.
|
// If oid is not in algo_list, return error.
|
||||||
return Err(anyhow!(
|
return Err(anyhow!(
|
||||||
"Oid {:?} unsupported according to algo_list",
|
"Oid {:?} unsupported according to algo_list",
|
||||||
oid
|
oid
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// (Precisely looking up ECC algorithms in the card's "Algorithm
|
// (Looking up ecc_type in the card's "Algorithm Information"
|
||||||
// Information" seems to do more harm than good, so we don't do it.
|
// seems to do more harm than good, so we don't do it.
|
||||||
// Some cards report erroneous information about supported algorithms
|
// Some cards report erroneous information about supported algorithms
|
||||||
// - e.g. Yubikey 5 reports support for EdDSA over Cv25519 and
|
// - e.g. Yubikey 5 reports support for EdDSA over Cv25519 and
|
||||||
// Ed25519, but not ECDH).
|
// Ed25519, but not ECDH).
|
||||||
|
|
||||||
|
if !algos.is_empty() {
|
||||||
|
return Ok(EccAttrs::new(
|
||||||
|
ecc_key.get_type(),
|
||||||
|
Curve::try_from(ecc_key.get_oid())?,
|
||||||
|
algos[0].import_format(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return a default when we have no algo_list.
|
||||||
|
// (Do cards that support ecc but have no algo_list exist?)
|
||||||
|
|
||||||
Ok(EccAttrs::new(
|
Ok(EccAttrs::new(
|
||||||
ecc_key.get_type(),
|
ecc_key.get_type(),
|
||||||
Curve::try_from(ecc_key.get_oid())?,
|
Curve::try_from(ecc_key.get_oid())?,
|
||||||
|
@ -345,12 +357,12 @@ fn get_card_algo_rsa(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if `oid` is supported for `key_type` in algo_list.
|
/// Get all entries from algo_list with matching `oid` and `key_type`.
|
||||||
fn check_card_algo_ecc(
|
fn check_card_algo_ecc(
|
||||||
algo_list: AlgoInfo,
|
algo_list: AlgoInfo,
|
||||||
key_type: KeyType,
|
key_type: KeyType,
|
||||||
oid: &[u8],
|
oid: &[u8],
|
||||||
) -> bool {
|
) -> Vec<EccAttrs> {
|
||||||
// Find suitable algorithm parameters (from card's list of algorithms).
|
// Find suitable algorithm parameters (from card's list of algorithms).
|
||||||
|
|
||||||
// Get Algos for this keytype
|
// Get Algos for this keytype
|
||||||
|
@ -363,8 +375,13 @@ fn check_card_algo_ecc(
|
||||||
.flatten()
|
.flatten()
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
// Check if this OID exists in the algorithm information for key_type
|
// Find entries with this OID in the algorithm information for key_type
|
||||||
ecc_algos.iter().any(|e| e.oid() == oid)
|
ecc_algos
|
||||||
|
.iter()
|
||||||
|
.filter(|e| e.oid() == oid)
|
||||||
|
.cloned()
|
||||||
|
.cloned()
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create command for RSA key import
|
/// Create command for RSA key import
|
||||||
|
@ -466,21 +483,50 @@ fn rsa_key_import_cmd(
|
||||||
|
|
||||||
/// Create command for ECC key import
|
/// Create command for ECC key import
|
||||||
fn ecc_key_import_cmd(
|
fn ecc_key_import_cmd(
|
||||||
ecc_key: Box<dyn EccKey>,
|
|
||||||
key_type: KeyType,
|
key_type: KeyType,
|
||||||
|
ecc_key: Box<dyn EccKey>,
|
||||||
|
ecc_attrs: &EccAttrs,
|
||||||
) -> Result<Command, Error> {
|
) -> Result<Command, Error> {
|
||||||
let scalar_data = ecc_key.get_scalar();
|
let private = ecc_key.get_private();
|
||||||
let scalar_len = scalar_data.len() as u8;
|
|
||||||
|
|
||||||
// 1) "Control Reference Template"
|
// Collect data for "Cardholder private key template" DO (7F48)
|
||||||
|
//
|
||||||
|
// (Describes the content of the Cardholder private key DO)
|
||||||
|
let mut cpkt_data = vec![];
|
||||||
|
|
||||||
|
// "Cardholder private key" (5F48)
|
||||||
|
//
|
||||||
|
// "The key data elements according to the definitions in the CPKT DO
|
||||||
|
// (7F48)."
|
||||||
|
let mut key_data = Vec::new();
|
||||||
|
|
||||||
|
// Process "scalar"
|
||||||
|
cpkt_data.push(0x92);
|
||||||
|
cpkt_data.extend_from_slice(&tlv_encode_length(private.len() as u16));
|
||||||
|
|
||||||
|
key_data.extend(private);
|
||||||
|
|
||||||
|
// Process "public", if the import format requires it
|
||||||
|
if ecc_attrs.import_format() == Some(0xff) {
|
||||||
|
let p = ecc_key.get_public();
|
||||||
|
|
||||||
|
cpkt_data.push(0x99);
|
||||||
|
cpkt_data.extend_from_slice(&tlv_encode_length(p.len() as u16));
|
||||||
|
|
||||||
|
key_data.extend(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assemble DOs
|
||||||
|
|
||||||
|
// "Cardholder private key template"
|
||||||
|
let cpkt = Tlv::new([0x7F, 0x48], Value::S(cpkt_data));
|
||||||
|
|
||||||
|
// "Cardholder private key"
|
||||||
|
let cpk = Tlv::new([0x5F, 0x48], Value::S(key_data));
|
||||||
|
|
||||||
|
// "Control Reference Template"
|
||||||
let crt = get_crt(key_type)?;
|
let crt = get_crt(key_type)?;
|
||||||
|
|
||||||
// 2) "Cardholder private key template" (7F48)
|
|
||||||
let cpkt = Tlv::new([0x7F, 0x48], Value::S(vec![0x92, scalar_len]));
|
|
||||||
|
|
||||||
// 3) "Cardholder private key" (5F48)
|
|
||||||
let cpk = Tlv::new([0x5F, 0x48], Value::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::new([0x4d], Value::C(vec![crt, cpkt, cpk]));
|
let ehl = Tlv::new([0x4d], Value::C(vec![crt, cpkt, cpk]));
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue