use crate::packet::{self, key, Key};
use crate::crypto::SessionKey;
use crate::crypto::mpi;
use crate::types::{
Curve,
HashAlgorithm,
PublicKeyAlgorithm,
SymmetricAlgorithm,
};
use crate::{Error, Result};
pub trait Signer {
fn public(&self) -> &Key<key::PublicParts, key::UnspecifiedRole>;
fn acceptable_hashes(&self) -> &[HashAlgorithm] {
crate::crypto::hash::default_hashes_sorted()
}
fn sign(&mut self, hash_algo: HashAlgorithm, digest: &[u8])
-> Result<mpi::Signature>;
}
impl Signer for Box<dyn Signer> {
fn public(&self) -> &Key<key::PublicParts, key::UnspecifiedRole> {
self.as_ref().public()
}
fn acceptable_hashes(&self) -> &[HashAlgorithm] {
self.as_ref().acceptable_hashes()
}
fn sign(&mut self, hash_algo: HashAlgorithm, digest: &[u8])
-> Result<mpi::Signature> {
self.as_mut().sign(hash_algo, digest)
}
}
impl Signer for Box<dyn Signer + Send + Sync> {
fn public(&self) -> &Key<key::PublicParts, key::UnspecifiedRole> {
self.as_ref().public()
}
fn acceptable_hashes(&self) -> &[HashAlgorithm] {
self.as_ref().acceptable_hashes()
}
fn sign(&mut self, hash_algo: HashAlgorithm, digest: &[u8])
-> Result<mpi::Signature> {
self.as_mut().sign(hash_algo, digest)
}
}
pub trait Decryptor {
fn public(&self) -> &Key<key::PublicParts, key::UnspecifiedRole>;
fn decrypt(&mut self, ciphertext: &mpi::Ciphertext,
plaintext_len: Option<usize>)
-> Result<SessionKey>;
}
impl Decryptor for Box<dyn Decryptor> {
fn public(&self) -> &Key<key::PublicParts, key::UnspecifiedRole> {
self.as_ref().public()
}
fn decrypt(&mut self, ciphertext: &mpi::Ciphertext,
plaintext_len: Option<usize>)
-> Result<SessionKey> {
self.as_mut().decrypt(ciphertext, plaintext_len)
}
}
impl Decryptor for Box<dyn Decryptor + Send + Sync> {
fn public(&self) -> &Key<key::PublicParts, key::UnspecifiedRole> {
self.as_ref().public()
}
fn decrypt(&mut self, ciphertext: &mpi::Ciphertext,
plaintext_len: Option<usize>)
-> Result<SessionKey> {
self.as_mut().decrypt(ciphertext, plaintext_len)
}
}
#[derive(Clone)]
pub struct KeyPair {
public: Key<key::PublicParts, key::UnspecifiedRole>,
secret: packet::key::Unencrypted,
}
assert_send_and_sync!(KeyPair);
impl KeyPair {
pub fn new(public: Key<key::PublicParts, key::UnspecifiedRole>,
secret: packet::key::Unencrypted)
-> Result<Self>
{
Ok(Self {
public,
secret,
})
}
pub fn public(&self) -> &Key<key::PublicParts, key::UnspecifiedRole> {
&self.public
}
pub fn secret(&self) -> &packet::key::Unencrypted {
&self.secret
}
}
impl From<KeyPair> for Key<key::SecretParts, key::UnspecifiedRole> {
fn from(p: KeyPair) -> Self {
let (key, secret) = (p.public, p.secret);
key.add_secret(secret.into()).0
}
}
impl Signer for KeyPair {
fn public(&self) -> &Key<key::PublicParts, key::UnspecifiedRole> {
KeyPair::public(self)
}
fn sign(&mut self, hash_algo: HashAlgorithm, digest: &[u8])
-> Result<mpi::Signature>
{
use crate::crypto::backend::{Backend, interface::Asymmetric};
self.secret().map(|secret| {
#[allow(deprecated)]
match (self.public().pk_algo(), self.public().mpis(), secret) {
(PublicKeyAlgorithm::Ed25519,
mpi::PublicKey::Ed25519 { a },
mpi::SecretKeyMaterial::Ed25519 { x }) => {
Ok(mpi::Signature::Ed25519 {
s: Box::new(Backend::ed25519_sign(x, a, digest)?),
})
},
(PublicKeyAlgorithm::Ed448,
mpi::PublicKey::Ed448 { a },
mpi::SecretKeyMaterial::Ed448 { x }) => {
Ok(mpi::Signature::Ed448 {
s: Box::new(Backend::ed448_sign(x, a, digest)?),
})
},
(PublicKeyAlgorithm::EdDSA,
mpi::PublicKey::EdDSA { curve, q },
mpi::SecretKeyMaterial::EdDSA { scalar }) => match curve {
Curve::Ed25519 => {
let public = q.decode_point(&Curve::Ed25519)?.0
.try_into()?;
let secret = scalar.value_padded(32);
let sig =
Backend::ed25519_sign(&secret, &public, digest)?;
Ok(mpi::Signature::EdDSA {
r: mpi::MPI::new(&sig[..32]),
s: mpi::MPI::new(&sig[32..]),
})
},
_ => Err(
Error::UnsupportedEllipticCurve(curve.clone()).into()),
},
(PublicKeyAlgorithm::DSA,
mpi::PublicKey::DSA { p, q, g, y },
mpi::SecretKeyMaterial::DSA { x }) => {
let (r, s) = Backend::dsa_sign(x, p, q, g, y, digest)?;
Ok(mpi::Signature::DSA { r, s })
},
(_algo, _public, secret) =>
self.sign_backend(secret, hash_algo, digest),
}
})
}
}
impl Decryptor for KeyPair {
fn public(&self) -> &Key<key::PublicParts, key::UnspecifiedRole> {
KeyPair::public(self)
}
fn decrypt(&mut self,
ciphertext: &mpi::Ciphertext,
plaintext_len: Option<usize>)
-> Result<SessionKey>
{
use crate::crypto::ecdh::aes_key_unwrap;
use crate::crypto::backend::{Backend, interface::{Asymmetric, Kdf}};
self.secret().map(|secret| {
#[allow(non_snake_case)]
match (self.public().mpis(), secret, ciphertext) {
(mpi::PublicKey::X25519 { u: U },
mpi::SecretKeyMaterial::X25519 { x },
mpi::Ciphertext::X25519 { e: E, key }) => {
let S = Backend::x25519_shared_point(x, E)?;
let wrap_algo = SymmetricAlgorithm::AES128;
let mut ikm: SessionKey = vec![0; 32 + 32 + 32].into();
#[allow(clippy::erasing_op)]
ikm[0 * 32..1 * 32].copy_from_slice(&E[..]);
ikm[1 * 32..2 * 32].copy_from_slice(&U[..]);
ikm[2 * 32..3 * 32].copy_from_slice(&S[..]);
let mut kek = vec![0; wrap_algo.key_size()?].into();
Backend::hkdf_sha256(&ikm, None, b"OpenPGP X25519",
&mut kek)?;
Ok(aes_key_unwrap(wrap_algo, kek.as_protected(),
key)?.into())
},
(mpi::PublicKey::X448 { u: U },
mpi::SecretKeyMaterial::X448 { x },
mpi::Ciphertext::X448 { e: E, key }) => {
let S = Backend::x448_shared_point(x, E)?;
let wrap_algo = SymmetricAlgorithm::AES256;
let mut ikm: SessionKey = vec![0; 56 + 56 + 56].into();
#[allow(clippy::erasing_op)]
ikm[0 * 56..1 * 56].copy_from_slice(&E[..]);
ikm[1 * 56..2 * 56].copy_from_slice(&U[..]);
ikm[2 * 56..3 * 56].copy_from_slice(&S[..]);
let mut kek = vec![0; wrap_algo.key_size()?].into();
Backend::hkdf_sha512(&ikm, None, b"OpenPGP X448",
&mut kek)?;
Ok(aes_key_unwrap(wrap_algo, kek.as_protected(),
key)?.into())
},
(mpi::PublicKey::ECDH { curve: Curve::Cv25519, .. },
mpi::SecretKeyMaterial::ECDH { scalar, },
mpi::Ciphertext::ECDH { e, .. }) =>
{
let V = e.decode_point(&Curve::Cv25519)?.0;
let mut r = scalar.value_padded(32);
r.reverse();
let S = Backend::x25519_shared_point(&r, &V.try_into()?)?;
crate::crypto::ecdh::decrypt_unwrap(
self.public(), &S, ciphertext, plaintext_len)
},
(_public, secret, _ciphertext) =>
self.decrypt_backend(secret, ciphertext, plaintext_len),
}
})
}
}