summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIgor Matuszewski <igor@sequoia-pgp.org>2020-04-10 00:03:01 +0200
committerIgor Matuszewski <igor@sequoia-pgp.org>2020-06-22 11:57:06 +0200
commitdf5eb44e8e442c5bc4d958d914121075258393ec (patch)
treef5106537792d08b7b33920aa5467f0dedae91121
parentbaf6f108b94f7e7b3a2ec12396e9bfbbd67c76ee (diff)
openpgp: Move Nettle asymmetric impls to the backend module
-rw-r--r--openpgp/src/crypto/asymmetric.rs343
-rw-r--r--openpgp/src/crypto/backend/nettle.rs2
-rw-r--r--openpgp/src/crypto/backend/nettle/asymmetric.rs352
3 files changed, 356 insertions, 341 deletions
diff --git a/openpgp/src/crypto/asymmetric.rs b/openpgp/src/crypto/asymmetric.rs
index 534e4912..dd650ff2 100644
--- a/openpgp/src/crypto/asymmetric.rs
+++ b/openpgp/src/crypto/asymmetric.rs
@@ -1,13 +1,10 @@
//! Asymmetric crypt operations.
-use nettle::{dsa, ecc, ecdsa, ed25519, rsa, random::Yarrow};
-
use crate::packet::{self, key, Key};
use crate::crypto::SessionKey;
-use crate::crypto::mpi::{self, MPI};
-use crate::types::{Curve, HashAlgorithm};
+use crate::crypto::mpi;
+use crate::types::HashAlgorithm;
-use crate::Error;
use crate::Result;
/// Creates a signature.
@@ -89,345 +86,9 @@ impl KeyPair {
}
}
-impl Signer for KeyPair {
- fn public(&self) -> &Key<key::PublicParts, key::UnspecifiedRole> {
- &self.public
- }
-
- fn sign(&mut self, hash_algo: HashAlgorithm, digest: &[u8])
- -> Result<mpi::Signature>
- {
- use crate::PublicKeyAlgorithm::*;
- use crate::crypto::mpi::PublicKey;
-
- let mut rng = Yarrow::default();
-
- self.secret.map(|secret| {
- #[allow(deprecated)]
- match (self.public.pk_algo(), self.public.mpis(), secret)
- {
- (RSASign,
- &PublicKey::RSA { ref e, ref n },
- &mpi::SecretKeyMaterial::RSA { ref p, ref q, ref d, .. }) |
- (RSAEncryptSign,
- &PublicKey::RSA { ref e, ref n },
- &mpi::SecretKeyMaterial::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(mpi::Signature::RSA {
- s: MPI::new(&sig),
- })
- },
-
- (DSA,
- &PublicKey::DSA { ref p, ref q, ref g, .. },
- &mpi::SecretKeyMaterial::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(&params, &secret, digest, &mut rng)?;
-
- Ok(mpi::Signature::DSA {
- r: MPI::new(&sig.r()),
- s: MPI::new(&sig.s()),
- })
- },
-
- (EdDSA,
- &PublicKey::EdDSA { ref curve, ref q },
- &mpi::SecretKeyMaterial::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 unconditional; otherwise we have a
- // secret-dependent 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(mpi::Signature::EdDSA {
- r: MPI::new(&sig[..32]),
- s: MPI::new(&sig[32..]),
- })
- },
- _ => Err(
- Error::UnsupportedEllipticCurve(curve.clone()).into()),
- },
-
- (ECDSA,
- &PublicKey::ECDSA { ref curve, .. },
- &mpi::SecretKeyMaterial::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(mpi::Signature::ECDSA {
- r: MPI::new(&sig.r()),
- s: MPI::new(&sig.s()),
- })
- },
-
- (pk_algo, _, _) => Err(Error::InvalidOperation(format!(
- "unsupported combination of algorithm {:?}, key {:?}, \
- and secret key {:?}",
- pk_algo, self.public, self.secret)).into()),
- }})
- }
-}
-
-impl Decryptor for KeyPair {
- fn public(&self) -> &Key<key::PublicParts, key::UnspecifiedRole> {
- &self.public
- }
-
- /// Creates a signature over the `digest` produced by `hash_algo`.
- fn decrypt(&mut self, ciphertext: &mpi::Ciphertext,
- plaintext_len: Option<usize>)
- -> Result<SessionKey>
- {
- use crate::PublicKeyAlgorithm::*;
- use crate::crypto::mpi::PublicKey;
-
- self.secret.map(
- |secret| Ok(match (self.public.mpis(), secret, ciphertext)
- {
- (PublicKey::RSA{ ref e, ref n },
- mpi::SecretKeyMaterial::RSA{ ref p, ref q, ref d, .. },
- mpi::Ciphertext::RSA{ ref c }) => {
- // Workaround for #440: Make sure c is of the same
- // length as n.
- // XXX: Remove once we depend on nettle > 7.0.0.
- let c_ = if c.value().len() < n.value().len() {
- let mut c_ = vec![0; n.value().len() - c.value().len()];
- c_.extend_from_slice(c.value());
- Some(c_)
- } else {
- // If it is bigger, then the packet is likely
- // corrupted, tough luck then.
- None
- };
- let c = if let Some(c_) = c_.as_ref() {
- &c_[..]
- } else {
- c.value()
- };
- // End of workaround.
-
- let public = rsa::PublicKey::new(n.value(), e.value())?;
- let secret = rsa::PrivateKey::new(d.value(), p.value(),
- q.value(), Option::None)?;
- let mut rand = Yarrow::default();
- if let Some(l) = plaintext_len {
- let mut plaintext: SessionKey = vec![0; l].into();
- rsa::decrypt_pkcs1(&public, &secret, &mut rand,
- c, plaintext.as_mut())?;
- plaintext
- } else {
- rsa::decrypt_pkcs1_insecure(&public, &secret,
- &mut rand, c)?
- .into()
- }
- }
-
- (PublicKey::ElGamal{ .. },
- mpi::SecretKeyMaterial::ElGamal{ .. },
- mpi::Ciphertext::ElGamal{ .. }) =>
- return Err(
- Error::UnsupportedPublicKeyAlgorithm(ElGamalEncrypt).into()),
-
- (PublicKey::ECDH{ .. },
- mpi::SecretKeyMaterial::ECDH { .. },
- mpi::Ciphertext::ECDH { .. }) =>
- crate::crypto::ecdh::decrypt(&self.public, secret, ciphertext)?,
-
- (public, secret, ciphertext) =>
- return Err(Error::InvalidOperation(format!(
- "unsupported combination of key pair {:?}/{:?} \
- and ciphertext {:?}",
- public, secret, ciphertext)).into()),
- }))
- }
-}
-
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<P: key::KeyParts, R: key::KeyRole> Key<P, R> {
- /// Encrypts the given data with this key.
- pub fn encrypt(&self, data: &SessionKey) -> Result<mpi::Ciphertext> {
- use crate::PublicKeyAlgorithm::*;
-
- #[allow(deprecated)]
- match self.pk_algo() {
- RSAEncryptSign | RSAEncrypt => {
- // Extract the public recipient.
- match self.mpis() {
- mpi::PublicKey::RSA { e, n } => {
- // The ciphertext has the length of the modulus.
- let mut esk = vec![0u8; n.value().len()];
- let mut rng = Yarrow::default();
- let pk = rsa::PublicKey::new(n.value(), e.value())?;
- rsa::encrypt_pkcs1(&pk, &mut rng, data,
- &mut esk)?;
- Ok(mpi::Ciphertext::RSA {
- c: MPI::new(&esk),
- })
- },
- pk => {
- Err(Error::MalformedPacket(
- format!(
- "Key: Expected RSA public key, got {:?}",
- pk)).into())
- },
- }
- },
- ECDH => crate::crypto::ecdh::encrypt(self.parts_as_public(),
- data),
- algo => Err(Error::UnsupportedPublicKeyAlgorithm(algo).into()),
- }
- }
-
- /// Verifies the given signature.
- pub fn verify(&self, sig: &packet::Signature, digest: &[u8]) -> Result<()>
- {
- use crate::PublicKeyAlgorithm::*;
- use crate::crypto::mpi::{PublicKey, Signature};
-
- #[allow(deprecated)]
- let ok = match (sig.pk_algo(), self.mpis(), sig.mpis()) {
- (RSASign, PublicKey::RSA { e, n }, Signature::RSA { s }) |
- (RSAEncryptSign, PublicKey::RSA { e, n }, Signature::RSA { s }) => {
- let key = rsa::PublicKey::new(n.value(), e.value())?;
-
- // 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::verify_digest_pkcs1(&key, digest, sig.hash_algo().oid()?,
- s.value())?
- },
- (DSA, PublicKey::DSA{ y, p, q, g }, Signature::DSA { s, r }) => {
- let key = dsa::PublicKey::new(y.value());
- let params = dsa::Params::new(p.value(), q.value(), g.value());
- let signature = dsa::Signature::new(r.value(), s.value());
-
- dsa::verify(&params, &key, digest, &signature)
- },
- (EdDSA, PublicKey::EdDSA{ curve, q }, Signature::EdDSA { r, s }) =>
- match curve {
- Curve::Ed25519 => {
- if q.value().get(0).map(|&b| b != 0x40).unwrap_or(true) {
- return Err(Error::MalformedPacket(
- "Invalid point encoding".into()).into());
- }
-
- // OpenPGP encodes R and S separately, but our
- // cryptographic library expects them to be
- // concatenated.
- let mut signature =
- Vec::with_capacity(ed25519::ED25519_SIGNATURE_SIZE);
-
- // We need to zero-pad them at the front, because
- // the MPI encoding drops leading zero bytes.
- let half = ed25519::ED25519_SIGNATURE_SIZE / 2;
- if r.value().len() < half {
- for _ in 0..half - r.value().len() {
- signature.push(0);
- }
- }
- signature.extend_from_slice(r.value());
- if s.value().len() < half {
- for _ in 0..half - s.value().len() {
- signature.push(0);
- }
- }
- signature.extend_from_slice(s.value());
-
- // Let's see if we got it right.
- if signature.len() != ed25519::ED25519_SIGNATURE_SIZE {
- return Err(Error::MalformedPacket(
- format!(
- "Invalid signature size: {}, r: {:?}, s: {:?}",
- signature.len(), r.value(), s.value())).into());
- }
-
- ed25519::verify(&q.value()[1..], digest, &signature)?
- },
- _ => return
- Err(Error::UnsupportedEllipticCurve(curve.clone()).into()),
- },
- (ECDSA, PublicKey::ECDSA{ curve, q }, Signature::ECDSA { s, r }) =>
- {
- let (x, y) = q.decode_point(curve)?;
- let key = match curve {
- Curve::NistP256 => ecc::Point::new::<ecc::Secp256r1>(x, y)?,
- Curve::NistP384 => ecc::Point::new::<ecc::Secp384r1>(x, y)?,
- Curve::NistP521 => ecc::Point::new::<ecc::Secp521r1>(x, y)?,
- _ => return Err(
- Error::UnsupportedEllipticCurve(curve.clone()).into()),
- };
-
- let signature = dsa::Signature::new(r.value(), s.value());
- ecdsa::verify(&key, digest, &signature)
- },
- _ => return Err(Error::MalformedPacket(format!(
- "unsupported combination of algorithm {}, key {} and \
- signature {:?}.",
- sig.pk_algo(), self.pk_algo(), sig.mpis())).into()),
- };
-
- if ok {
- Ok(())
- } else {
- Err(Error::ManipulatedMessage.into())
- }
- }
-}
diff --git a/openpgp/src/crypto/backend/nettle.rs b/openpgp/src/crypto/backend/nettle.rs
index 5c0149fc..e7614445 100644
--- a/openpgp/src/crypto/backend/nettle.rs
+++ b/openpgp/src/crypto/backend/nettle.rs
@@ -2,6 +2,8 @@
use nettle::random::{Random, Yarrow};
+pub mod asymmetric;
+
/// Fills the given buffer with random data.
pub fn random<B: AsMut<[u8]>>(mut buf: B) {
Yarrow::default().random(buf.as_mut());
diff --git a/openpgp/src/crypto/backend/nettle/asymmetric.rs b/openpgp/src/crypto/backend/nettle/asymmetric.rs
new file mode 100644
index 00000000..95c3c7e2
--- /dev/null
+++ b/openpgp/src/crypto/backend/nettle/asymmetric.rs
@@ -0,0 +1,352 @@
+//! Hold the implementation of [`Signer`] and [`Decryptor`] for [`KeyPair`].
+//!
+//! [`Signer`]: ../../asymmetric/trait.Signer.html
+//! [`Decryptor`]: ../../asymmetric/trait.Decryptor.html
+//! [`KeyPair`]: ../../asymmetric/struct.KeyPair.html
+
+use nettle::{ecc, ecdsa, ed25519, dsa, rsa, random::Yarrow};
+
+use crate::{Error, Result};
+
+use crate::packet::{self, key, Key};
+use crate::crypto::asymmetric::{KeyPair, Decryptor, Signer};
+use crate::crypto::mpi::{self, MPI};
+use crate::crypto::SessionKey;
+use crate::types::{Curve, HashAlgorithm};
+
+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::PublicKeyAlgorithm::*;
+ use crate::crypto::mpi::PublicKey;
+
+ let mut rng = Yarrow::default();
+
+ self.secret().map(|secret| {
+ #[allow(deprecated)]
+ match (self.public().pk_algo(), self.public().mpis(), secret)
+ {
+ (RSASign,
+ &PublicKey::RSA { ref e, ref n },
+ &mpi::SecretKeyMaterial::RSA { ref p, ref q, ref d, .. }) |
+ (RSAEncryptSign,
+ &PublicKey::RSA { ref e, ref n },
+ &mpi::SecretKeyMaterial::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(mpi::Signature::RSA {
+ s: MPI::new(&sig),
+ })
+ },
+
+ (DSA,
+ &PublicKey::DSA { ref p, ref q, ref g, .. },
+ &mpi::SecretKeyMaterial::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(&params, &secret, digest, &mut rng)?;
+
+ Ok(mpi::Signature::DSA {
+ r: MPI::new(&sig.r()),
+ s: MPI::new(&sig.s()),
+ })
+ },
+
+ (EdDSA,
+ &PublicKey::EdDSA { ref curve, ref q },
+ &mpi::SecretKeyMaterial::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 unconditional; otherwise we have a
+ // secret-dependent 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(mpi::Signature::EdDSA {
+ r: MPI::new(&sig[..32]),
+ s: MPI::new(&sig[32..]),
+ })
+ },
+ _ => Err(
+ Error::UnsupportedEllipticCurve(curve.clone()).into()),
+ },
+
+ (ECDSA,
+ &PublicKey::ECDSA { ref curve, .. },
+ &mpi::SecretKeyMaterial::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(mpi::Signature::ECDSA {
+ r: MPI::new(&sig.r()),
+ s: MPI::new(&sig.s()),
+ })
+ },
+
+ (pk_algo, _, _) => Err(Error::InvalidOperation(format!(
+ "unsupported combination of algorithm {:?}, key {:?}, \
+ and secret key {:?}",
+ pk_algo, self.public(), self.secret())).into()),
+ }})
+ }
+}
+
+impl Decryptor for KeyPair {
+ fn public(&self) -> &Key<key::PublicParts, key::UnspecifiedRole> {
+ KeyPair::public(self)
+ }
+
+ /// Creates a signature over the `digest` produced by `hash_algo`.
+ fn decrypt(&mut self, ciphertext: &mpi::Ciphertext,
+ plaintext_len: Option<usize>)
+ -> Result<SessionKey>
+ {
+ use crate::PublicKeyAlgorithm::*;
+ use crate::crypto::mpi::PublicKey;
+
+ self.secret().map(
+ |secret| Ok(match (self.public().mpis(), secret, ciphertext)
+ {
+ (PublicKey::RSA{ ref e, ref n },
+ mpi::SecretKeyMaterial::RSA{ ref p, ref q, ref d, .. },
+ mpi::Ciphertext::RSA{ ref c }) => {
+ // Workaround for #440: Make sure c is of the same
+ // length as n.
+ // XXX: Remove once we depend on nettle > 7.0.0.
+ let c_ = if c.value().len() < n.value().len() {
+ let mut c_ = vec![0; n.value().len() - c.value().len()];
+ c_.extend_from_slice(c.value());
+ Some(c_)
+ } else {
+ // If it is bigger, then the packet is likely
+ // corrupted, tough luck then.
+ None
+ };
+ let c = if let Some(c_) = c_.as_ref() {
+ &c_[..]
+ } else {
+ c.value()
+ };
+ // End of workaround.
+
+ let public = rsa::PublicKey::new(n.value(), e.value())?;
+ let secret = rsa::PrivateKey::new(d.value(), p.value(),
+ q.value(), Option::None)?;
+ let mut rand = Yarrow::default();
+ if let Some(l) = plaintext_len {
+ let mut plaintext: SessionKey = vec![0; l].into();
+ rsa::decrypt_pkcs1(&public, &secret, &mut rand,
+ c, plaintext.as_mut())?;
+ plaintext
+ } else {
+ rsa::decrypt_pkcs1_insecure(&public, &secret,
+ &mut rand, c)?
+ .into()
+ }
+ }
+
+ (PublicKey::ElGamal{ .. },
+ mpi::SecretKeyMaterial::ElGamal{ .. },
+ mpi::Ciphertext::ElGamal{ .. }) =>
+ return Err(
+ Error::UnsupportedPublicKeyAlgorithm(ElGamalEncrypt).into()),
+
+ (PublicKey::ECDH{ .. },
+ mpi::SecretKeyMaterial::ECDH { .. },
+ mpi::Ciphertext::ECDH { .. }) =>
+ crate::crypto::ecdh::decrypt(self.public(), secret, ciphertext)?,
+
+ (public, secret, ciphertext) =>
+ return Err(Error::InvalidOperation(format!(
+ "unsupported combination of key pair {:?}/{:?} \
+ and ciphertext {:?}",
+ public, secret, ciphertext)).into()),
+ }))
+ }
+}
+
+
+impl<P: key::KeyParts, R: key::KeyRole> Key<P, R> {
+ /// Encrypts the given data with this key.
+ pub fn encrypt(&self, data: &SessionKey) -> Result<mpi::Ciphertext> {
+ use crate::PublicKeyAlgorithm::*;
+
+ #[allow(deprecated)]
+ match self.pk_algo() {
+ RSAEncryptSign | RSAEncrypt => {
+ // Extract the public recipient.
+ match self.mpis() {
+ mpi::PublicKey::RSA { e, n } => {
+ // The ciphertext has the length of the modulus.
+ let mut esk = vec![0u8; n.value().len()];
+ let mut rng = Yarrow::default();
+ let pk = rsa::PublicKey::new(n.value(), e.value())?;
+ rsa::encrypt_pkcs1(&pk, &mut rng, data,
+ &mut esk)?;
+ Ok(mpi::Ciphertext::RSA {
+ c: MPI::new(&esk),
+ })
+ },
+ pk => {
+ Err(Error::MalformedPacket(
+ format!(
+ "Key: Expected RSA public key, got {:?}",
+ pk)).into())
+ },
+ }
+ },
+ ECDH => crate::crypto::ecdh::encrypt(self.parts_as_public(),
+ data),
+ algo => Err(Error::UnsupportedPublicKeyAlgorithm(algo).into()),
+ }
+ }
+
+ /// Verifies the given signature.
+ pub fn verify(&self, sig: &packet::Signature, digest: &[u8]) -> Result<()>
+ {
+ use crate::PublicKeyAlgorithm::*;
+ use crate::crypto::mpi::{PublicKey, Signature};
+
+ #[allow(deprecated)]
+ let ok = match (sig.pk_algo(), self.mpis(), sig.mpis()) {
+ (RSASign, PublicKey::RSA { e, n }, Signature::RSA { s }) |
+ (RSAEncryptSign, PublicKey::RSA { e, n }, Signature::RSA { s }) => {
+ let key = rsa::PublicKey::new(n.value(), e.value())?;
+
+ // 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::verify_digest_pkcs1(&key, digest, sig.hash_algo().oid()?,
+ s.value())?
+ },
+ (DSA, PublicKey::DSA{ y, p, q, g }, Signature::DSA { s, r }) => {
+ let key = dsa::PublicKey::new(y.value());
+ let params = dsa::Params::new(p.value(), q.value(), g.value());
+ let signature = dsa::Signature::new(r.value(), s.value());
+
+ dsa::verify(&params, &key, digest, &signature)
+ },
+ (EdDSA, PublicKey::EdDSA{ curve, q }, Signature::EdDSA { r, s }) =>
+ match curve {
+ Curve::Ed25519 => {
+ if q.value().get(0).map(|&b| b != 0x40).unwrap_or(true) {
+ return Err(Error::MalformedPacket(
+ "Invalid point encoding".into()).into());
+ }
+
+ // OpenPGP encodes R and S separately, but our
+ // cryptographic library expects them to be
+ // concatenated.
+ let mut signature =
+ Vec::with_capacity(ed25519::ED25519_SIGNATURE_SIZE);
+
+ // We need to zero-pad them at the front, because
+ // the MPI encoding drops leading zero bytes.
+ let half = ed25519::ED25519_SIGNATURE_SIZE / 2;
+ if r.value().len() < half {
+ for _ in 0..half - r.value().len() {
+ signature.push(0);
+ }
+ }
+ signature.extend_from_slice(r.value());
+ if s.value().len() < half {
+ for _ in 0..half - s.value().len() {
+ signature.push(0);
+ }
+