Implement additional import formats for RSA key import.

This commit is contained in:
Heiko Schaefer 2021-09-19 19:15:39 +02:00
parent 256690d97c
commit 332360cbbb
5 changed files with 107 additions and 53 deletions

View file

@ -13,6 +13,7 @@ documentation = "https://docs.rs/crate/openpgp-card-sequoia"
[dependencies] [dependencies]
sequoia-openpgp = "1.3" sequoia-openpgp = "1.3"
nettle = "7"
openpgp-card = { path = "../openpgp-card", version = "0.0.4" } openpgp-card = { path = "../openpgp-card", version = "0.0.4" }
openpgp-card-pcsc = { path = "../pcsc", version = "0.0.4" } openpgp-card-pcsc = { path = "../pcsc", version = "0.0.4" }
openpgp-card-scdc = { path = "../scdc", version = "0.0.2" } openpgp-card-scdc = { path = "../scdc", version = "0.0.2" }

View file

@ -73,9 +73,9 @@ impl CardUploadableKey for SequoiaKey {
match (self.public.clone(), secret_key_material) { match (self.public.clone(), secret_key_material) {
( (
mpi::PublicKey::RSA { e, n }, mpi::PublicKey::RSA { e, n },
mpi::SecretKeyMaterial::RSA { d: _, p, q, u: _ }, mpi::SecretKeyMaterial::RSA { d, p, q, u: _ },
) => { ) => {
let sq_rsa = SqRSA::new(e, n, p, q); let sq_rsa = SqRSA::new(e, d, n, p, q)?;
Ok(PrivateKeyMaterial::R(Box::new(sq_rsa))) Ok(PrivateKeyMaterial::R(Box::new(sq_rsa)))
} }
@ -146,11 +146,25 @@ struct SqRSA {
n: MPI, n: MPI,
p: ProtectedMPI, p: ProtectedMPI,
q: ProtectedMPI, q: ProtectedMPI,
nettle: nettle::rsa::PrivateKey,
} }
impl SqRSA { impl SqRSA {
fn new(e: MPI, n: MPI, p: ProtectedMPI, q: ProtectedMPI) -> Self { fn new(
Self { e, n, p, q } e: MPI,
d: ProtectedMPI,
n: MPI,
p: ProtectedMPI,
q: ProtectedMPI,
) -> Result<Self> {
let nettle = nettle::rsa::PrivateKey::new(
d.value(),
p.value(),
q.value(),
None,
)?;
Ok(Self { e, n, p, q, nettle })
} }
} }
@ -159,10 +173,6 @@ impl RSAKey for SqRSA {
self.e.value() self.e.value()
} }
fn get_n(&self) -> &[u8] {
self.n.value()
}
fn get_p(&self) -> &[u8] { fn get_p(&self) -> &[u8] {
self.p.value() self.p.value()
} }
@ -170,6 +180,23 @@ impl RSAKey for SqRSA {
fn get_q(&self) -> &[u8] { fn get_q(&self) -> &[u8] {
self.q.value() self.q.value()
} }
fn get_pq(&self) -> Box<[u8]> {
let (_, _, inv) = self.nettle.d_crt();
inv
}
fn get_dp1(&self) -> Box<[u8]> {
let (dp, _, _) = self.nettle.d_crt();
dp
}
fn get_dq1(&self) -> Box<[u8]> {
let (_, dq, _) = self.nettle.d_crt();
dq
}
fn get_n(&self) -> &[u8] {
self.n.value()
}
} }
/// ECC-specific data-structure to hold private (sub)key material for upload /// ECC-specific data-structure to hold private (sub)key material for upload

View file

@ -200,15 +200,6 @@ impl Algo {
algo_attributes.push(0x00); algo_attributes.push(0x00);
algo_attributes.push(algo_attrs.len_e() as u8); algo_attributes.push(algo_attrs.len_e() as u8);
// Import-Format of private key
// (This fn currently assumes import_format "00 = standard (e, p, q)")
if algo_attrs.import_format() != 0 {
return Err(anyhow!(
"Unexpected RSA input format (only 0 is supported)"
)
.into());
}
algo_attributes.push(algo_attrs.import_format()); algo_attributes.push(algo_attrs.import_format());
Ok(algo_attributes) Ok(algo_attributes)

View file

@ -86,9 +86,14 @@ pub enum PrivateKeyMaterial {
/// card. /// card.
pub trait RSAKey { pub trait RSAKey {
fn get_e(&self) -> &[u8]; fn get_e(&self) -> &[u8];
fn get_n(&self) -> &[u8];
fn get_p(&self) -> &[u8]; fn get_p(&self) -> &[u8];
fn get_q(&self) -> &[u8]; fn get_q(&self) -> &[u8];
fn get_pq(&self) -> Box<[u8]>;
fn get_dp1(&self) -> Box<[u8]>;
fn get_dq1(&self) -> Box<[u8]>;
fn get_n(&self) -> &[u8];
} }
/// ECC-specific container for private key material to upload to an OpenPGP /// ECC-specific container for private key material to upload to an OpenPGP

View file

@ -359,64 +359,94 @@ fn rsa_key_import_cmd(
rsa_key: Box<dyn RSAKey>, rsa_key: Box<dyn RSAKey>,
rsa_attrs: &RsaAttrs, rsa_attrs: &RsaAttrs,
) -> Result<Command, Error> { ) -> Result<Command, Error> {
// Assemble key command, which contains three sub-TLV: // Assemble key command (see 4.4.3.12 Private Key Template)
// 1) "Control Reference Template" // Collect data for "Cardholder private key template" DO (7F48)
let crt = get_crt(key_type)?; //
// (Describes the content of the Cardholder private key DO)
let mut cpkt_data = vec![];
// 2) "Cardholder private key template" (7F48) // "Cardholder private key" (5F48)
// "describes the input and the length of the content of the following DO" //
// "The key data elements according to the definitions in the CPKT DO
// (7F48)."
let mut key_data = Vec::new();
// collect the value for this DO // -- Public exponent: e --
let mut value = vec![];
// Length of e in bytes, rounding up from the bit value in algo. // Expected length of e in bytes, rounding up from the bit value in algo.
let len_e_bytes = ((rsa_attrs.len_e() + 7) / 8) as u8; let len_e_bytes = ((rsa_attrs.len_e() + 7) / 8) as u8;
cpkt_data.push(0x91);
value.push(0x91);
// len_e in bytes has a value of 3-4, it doesn't need TLV encoding // len_e in bytes has a value of 3-4, it doesn't need TLV encoding
value.push(len_e_bytes); cpkt_data.push(len_e_bytes);
// Push e, padded with zero bytes from the left
let e_as_bytes = rsa_key.get_e();
for _ in e_as_bytes.len()..(len_e_bytes as usize) {
key_data.push(0);
}
key_data.extend(e_as_bytes);
// -- Prime1: p + Prime2: q --
// len_p and len_q are len_n/2 (value from card algorithm list). // len_p and len_q are len_n/2 (value from card algorithm list).
// transform unit from bits to bytes. // transform unit from bits to bytes.
let len_p_bytes: u16 = rsa_attrs.len_n() / 2 / 8; let len_p_bytes: u16 = rsa_attrs.len_n() / 2 / 8;
let len_q_bytes: u16 = rsa_attrs.len_n() / 2 / 8; let len_q_bytes: u16 = rsa_attrs.len_n() / 2 / 8;
value.push(0x92); cpkt_data.push(0x92);
// len p in bytes, TLV-encoded // len p in bytes, TLV-encoded
value.extend_from_slice(&tlv_encode_length(len_p_bytes)); cpkt_data.extend_from_slice(&tlv_encode_length(len_p_bytes));
value.push(0x93); cpkt_data.push(0x93);
// len q in bytes, TLV-encoded // len q in bytes, TLV-encoded
value.extend_from_slice(&tlv_encode_length(len_q_bytes)); cpkt_data.extend_from_slice(&tlv_encode_length(len_q_bytes));
let cpkt = Tlv::new([0x7F, 0x48], Value::S(value));
// 3) "Cardholder private key" (5F48)
//
// "represents a concatenation of the key data elements according to
// the definitions in DO '7F48'."
let mut keydata = Vec::new();
let e_as_bytes = rsa_key.get_e();
// Push e, padded to length with zero bytes from the left
for _ in e_as_bytes.len()..(len_e_bytes as usize) {
keydata.push(0);
}
keydata.extend(e_as_bytes);
// FIXME: do p/q need to be padded from the left when many leading // FIXME: do p/q need to be padded from the left when many leading
// bits are zero? // bits are zero?
keydata.extend(rsa_key.get_p().iter()); key_data.extend(rsa_key.get_p().iter());
keydata.extend(rsa_key.get_q().iter()); key_data.extend(rsa_key.get_q().iter());
let cpk = Tlv::new([0x5F, 0x48], Value::S(keydata)); // import format requires chinese remainder theorem fields
if rsa_attrs.import_format() == 2 || rsa_attrs.import_format() == 3 {
// PQ: 1/q mod p
let pq = rsa_key.get_pq();
cpkt_data.push(0x94);
cpkt_data.extend(&tlv_encode_length(pq.len() as u16));
key_data.extend(pq.iter());
// DP1: d mod (p - 1)
let dp1 = rsa_key.get_dp1();
cpkt_data.push(0x95);
cpkt_data.extend(&tlv_encode_length(dp1.len() as u16));
key_data.extend(dp1.iter());
// DQ1: d mod (q - 1)
let dq1 = rsa_key.get_dq1();
cpkt_data.push(0x96);
cpkt_data.extend(&tlv_encode_length(dq1.len() as u16));
key_data.extend(dq1.iter());
}
// import format requires modulus n field
if rsa_attrs.import_format() == 1 || rsa_attrs.import_format() == 3 {
let n = rsa_key.get_n();
cpkt_data.push(0x97);
cpkt_data.extend(&tlv_encode_length(n.len() as u16));
key_data.extend(n.iter());
}
// Assemble the DOs for upload
let cpkt = Tlv::new([0x7F, 0x48], Value::S(cpkt_data));
let cpk = Tlv::new([0x5F, 0x48], Value::S(key_data));
// "Control Reference Template"
let crt = get_crt(key_type)?;
// "Extended header list (DO 4D)" // "Extended header list (DO 4D)"
let ehl = Tlv::new([0x4d], Value::C(vec![crt, cpkt, cpk])); let ehl = Tlv::new([0x4d], Value::C(vec![crt, cpkt, cpk]));
// key import command // Return the full key import command
Ok(commands::key_import(ehl.serialize().to_vec())) Ok(commands::key_import(ehl.serialize().to_vec()))
} }