use std::convert::TryFrom;
use std::convert::TryInto;
use openpgp::cert::amalgamation::key::ValidErasedKeyAmalgamation;
use openpgp::crypto::{mpi, mpi::ProtectedMPI, mpi::MPI};
use openpgp::packet::{
key,
key::{SecretParts, UnspecifiedRole},
Key,
};
use openpgp::types::Timestamp;
use sequoia_openpgp as openpgp;
use openpgp_card::card_do::{Fingerprint, KeyGenerationTime};
use openpgp_card::crypto_data::{CardUploadableKey, EccKey, EccType, PrivateKeyMaterial, RSAKey};
use openpgp_card::Error;
use sequoia_openpgp::types::Curve;
pub(crate) struct SequoiaKey {
key: Key<SecretParts, UnspecifiedRole>,
public: mpi::PublicKey,
password: Option<String>,
}
impl SequoiaKey {
pub(crate) fn new(
vka: ValidErasedKeyAmalgamation<SecretParts>,
password: Option<String>,
) -> Self {
let public = vka.parts_as_public().mpis().clone();
Self {
key: vka.key().clone(),
public,
password,
}
}
}
impl CardUploadableKey for SequoiaKey {
fn private_key(&self) -> Result<PrivateKeyMaterial, Error> {
let key = match &self.password {
None => self.key.clone(),
Some(pw) => self
.key
.clone()
.decrypt_secret(&openpgp::crypto::Password::from(pw.as_str()))
.map_err(|e| Error::InternalError(format!("sequoia decrypt failed {:?}", e)))?,
};
let unenc = if let Some(key::SecretKeyMaterial::Unencrypted(ref u)) = key.optional_secret()
{
u
} else {
panic!("can't get private key material");
};
let secret_key_material = unenc.map(|mpis| mpis.clone());
match (self.public.clone(), secret_key_material) {
(mpi::PublicKey::RSA { e, n }, mpi::SecretKeyMaterial::RSA { d, p, q, u: _ }) => {
let sq_rsa = SqRSA::new(e, d, n, p, q)?;
Ok(PrivateKeyMaterial::R(Box::new(sq_rsa)))
}
(mpi::PublicKey::ECDH { curve, q, .. }, mpi::SecretKeyMaterial::ECDH { scalar }) => {
let sq_ecc = SqEccKey::new(curve, scalar, q, EccType::ECDH);
Ok(PrivateKeyMaterial::E(Box::new(sq_ecc)))
}
(mpi::PublicKey::ECDSA { curve, q, .. }, mpi::SecretKeyMaterial::ECDSA { scalar }) => {
let sq_ecc = SqEccKey::new(curve, scalar, q, EccType::ECDSA);
Ok(PrivateKeyMaterial::E(Box::new(sq_ecc)))
}
(mpi::PublicKey::EdDSA { curve, q, .. }, mpi::SecretKeyMaterial::EdDSA { scalar }) => {
let sq_ecc = SqEccKey::new(curve, scalar, q, EccType::EdDSA);
Ok(PrivateKeyMaterial::E(Box::new(sq_ecc)))
}
(p, s) => {
unimplemented!("Unexpected algorithms: public {:?}, secret {:?}", p, s);
}
}
}
fn timestamp(&self) -> KeyGenerationTime {
let ts: Timestamp = Timestamp::try_from(self.key.creation_time())
.expect("Creation time cannot be converted into u32 timestamp");
let ts: u32 = ts.into();
ts.into()
}
fn fingerprint(&self) -> Result<Fingerprint, Error> {
let fp = self.key.fingerprint();
fp.as_bytes().try_into()
}
}
struct SqRSA {
e: MPI,
n: MPI,
p: ProtectedMPI,
q: ProtectedMPI,
nettle: nettle::rsa::PrivateKey,
}
impl SqRSA {
#[allow(clippy::many_single_char_names)]
fn new(
e: MPI,
d: ProtectedMPI,
n: MPI,
p: ProtectedMPI,
q: ProtectedMPI,
) -> Result<Self, Error> {
let nettle = nettle::rsa::PrivateKey::new(d.value(), p.value(), q.value(), None)
.map_err(|e| Error::InternalError(format!("nettle error {:?}", e)))?;
Ok(Self { e, n, p, q, nettle })
}
}
impl RSAKey for SqRSA {
fn e(&self) -> &[u8] {
self.e.value()
}
fn p(&self) -> &[u8] {
self.p.value()
}
fn q(&self) -> &[u8] {
self.q.value()
}
fn pq(&self) -> Box<[u8]> {
let (_, _, inv) = self.nettle.d_crt();
inv
}
fn dp1(&self) -> Box<[u8]> {
let (dp, _, _) = self.nettle.d_crt();
dp
}
fn dq1(&self) -> Box<[u8]> {
let (_, dq, _) = self.nettle.d_crt();
dq
}
fn n(&self) -> &[u8] {
self.n.value()
}
}
struct SqEccKey {
curve: Curve,
private: ProtectedMPI,
public: MPI,
ecc_type: EccType,
}
impl SqEccKey {
fn new(curve: Curve, private: ProtectedMPI, public: MPI, ecc_type: EccType) -> Self {
SqEccKey {
curve,
private,
public,
ecc_type,
}
}
}
impl EccKey for SqEccKey {
fn oid(&self) -> &[u8] {
self.curve.oid()
}
fn private(&self) -> Vec<u8> {
match self.curve {
Curve::NistP256 => self.private.value_padded(0x20).to_vec(),
Curve::NistP384 => self.private.value_padded(0x30).to_vec(),
Curve::NistP521 => self.private.value_padded(0x42).to_vec(),
_ => self.private.value().to_vec(),
}
}
fn public(&self) -> Vec<u8> {
self.public.value().to_vec()
}
fn ecc_type(&self) -> EccType {
self.ecc_type
}
}