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]
|
[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" }
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue