#[macro_export]
macro_rules! impl_secret_key {
($name:ident, $tag:expr, $details:ident) => {
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct $name {
pub(crate) details: $crate::packet::$details,
pub(crate) secret_params: $crate::types::SecretParams,
}
impl zeroize::Zeroize for $name {
fn zeroize(&mut self) {
self.secret_params.zeroize();
}
}
impl Drop for $name {
fn drop(&mut self) {
use zeroize::Zeroize;
self.zeroize();
}
}
impl $name {
pub fn from_slice(
packet_version: $crate::types::Version,
input: &[u8],
) -> $crate::errors::Result<Self> {
let (_, details) = $crate::packet::secret_key_parser::parse(input)?;
let (version, algorithm, created_at, expiration, public_params, secret_params) =
details;
Ok($name {
details: $crate::packet::$details {
packet_version,
version,
algorithm,
created_at,
expiration,
public_params,
},
secret_params,
})
}
pub fn version(&self) -> $crate::types::KeyVersion {
self.details.version()
}
pub fn created_at(&self) -> &chrono::DateTime<chrono::Utc> {
&self.details.created_at()
}
pub fn expiration(&self) -> Option<u16> {
self.details.expiration()
}
pub fn public_params(&self) -> &$crate::types::PublicParams {
&self.details.public_params()
}
pub fn verify(&self) -> $crate::errors::Result<()> {
unimplemented!("verify");
}
fn repr_from_ciphertext<F>(
&self,
pw: F,
ciphertext: &$crate::types::EncryptedSecretParams,
) -> $crate::errors::Result<$crate::types::SecretKeyRepr>
where
F: FnOnce() -> String,
{
let plain = ciphertext.unlock(pw, self.details.algorithm, self.public_params())?;
self.repr_from_plaintext(&plain)
}
fn repr_from_plaintext(
&self,
plaintext: &$crate::types::PlainSecretParams,
) -> $crate::errors::Result<$crate::types::SecretKeyRepr> {
plaintext.as_ref().as_repr(self.public_params())
}
pub fn secret_params(&self) -> &$crate::types::SecretParams {
&self.secret_params
}
pub fn has_sha1_checksum(&self) -> bool {
self.secret_params.string_to_key_id() == 254
}
fn to_writer_old<W: std::io::Write>(
&self,
writer: &mut W,
) -> $crate::errors::Result<()> {
use $crate::ser::Serialize;
self.details.to_writer_old(writer)?;
self.secret_params.to_writer(writer)?;
Ok(())
}
fn to_writer_new<W: std::io::Write>(
&self,
writer: &mut W,
) -> $crate::errors::Result<()> {
use $crate::ser::Serialize;
self.details.to_writer_new(writer)?;
self.secret_params.to_writer(writer)?;
Ok(())
}
pub fn sign<F>(
&self,
key: &impl $crate::types::SecretKeyTrait,
key_pw: F,
) -> $crate::errors::Result<$crate::packet::Signature>
where
F: FnOnce() -> String,
{
use chrono::SubsecRound;
let mut config = $crate::packet::SignatureConfigBuilder::default();
match $tag {
$crate::types::Tag::SecretKey => {
config.typ($crate::packet::SignatureType::KeyBinding);
}
$crate::types::Tag::SecretSubkey => {
config.typ($crate::packet::SignatureType::SubkeyBinding);
}
_ => panic!("invalid tag"),
};
config
.pub_alg(key.algorithm())
.hashed_subpackets(vec![$crate::packet::Subpacket::regular(
$crate::packet::SubpacketData::SignatureCreationTime(
chrono::Utc::now().trunc_subsecs(0),
),
)])
.unhashed_subpackets(vec![$crate::packet::Subpacket::regular(
$crate::packet::SubpacketData::Issuer(key.key_id()),
)])
.build()?
.sign_key(key, key_pw, &self)
}
}
impl $crate::types::SecretKeyTrait for $name {
type PublicKey = $details;
fn unlock<F, G>(&self, pw: F, work: G) -> $crate::errors::Result<()>
where
F: FnOnce() -> String,
G: FnOnce(&$crate::types::SecretKeyRepr) -> $crate::errors::Result<()>,
{
use $crate::types::SecretParams;
let decrypted = match self.secret_params {
SecretParams::Plain(ref k) => self.repr_from_plaintext(k),
SecretParams::Encrypted(ref k) => self.repr_from_ciphertext(pw, k),
}?;
work(&decrypted)
}
fn create_signature<F>(
&self,
key_pw: F,
hash: $crate::crypto::hash::HashAlgorithm,
data: &[u8],
) -> $crate::errors::Result<Vec<$crate::types::Mpi>>
where
F: FnOnce() -> String,
{
use $crate::crypto::ecc_curve::ECCCurve;
use $crate::types::{PublicParams, SecretKeyRepr};
let mut signature: Option<Vec<$crate::types::Mpi>> = None;
self.unlock(key_pw, |priv_key| {
debug!("unlocked key");
let sig = match *priv_key {
SecretKeyRepr::RSA(ref priv_key) => {
$crate::crypto::rsa::sign(priv_key, hash, data)
}
SecretKeyRepr::DSA(_) => unimplemented_err!("sign DSA"),
SecretKeyRepr::ECDSA(ref priv_key) => match self.public_params() {
PublicParams::ECDSA(ref _params) => {
$crate::crypto::ecdsa::sign(priv_key, hash, data)
}
_ => unreachable!("inconsistent key state"),
},
SecretKeyRepr::ECDH(_) => {
bail!("ECDH can not be used to for signing operations")
}
SecretKeyRepr::EdDSA(ref priv_key) => match self.public_params() {
PublicParams::EdDSA { ref curve, ref q } => match *curve {
ECCCurve::Ed25519 => {
$crate::crypto::eddsa::sign(q.as_bytes(), priv_key, hash, data)
}
_ => unsupported_err!("curve {:?} for EdDSA", curve.to_string()),
},
_ => unreachable!("inconsistent key state"),
},
}?;
signature = Some(
sig.iter()
.map(|v| $crate::types::Mpi::from_raw_slice(&v[..]))
.collect::<Vec<_>>(),
);
Ok(())
})?;
signature.ok_or_else(|| unreachable!())
}
fn public_key(&self) -> $details {
self.details.clone()
}
}
impl $crate::ser::Serialize for $name {
fn to_writer<W: std::io::Write>(&self, writer: &mut W) -> $crate::errors::Result<()> {
writer.write_all(&[self.version() as u8])?;
match self.version() {
$crate::types::KeyVersion::V2 | $crate::types::KeyVersion::V3 => {
self.to_writer_old(writer)
}
$crate::types::KeyVersion::V4 => self.to_writer_new(writer),
$crate::types::KeyVersion::V5 => unimplemented_err!("V5 keys"),
}
}
}
impl $crate::packet::PacketTrait for $name {
fn packet_version(&self) -> $crate::types::Version {
self.details.packet_version()
}
fn tag(&self) -> $crate::types::Tag {
$tag
}
}
impl $crate::types::KeyTrait for $name {
fn fingerprint(&self) -> Vec<u8> {
self.details.fingerprint()
}
fn key_id(&self) -> $crate::types::KeyId {
self.details.key_id()
}
fn algorithm(&self) -> $crate::crypto::public_key::PublicKeyAlgorithm {
self.details.algorithm()
}
}
impl $crate::types::PublicKeyTrait for $name {
fn verify_signature(
&self,
hash: $crate::crypto::hash::HashAlgorithm,
hashed: &[u8],
sig: &[$crate::types::Mpi],
) -> $crate::errors::Result<()> {
self.details.verify_signature(hash, hashed, sig)
}
fn encrypt<R: rand::Rng + rand::CryptoRng>(
&self,
rng: &mut R,
plain: &[u8],
) -> $crate::errors::Result<Vec<$crate::types::Mpi>> {
self.details.encrypt(rng, plain)
}
fn to_writer_old(
&self,
writer: &mut impl std::io::Write,
) -> $crate::errors::Result<()> {
use $crate::ser::Serialize;
let mut key_buf = Vec::new();
self.details.to_writer(&mut key_buf)?;
writer.write_all(&[0x99, (key_buf.len() >> 8) as u8, key_buf.len() as u8])?;
writer.write_all(&key_buf)?;
Ok(())
}
}
};
}