From de44f79686b9b4caadb6eda4637e5ed7ea9298a7 Mon Sep 17 00:00:00 2001 From: Justus Winter Date: Wed, 2 Jan 2019 15:07:06 +0100 Subject: openpgp: Move KeyPair to crypto. --- openpgp/src/crypto/asymmetric.rs | 153 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 150 insertions(+), 3 deletions(-) (limited to 'openpgp/src/crypto/asymmetric.rs') diff --git a/openpgp/src/crypto/asymmetric.rs b/openpgp/src/crypto/asymmetric.rs index 9c8a1ae6..51d95934 100644 --- a/openpgp/src/crypto/asymmetric.rs +++ b/openpgp/src/crypto/asymmetric.rs @@ -1,9 +1,12 @@ -//! Asymmetric crypto operations. +//! Asymmetric crypt operations. + +use nettle::{dsa, ecdsa, ed25519, rsa, Yarrow}; use packet::Key; -use crypto::mpis; -use constants::HashAlgorithm; +use crypto::mpis::{self, MPI}; +use constants::{Curve, HashAlgorithm}; +use Error; use Result; /// Creates a signature. @@ -20,3 +23,147 @@ pub trait Signer { fn sign(&mut self, hash_algo: HashAlgorithm, digest: &[u8]) -> Result; } + +/// A cryptographic key pair. +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 => + ecdsa::PrivateKey::new::( + &scalar.value)?, + Curve::NistP384 => + ecdsa::PrivateKey::new::( + &scalar.value)?, + Curve::NistP521 => + ecdsa::PrivateKey::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()), + } + } +} -- cgit v1.2.3