Implement additional import formats for RSA key import.
This commit is contained in:
parent
256690d97c
commit
332360cbbb
5 changed files with 107 additions and 53 deletions
|
@ -13,6 +13,7 @@ documentation = "https://docs.rs/crate/openpgp-card-sequoia"
|
|||
|
||||
[dependencies]
|
||||
sequoia-openpgp = "1.3"
|
||||
nettle = "7"
|
||||
openpgp-card = { path = "../openpgp-card", version = "0.0.4" }
|
||||
openpgp-card-pcsc = { path = "../pcsc", version = "0.0.4" }
|
||||
openpgp-card-scdc = { path = "../scdc", version = "0.0.2" }
|
||||
|
|
|
@ -73,9 +73,9 @@ impl CardUploadableKey for SequoiaKey {
|
|||
match (self.public.clone(), secret_key_material) {
|
||||
(
|
||||
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)))
|
||||
}
|
||||
|
@ -146,11 +146,25 @@ struct SqRSA {
|
|||
n: MPI,
|
||||
p: ProtectedMPI,
|
||||
q: ProtectedMPI,
|
||||
nettle: nettle::rsa::PrivateKey,
|
||||
}
|
||||
|
||||
impl SqRSA {
|
||||
fn new(e: MPI, n: MPI, p: ProtectedMPI, q: ProtectedMPI) -> Self {
|
||||
Self { e, n, p, q }
|
||||
fn new(
|
||||
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()
|
||||
}
|
||||
|
||||
fn get_n(&self) -> &[u8] {
|
||||
self.n.value()
|
||||
}
|
||||
|
||||
fn get_p(&self) -> &[u8] {
|
||||
self.p.value()
|
||||
}
|
||||
|
@ -170,6 +180,23 @@ impl RSAKey for SqRSA {
|
|||
fn get_q(&self) -> &[u8] {
|
||||
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
|
||||
|
|
|
@ -200,15 +200,6 @@ impl Algo {
|
|||
algo_attributes.push(0x00);
|
||||
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());
|
||||
|
||||
Ok(algo_attributes)
|
||||
|
|
|
@ -86,9 +86,14 @@ pub enum PrivateKeyMaterial {
|
|||
/// card.
|
||||
pub trait RSAKey {
|
||||
fn get_e(&self) -> &[u8];
|
||||
fn get_n(&self) -> &[u8];
|
||||
fn get_p(&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
|
||||
|
|
|
@ -359,64 +359,94 @@ fn rsa_key_import_cmd(
|
|||
rsa_key: Box<dyn RSAKey>,
|
||||
rsa_attrs: &RsaAttrs,
|
||||
) -> 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"
|
||||
let crt = get_crt(key_type)?;
|
||||
// Collect data for "Cardholder private key template" DO (7F48)
|
||||
//
|
||||
// (Describes the content of the Cardholder private key DO)
|
||||
let mut cpkt_data = vec![];
|
||||
|
||||
// 2) "Cardholder private key template" (7F48)
|
||||
// "describes the input and the length of the content of the following DO"
|
||||
// "Cardholder private key" (5F48)
|
||||
//
|
||||
// "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
|
||||
let mut value = vec![];
|
||||
// -- Public exponent: e --
|
||||
|
||||
// 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;
|
||||
|
||||
value.push(0x91);
|
||||
cpkt_data.push(0x91);
|
||||
// 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).
|
||||
// transform unit from bits to bytes.
|
||||
let len_p_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
|
||||
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
|
||||
value.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);
|
||||
cpkt_data.extend_from_slice(&tlv_encode_length(len_q_bytes));
|
||||
|
||||
// FIXME: do p/q need to be padded from the left when many leading
|
||||
// bits are zero?
|
||||
keydata.extend(rsa_key.get_p().iter());
|
||||
keydata.extend(rsa_key.get_q().iter());
|
||||
key_data.extend(rsa_key.get_p().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)"
|
||||
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()))
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue