diff options
Diffstat (limited to 'openpgp')
-rw-r--r-- | openpgp/NEWS | 5 | ||||
-rw-r--r-- | openpgp/src/cert.rs | 65 | ||||
-rw-r--r-- | openpgp/src/cert/amalgamation.rs | 21 | ||||
-rw-r--r-- | openpgp/src/crypto/asymmetric.rs | 31 | ||||
-rw-r--r-- | openpgp/src/crypto/backend/botan/ecdh.rs | 36 | ||||
-rw-r--r-- | openpgp/src/crypto/backend/cng/asymmetric.rs | 15 | ||||
-rw-r--r-- | openpgp/src/crypto/backend/cng/ecdh.rs | 69 | ||||
-rw-r--r-- | openpgp/src/crypto/backend/nettle/ecdh.rs | 51 | ||||
-rw-r--r-- | openpgp/src/crypto/backend/openssl/ecdh.rs | 29 | ||||
-rw-r--r-- | openpgp/src/crypto/backend/rust/ecdh.rs | 45 | ||||
-rw-r--r-- | openpgp/src/packet/key.rs | 45 | ||||
-rw-r--r-- | openpgp/src/packet/signature.rs | 24 | ||||
-rw-r--r-- | openpgp/src/packet/signature/cache.rs | 634 |
13 files changed, 829 insertions, 241 deletions
diff --git a/openpgp/NEWS b/openpgp/NEWS index 0e50791d..c30b350d 100644 --- a/openpgp/NEWS +++ b/openpgp/NEWS @@ -42,6 +42,11 @@ - ComponentBundle::other_revocations - ComponentBundle::self_revocations - ComponentBundle::self_signatures + - Add a signature verification cache, which is automatically + consulted by the low-level signature verification functions, like + `Signature::verify_digest`. + - openpgp::packet::signature::cache::SignatureVerificationCache + - openpgp::packet::signature::cache::Entry * Changes in 1.20.0 ** New functionality - S2K::Implicit diff --git a/openpgp/src/cert.rs b/openpgp/src/cert.rs index ebc9c8fd..105a69c0 100644 --- a/openpgp/src/cert.rs +++ b/openpgp/src/cert.rs @@ -1530,10 +1530,11 @@ impl Cert { $sig_type_pat:pat, // pattern to test signature types against $($hash_args:expr),* // additional arguments to pass to hash_method ) => ({ - t!("check!({}, {}, {:?}, {}, ...)", - $desc, stringify!($binding), $binding.$sigs, + let sigs = $binding.$sigs.take(); + t!("check!({}, {}, {} ({:?}), {}, ...)", + $desc, stringify!($binding), sigs.len(), sigs, stringify!($hash_method)); - for sig in $binding.$sigs.take().into_iter() { + for sig in sigs.into_iter() { // Use hash prefix as heuristic. let key = self.primary.key(); match sig.hash_algo().context().and_then(|mut ctx| { @@ -1592,10 +1593,11 @@ impl Cert { $sig_type_pat:pat, // pattern to test signature types against $($verify_args:expr),* // additional arguments to pass to the above ) => ({ - t!("check_3rd_party!({}, {}, {:?}, {}, {}, ...)", - $desc, stringify!($binding), $binding.$sigs, + let sigs = mem::take(&mut $binding.$sigs); + t!("check_3rd_party!({}, {}, {} ({:?}_, {}, {}, ...)", + $desc, stringify!($binding), sigs.len(), sigs, stringify!($verify_method), stringify!($hash_method)); - for sig in mem::take(&mut $binding.$sigs) { + for sig in sigs { // Use hash prefix as heuristic. let key = self.primary.key(); match sig.hash_algo().context().and_then(|mut ctx| { @@ -1678,6 +1680,10 @@ impl Cert { verify_primary_key_revocation, hash_direct_key, KeyRevocation); + // Attestations are never associated with a primary key. If + // there are any, they need to be reordered. + self.bad.append(&mut self.primary.attestations.take()); + for ua in self.userids.iter_mut() { check!(format!("userid \"{}\"", String::from_utf8_lossy(ua.userid().value())), @@ -1762,6 +1768,10 @@ impl Cert { verify_subkey_revocation, hash_subkey_binding, SubkeyRevocation, binding.key()); + + // Attestations are never associated with a subkey. If + // there are any, they need to be reordered. + self.bad.append(&mut binding.attestations.take()); } // See if the signatures that didn't validate are just out of @@ -1773,6 +1783,7 @@ impl Cert { (None, sig) }) .collect(); + t!("Attempting to reorder {} signatures", bad_sigs.len()); // Do the same for signatures on unknown components, but // remember where we took them from. @@ -1815,9 +1826,9 @@ impl Cert { $($verify_args:expr),* // additional arguments for the above ) => ({ if is_selfsig { - t!("check_one!({}, {:?}, {:?}, {}, ...)", - $desc, $sigs, $sig, - stringify!($hash_method)); + t!("check_one!({}, {:?}, {:?}/{}, {}, ...)", + $desc, $sigs, $sig, $sig.typ(), + stringify!($hash_method)); // Use hash prefix as heuristic. let key = self.primary.key(); if let Ok(hash) = $sig.hash_algo().context() @@ -7351,6 +7362,42 @@ Pu1xwz57O4zo1VYf6TqHJzVC3OMvMUM2hhdecMUe5x6GorNaj6g= Ok(()) } + /// Makes sure that attested key signatures are correctly reordered. + #[test] + fn attested_key_signature_out_of_order() -> Result<()> { + let p = &crate::policy::StandardPolicy::new(); + + let (alice, _) = CertBuilder::general_purpose( + None, Some("alice@example.org")).generate().unwrap(); + assert!(alice.keys().subkeys().count() > 0); + let mut alice_signer = + alice.primary_key().key().clone().parts_into_secret()? + .into_keypair()?; + + // Now, create new attestation signatures. + let mut attestation_signatures = Vec::new(); + for uid in alice.userids() { + attestation_signatures.append(&mut uid.attest_certifications( + p, + &mut alice_signer, + uid.certifications(), + )?); + } + + // Add the new signatures. This appends the attestation + // signature so that it is considered part of last component, + // a subkey. + let alice2 = alice.insert_packets(attestation_signatures)?; + + // Now we make sure the attestation signature was correctly reordered. + assert_eq!(alice2.bad_signatures().count(), 0); + assert!(alice2.keys().all(|ka| ka.attestations().count() == 0)); + let ua = alice2.userids().next().unwrap(); + assert_eq!(ua.attestations().count(), 1); + + Ok(()) + } + /// Makes sure that marker packets are ignored when parsing certs. #[test] fn marker_packets() -> Result<()> { diff --git a/openpgp/src/cert/amalgamation.rs b/openpgp/src/cert/amalgamation.rs index f65c588c..f4c024e4 100644 --- a/openpgp/src/cert/amalgamation.rs +++ b/openpgp/src/cert/amalgamation.rs @@ -1405,14 +1405,13 @@ impl<'a> UserIDAmalgamation<'a> { /// This feature is [experimental](crate#experimental-features). /// /// Allows the certificate owner to attest to third party - /// certifications. See [Section 5.2.3.30 of RFC 4880bis] for - /// details. This can be used to address certificate flooding - /// concerns. + /// certifications. See [draft-dkg-openpgp-1pa3pc] for details. + /// This can be used to address certificate flooding concerns. /// /// A policy is needed, because the expiration is updated by /// updating the current binding signatures. /// - /// [Section 5.2.3.30 of RFC 4880bis]: https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-10.html#section-5.2.3.30 + /// [draft-dkg-openpgp-1pa3pc]: https://www.ietf.org/archive/id/draft-dkg-openpgp-1pa3pc-00.html /// /// # Examples /// @@ -1583,8 +1582,18 @@ where C: IntoIterator<Item = S>, // Backdate the signature a little so that we can immediately // override it. use crate::packet::signature::SIG_BACKDATE_BY; - let creation_time = - crate::now() - time::Duration::new(SIG_BACKDATE_BY, 0); + + let now = crate::now(); + let mut creation_time = + now - time::Duration::new(SIG_BACKDATE_BY, 0); + + // ... but don't backdate it further than the key's creation + // time, which would make it invalid. + let key_creation_time = primary_signer.public().creation_time(); + if creation_time < key_creation_time { + // ... unless that would make it is later than now. + creation_time = key_creation_time.min(now); + } let template = SignatureBuilder::new(SignatureType::AttestationKey) .set_signature_creation_time(creation_time)?; diff --git a/openpgp/src/crypto/asymmetric.rs b/openpgp/src/crypto/asymmetric.rs index 6420048d..b83ce0ba 100644 --- a/openpgp/src/crypto/asymmetric.rs +++ b/openpgp/src/crypto/asymmetric.rs @@ -257,9 +257,40 @@ impl Decryptor for KeyPair { plaintext_len: Option<usize>) -> Result<SessionKey> { + use crate::crypto::backend::{Backend, interface::Asymmetric}; + self.secret().map(|secret| { + #[allow(non_snake_case)] #[allow(clippy::match_single_binding)] match (self.public().mpis(), secret, ciphertext) { + (mpi::PublicKey::ECDH { curve: Curve::Cv25519, .. }, + mpi::SecretKeyMaterial::ECDH { scalar, }, + mpi::Ciphertext::ECDH { e, .. }) => + { + // Get the public part V of the ephemeral key. + let V = e.decode_point(&Curve::Cv25519)?.0; + + // X25519 expects the private key to be exactly 32 + // bytes long but OpenPGP allows leading zeros to + // be stripped. Padding has to be unconditional; + // otherwise we have a secret-dependent branch. + let mut r = scalar.value_padded(32); + + // Reverse the scalar. See + // https://lists.gnupg.org/pipermail/gnupg-devel/2018-February/033437.html + r.reverse(); + + // Compute the shared point S = rV = rvG, where + // (r, R) is the recipient's key pair. + dbg!(r.as_ref()); + dbg!(&V); + let S = dbg!(Backend::x25519_shared_point(&r, &V.try_into()?))?; + + crate::crypto::ecdh::decrypt_unwrap2( + self.public(), &S, ciphertext, plaintext_len) + + }, + (_public, secret, _ciphertext) => self.decrypt_backend(secret, ciphertext, plaintext_len), } diff --git a/openpgp/src/crypto/backend/botan/ecdh.rs b/openpgp/src/crypto/backend/botan/ecdh.rs index 41591421..6e5da2bb 100644 --- a/openpgp/src/crypto/backend/botan/ecdh.rs +++ b/openpgp/src/crypto/backend/botan/ecdh.rs @@ -31,21 +31,8 @@ pub fn encrypt<R>(recipient: &Key<key::PublicParts, R>, ref curve, ref q,.. } = recipient.mpis() { match curve { - Curve::Cv25519 => { - // Obtain the recipient public key R - let R = &q.decode_point(curve)?.0; - - // Generate an ephemeral key pair {v, V=vG} - let v = Privkey::create("Curve25519", "", &mut rng)?; - let V = v.pubkey()?.get_x25519_key()?; - - // Compute the shared point S = vR; - let S: Protected = v.agree(&R, 32, b"", "Raw")?.into(); - - encrypt_wrap(recipient, session_key, - MPI::new_compressed_point(&V), - &S) - }, + Curve::Cv25519 => + Err(Error::InvalidArgument("implemented elsewhere".into()).into()), // N/A Curve::Unknown(_) if ! curve.is_brainpoolp384() => @@ -96,23 +83,8 @@ pub fn decrypt<R>(recipient: &Key<key::PublicParts, R>, Ciphertext::ECDH { ref e, .. }) => { let S: Protected = match curve { - Curve::Cv25519 => { - // Get the public part V of the ephemeral key. - let V = e.decode_point(curve)?.0; - - // Get our secret key. - let mut r = scalar.value_padded(32); - - // Reverse the scalar. See - // https://lists.gnupg.org/pipermail/gnupg-devel/2018-February/033437.html. - r.reverse(); - let r = Privkey::load_x25519(&r)?; - - // Compute the shared point S = rV = rvG, where (r, R) - // is the recipient's key pair. - r.agree(&V, 32, b"", "Raw")?.into() - }, - + Curve::Cv25519 => return + Err(Error::InvalidArgument("implemented elsewhere".into()).into()), // N/A Curve::Unknown(_) if ! curve.is_brainpoolp384() => return diff --git a/openpgp/src/crypto/backend/cng/asymmetric.rs b/openpgp/src/crypto/backend/cng/asymmetric.rs index 4bd8fa70..b7c17a49 100644 --- a/openpgp/src/crypto/backend/cng/asymmetric.rs +++ b/openpgp/src/crypto/backend/cng/asymmetric.rs @@ -68,7 +68,10 @@ impl Asymmetric for super::Backend { let mut public = [0u8; 32]; public.copy_from_slice(pair.x()); - Ok((pair.d().into(), public)) + let mut clamped_secret = pair.d().into(); + Self::x25519_clamp_secret(&mut clamped_secret); + + Ok((clamped_secret, public)) } fn x25519_derive_public(secret: &Protected) -> Result<[u8; 32]> { @@ -79,9 +82,12 @@ impl Asymmetric for super::Backend { let provider = AsymmetricAlgorithm::open( AsymmetricAlgorithmId::Ecdh(NamedCurve::Curve25519) )?; + + let mut clamped_secret = secret.clone(); + Self::x25519_clamp_secret(&mut clamped_secret); let key = AsymmetricKey::<Ecdh<Curve25519>, Private>::import_from_parts( &provider, - secret, + &clamped_secret, )?; Ok(<[u8; 32]>::try_from(&key.export()?.x()[..])?) } @@ -101,10 +107,13 @@ impl Asymmetric for super::Backend { &provider, public, )?; + + let mut clamped_secret = secret.clone(); + Self::x25519_clamp_secret(&mut clamped_secret); let secret = AsymmetricKey::<Ecdh<Curve25519>, Private>::import_from_parts( &provider, - secret, + &clamped_secret, )?; let shared = secret_agreement(&secret, &public)?; diff --git a/openpgp/src/crypto/backend/cng/ecdh.rs b/openpgp/src/crypto/backend/cng/ecdh.rs index afa688d1..285a408c 100644 --- a/openpgp/src/crypto/backend/cng/ecdh.rs +++ b/openpgp/src/crypto/backend/cng/ecdh.rs @@ -32,34 +32,9 @@ where }; match curve { - Curve::Cv25519 => { - // Obtain the authenticated recipient public key R - let R = q.decode_point(curve)?.0; - let provider = AsymmetricAlgorithm::open(AsymmetricAlgorithmId::Ecdh(NamedCurve::Curve25519))?; - let recipient_key = AsymmetricKey::<Ecdh<Curve25519>, Public>::import_from_parts( - &provider, - R, - )?; + Curve::Cv25519 => return + Err(Error::InvalidArgument("implemented elsewhere".into()).into()), - // Generate an ephemeral key pair {v, V=vG} - let ephemeral = AsymmetricKey::builder(Ecdh(Curve25519)).build().unwrap(); - - // Compute the public key. We need to add an encoding - // octet in front of the key. - let blob = ephemeral.export().unwrap(); - let mut VB = [0; 33]; - VB[0] = 0x40; - VB[1..].copy_from_slice(blob.x()); - let VB = MPI::new(&VB); - - // Compute the shared point S = vR; - let secret = cng::asymmetric::agreement::secret_agreement(&ephemeral, &recipient_key)?; - let mut S = Protected::from(secret.derive_raw()?); - // Returned secret is little-endian, flip it to big-endian - S.reverse(); - - encrypt_wrap(recipient, session_key, VB, &S) - } Curve::NistP256 | Curve::NistP384 | Curve::NistP521 => { let (Rx, Ry) = q.decode_point(curve)?; @@ -166,44 +141,8 @@ where }; let S: Protected = match curve { - Curve::Cv25519 => { - // Get the public part V of the ephemeral key. - let V = e.decode_point(curve)?.0; - - let provider = AsymmetricAlgorithm::open(AsymmetricAlgorithmId::Ecdh(NamedCurve::Curve25519))?; - let V = AsymmetricKey::<Ecdh<Curve25519>, Public>::import_from_parts( - &provider, - V, - )?; - - let mut scalar = scalar.value_padded(32); - // Reverse the scalar. See - // https://lists.gnupg.org/pipermail/gnupg-devel/2018-February/033437.html. - scalar.reverse(); - - // Some bits must be clamped. Usually, this is done - // during key creation. However, if that is not done, we - // should do that before handing it to CNG. See - // Curve25519 Paper, Sec. 3: - // - // > A user can, for example, generate 32 uniform random - // > bytes, clear bits 0, 1, 2 of the first byte, clear bit - // > 7 of the last byte, and set bit 6 of the last byte. - scalar[0] &= 0b1111_1000; - scalar[CURVE25519_SIZE - 1] &= !0b1000_0000; - scalar[CURVE25519_SIZE - 1] |= 0b0100_0000; - - let r = AsymmetricKey::<Ecdh<Curve25519>, Private>::import_from_parts( - &provider, - &scalar, - )?; - - let secret = cng::asymmetric::agreement::secret_agreement(&r, &V)?; - // Returned secret is little-endian, flip it to big-endian - let mut secret = secret.derive_raw()?; - secret.reverse(); - secret.into() - } + Curve::Cv25519 => return + Err(Error::InvalidArgument("implemented elsewhere".into()).into()), Curve::NistP256 | Curve::NistP384 | Curve::NistP521 => { // Get the public part V of the ephemeral key and diff --git a/openpgp/src/crypto/backend/nettle/ecdh.rs b/openpgp/src/crypto/backend/nettle/ecdh.rs index 69cf4752..a3fa9775 100644 --- a/openpgp/src/crypto/backend/nettle/ecdh.rs +++ b/openpgp/src/crypto/backend/nettle/ecdh.rs @@ -1,6 +1,6 @@ //! Elliptic Curve Diffie-Hellman. -use nettle::{curve25519, ecc, ecdh, random::Yarrow}; +use nettle::{ecc, ecdh, random::Yarrow}; use crate::{Error, Result}; use crate::crypto::SessionKey; @@ -23,28 +23,9 @@ pub fn encrypt<R>(recipient: &Key<key::PublicParts, R>, ref curve, ref q,.. } = recipient.mpis() { match curve { - Curve::Cv25519 => { - // Obtain the authenticated recipient public key R - let R = q.decode_point(curve)?.0; + Curve::Cv25519 => + Err(Error::InvalidArgument("implemented elsewhere".into()).into()), - // Generate an ephemeral key pair {v, V=vG} - let v: Protected = - curve25519::private_key(&mut rng).into(); - - // Compute the public key. - let mut VB = [0; curve25519::CURVE25519_SIZE]; - curve25519::mul_g(&mut VB, &v) - .expect("buffers are of the wrong size"); - let VB = MPI::new_compressed_point(&VB); - - // Compute the shared point S = vR; - let mut S: Protected = - vec![0; curve25519::CURVE25519_SIZE].into(); - curve25519::mul(&mut S, &v, R) - .expect("buffers are of the wrong size"); - - encrypt_wrap(recipient, session_key, VB, &S) - } Curve::NistP256 | Curve::NistP384 | Curve::NistP521 => { // Obtain the authenticated recipient public key R and // generate an ephemeral private key v. @@ -130,30 +111,8 @@ pub fn decrypt<R>(recipient: &Key<key::PublicParts, R>, Ciphertext::ECDH { ref e, .. }) => { let S: Protected = match curve { - Curve::Cv25519 => { - // Get the public part V of the ephemeral key. - let V = e.decode_point(curve)?.0; - - // Nettle expects the private key to be exactly - // CURVE25519_SIZE bytes long but OpenPGP allows leading - // zeros to be stripped. - // Padding has to be unconditional; otherwise we have a - // secret-dependent branch. - let mut r = - scalar.value_padded(curve25519::CURVE25519_SIZE); - - // Reverse the scalar. See - // https://lists.gnupg.org/pipermail/gnupg-devel/2018-February/033437.html. - r.reverse(); - - // Compute the shared point S = rV = rvG, where (r, R) - // is the recipient's key pair. - let mut S: Protected = - vec![0; curve25519::CURVE25519_SIZE].into(); - curve25519::mul(&mut S, &r[..], V) - .expect("buffers are of the wrong size"); - S - } + Curve::Cv25519 => return + Err(Error::InvalidArgument("implemented elsewhere".into()).into()), Curve::NistP256 | Curve::NistP384 | Curve::NistP521 => { // Get the public part V of the ephemeral key and diff --git a/openpgp/src/crypto/backend/openssl/ecdh.rs b/openpgp/src/crypto/backend/openssl/ecdh.rs index 04a87c23..41e6e67c 100644 --- a/openpgp/src/crypto/backend/openssl/ecdh.rs +++ b/openpgp/src/crypto/backend/openssl/ecdh.rs @@ -27,19 +27,7 @@ where _ => return Err(Error::InvalidArgument("Expected an ECDHPublicKey".into()).into()), }; if curve == &Curve::Cv25519 { - let public = q.decode_point(curve)?.0; - - let public_key = PKey::public_key_from_raw_bytes(public, openssl::pkey::Id::X25519)?; - - let key = PKey::generate_x25519()?; - let mut deriver = Deriver::new(&key)?; - deriver.set_peer(&public_key)?; - - let secret = deriver.derive_to_vec()?.into(); - - let q = mpi::MPI::new_compressed_point(&key.raw_public_key()?); - - return encrypt_wrap(recipient, session_key, q, &secret); + return Err(Error::InvalidArgument("implemented elsewhere".into()).into()); } let nid = curve.try_into()?; @@ -88,20 +76,7 @@ where }; if curve == &Curve::Cv25519 { - let mut scalar = scalar.value_padded(32); - scalar.reverse(); - - let key = PKey::private_key_from_raw_bytes(&scalar[..], openssl::pkey::Id::X25519)?; - - let public = e.decode_point(curve)?.0; - let public_key = PKey::public_key_from_raw_bytes(public, openssl::pkey::Id::X25519)?; - - let mut deriver = Deriver::new(&key)?; - deriver.set_peer(&public_key)?; - let secret = deriver.derive_to_vec()?.into(); - - return decrypt_unwrap2(recipient.role_as_unspecified(), &secret, - ciphertext, plaintext_len); + return Err(Error::InvalidArgument("implemented elsewhere".into()).into()); } let nid = curve.try_into()?; diff --git a/openpgp/src/crypto/backend/rust/ecdh.rs b/openpgp/src/crypto/backend/rust/ecdh.rs index 77fd7d23..f3b3d84f 100644 --- a/openpgp/src/crypto/backend/rust/ecdh.rs +++ b/openpgp/src/crypto/backend/rust/ecdh.rs @@ -32,34 +32,9 @@ pub fn encrypt<R>(recipient: &Key<key::PublicParts, R>, }; let (VB, shared) = match curve { - Curve::Cv25519 => { - // x25519_dalek v1.1 doesn't reexport OsRng. It - // depends on rand 0.8. - use rand::rngs::OsRng; - use x25519_dalek::{EphemeralSecret, PublicKey}; + Curve::Cv25519 => return + Err(Error::InvalidArgument("implemented elsewhere".into()).into()), - // Decode the recipient's public key. - let R: [u8; CURVE25519_SIZE] = q.decode_point(curve)?.0.try_into()?; - let recipient_key = PublicKey::from(R); - - // Generate a keypair and perform Diffie-Hellman. - let secret = EphemeralSecret::random_from_rng(OsRng); - let public = PublicKey::from(&secret); - let shared = secret.diffie_hellman(&recipient_key); - - // Encode our public key. We need to add an encoding - // octet in front of the key. - let mut VB = [0; 1 + CURVE25519_SIZE]; - VB[0] = 0x40; - VB[1..].copy_from_slice(public.as_bytes()); - let VB = MPI::new(&VB); - - // Encode the shared secret. - let shared: &[u8] = shared.as_bytes(); - let shared = Protected::from(shared); - - (VB, shared) - }, Curve::NistP256 => { use p256::{EncodedPoint, PublicKey, ecdh::EphemeralSecret}; @@ -150,21 +125,9 @@ pub fn decrypt<R>(recipient: &Key<key::PublicParts, R>, }; let S: Protected = match curve { - Curve::Cv25519 => { - use x25519_dalek::{PublicKey, StaticSecret}; + Curve::Cv25519 => return + Err(Error::InvalidArgument("implemented elsewhere".into()).into()), - // Get the public part V of the ephemeral key. - let V: [u8; CURVE25519_SIZE] = e.decode_point(curve)?.0.try_into()?; - let V = PublicKey::from(V); - - let mut scalar: [u8; CURVE25519_SIZE] = - scalar.value_padded(CURVE25519_SIZE).as_ref().try_into()?; - scalar.reverse(); - let r = StaticSecret::from(scalar); - - let secret = r.diffie_hellman(&V); - Vec::from(secret.to_bytes()).into() - }, Curve::NistP256 => { use p256::{SecretKey, PublicKey}; const NISTP256_SIZE: usize = 32; diff --git a/openpgp/src/packet/key.rs b/openpgp/src/packet/key.rs index a6d7c660..38bcf7cd 100644 --- a/openpgp/src/packet/key.rs +++ b/openpgp/src/packet/key.rs @@ -742,6 +742,8 @@ impl<P, R> Key<P, R> { /// Encrypts the given data with this key. pub fn encrypt(&self, data: &SessionKey) -> Result<mpi::Ciphertext> { + use crate::crypto::backend::{Backend, interface::Asymmetric}; + use crate::crypto::mpi::PublicKey; use PublicKeyAlgorithm::*; #[allow(deprecated, non_snake_case)] @@ -751,6 +753,29 @@ impl<P, R> Key<P, R> format!("{} is not an encryption algorithm", self.pk_algo()) ).into()), + ECDH if matches!(self.mpis(), + PublicKey::ECDH { curve: Curve::Cv25519, ..}) => + { + let q = match self.mpis() { + PublicKey::ECDH { q, .. } => q, + _ => unreachable!(), + }; + + // Obtain the authenticated recipient public key R + let R = q.decode_point(&C |