//! Asymmetric crypt operations. use nettle::{dsa, ecc, ecdsa, ed25519, rsa, Yarrow}; use packet::Key; use crypto::mpis::{self, MPI}; use constants::{Curve, HashAlgorithm}; use Error; use Result; /// Creates a signature. /// /// This is a low-level mechanism to produce an arbitrary OpenPGP /// signature. Using this trait allows Sequoia to perform all /// operations involving signing to use a variety of secret key /// storage mechanisms (e.g. smart cards). pub trait Signer { /// Returns a reference to the public key. fn public(&self) -> &Key; /// Creates a signature over the `digest` produced by `hash_algo`. fn sign(&mut self, hash_algo: HashAlgorithm, digest: &[u8]) -> Result; } /// A cryptographic key pair. /// /// A `KeyPair` is a combination of public and secret key. If both /// are available in memory, a `KeyPair` is a convenient /// implementation of [`Signer`]. /// /// [`Signer`]: trait.Signer.html pub struct KeyPair { public: Key, secret: mpis::SecretKey, } impl KeyPair { /// Creates a new key pair. pub fn new(public: Key, secret: mpis::SecretKey) -> Result { Ok(Self { public: public, secret: secret, }) } } impl Signer for KeyPair { fn public(&self) -> &Key { &self.public } fn sign(&mut self, hash_algo: HashAlgorithm, digest: &[u8]) -> Result { use PublicKeyAlgorithm::*; use crypto::mpis::PublicKey; use memsec; let mut rng = Yarrow::default(); #[allow(deprecated)] match (self.public.pk_algo(), self.public.mpis(), &self.secret) { (RSASign, &PublicKey::RSA { ref e, ref n }, &mpis::SecretKey::RSA { ref p, ref q, ref d, .. }) | (RSAEncryptSign, &PublicKey::RSA { ref e, ref n }, &mpis::SecretKey::RSA { ref p, ref q, ref d, .. }) => { let public = rsa::PublicKey::new(&n.value, &e.value)?; let secret = rsa::PrivateKey::new(&d.value, &p.value, &q.value, Option::None)?; // The signature has the length of the modulus. let mut sig = vec![0u8; n.value.len()]; // As described in [Section 5.2.2 and 5.2.3 of RFC 4880], // to verify the signature, we need to encode the // signature data in a PKCS1-v1.5 packet. // // [Section 5.2.2 and 5.2.3 of RFC 4880]: // https://tools.ietf.org/html/rfc4880#section-5.2.2 rsa::sign_digest_pkcs1(&public, &secret, digest, hash_algo.oid()?, &mut rng, &mut sig)?; Ok(mpis::Signature::RSA { s: MPI::new(&sig), }) }, (DSA, &PublicKey::DSA { ref p, ref q, ref g, .. }, &mpis::SecretKey::DSA { ref x }) => { let params = dsa::Params::new(&p.value, &q.value, &g.value); let secret = dsa::PrivateKey::new(&x.value); let sig = dsa::sign(¶ms, &secret, digest, &mut rng)?; Ok(mpis::Signature::DSA { r: MPI::new(&sig.r()), s: MPI::new(&sig.s()), }) }, (EdDSA, &PublicKey::EdDSA { ref curve, ref q }, &mpis::SecretKey::EdDSA { ref scalar }) => match curve { Curve::Ed25519 => { let public = q.decode_point(&Curve::Ed25519)?.0; let mut sig = vec![0; ed25519::ED25519_SIGNATURE_SIZE]; // Nettle expects the private key to be exactly // ED25519_KEY_SIZE bytes long but OpenPGP allows leading // zeros to be stripped. // Padding has to be unconditionaly, otherwise we have a // secret-dependant branch. let missing = ed25519::ED25519_KEY_SIZE .saturating_sub(scalar.value.len()); let mut sec = [0u8; ed25519::ED25519_KEY_SIZE]; sec[missing..].copy_from_slice(&scalar.value[..]); let res = ed25519::sign(public, &sec[..], digest, &mut sig); unsafe { memsec::memzero(sec.as_mut_ptr(), ed25519::ED25519_KEY_SIZE); } res?; Ok(mpis::Signature::EdDSA { r: MPI::new(&sig[..32]), s: MPI::new(&sig[32..]), }) }, _ => Err( Error::UnsupportedEllipticCurve(curve.clone()).into()), }, (ECDSA, &PublicKey::ECDSA { ref curve, .. }, &mpis::SecretKey::ECDSA { ref scalar }) => { let secret = match curve { Curve::NistP256 => ecc::Scalar::new::( &scalar.value)?, Curve::NistP384 => ecc::Scalar::new::( &scalar.value)?, Curve::NistP521 => ecc::Scalar::new::( &scalar.value)?, _ => return Err( Error::UnsupportedEllipticCurve(curve.clone()) .into()), }; let sig = ecdsa::sign(&secret, digest, &mut rng); Ok(mpis::Signature::ECDSA { r: MPI::new(&sig.r()), s: MPI::new(&sig.s()), }) }, (pk_algo, _, _) => Err(Error::InvalidArgument(format!( "unsupported combination of algorithm {:?}, key {:?}, \ and secret key {:?}", pk_algo, self.public, self.secret)).into()), } } }