//! 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<mpis::Signature>;
}
/// 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<Self> {
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<mpis::Signature>
{
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::<ecc::Secp256r1>(
&scalar.value)?,
Curve::NistP384 =>
ecc::Scalar::new::<ecc::Secp384r1>(
&scalar.value)?,
Curve::NistP521 =>
ecc::Scalar::new::<ecc::Secp521r1>(
&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()),
}
}
}