summaryrefslogtreecommitdiffstats
path: root/openpgp/src/crypto/backend/botan/ecdh.rs
diff options
context:
space:
mode:
Diffstat (limited to 'openpgp/src/crypto/backend/botan/ecdh.rs')
-rw-r--r--openpgp/src/crypto/backend/botan/ecdh.rs150
1 files changed, 150 insertions, 0 deletions
diff --git a/openpgp/src/crypto/backend/botan/ecdh.rs b/openpgp/src/crypto/backend/botan/ecdh.rs
new file mode 100644
index 00000000..e798503c
--- /dev/null
+++ b/openpgp/src/crypto/backend/botan/ecdh.rs
@@ -0,0 +1,150 @@
+//! Elliptic Curve Diffie-Hellman.
+
+#[allow(unused_imports)]
+
+use botan::{
+ RandomNumberGenerator,
+ //Pubkey,
+ Privkey,
+};
+
+use crate::{
+ Error,
+ Result,
+};
+use crate::crypto::SessionKey;
+use crate::crypto::ecdh::{encrypt_wrap, decrypt_unwrap};
+use crate::crypto::mem::Protected;
+use crate::crypto::mpi::{
+ MPI,
+ PublicKey, SecretKeyMaterial, Ciphertext};
+use crate::packet::{key, Key};
+use crate::types::Curve;
+
+/// Wraps a session key using Elliptic Curve Diffie-Hellman.
+#[allow(non_snake_case)]
+pub fn encrypt<R>(recipient: &Key<key::PublicParts, R>,
+ session_key: &SessionKey)
+ -> Result<Ciphertext>
+ where R: key::KeyRole
+{
+ let mut rng = RandomNumberGenerator::new_userspace()?;
+
+ if let PublicKey::ECDH {
+ 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)
+ },
+
+ // N/A
+ Curve::Unknown(_) if ! curve.is_brainpoolp384() =>
+ Err(Error::UnsupportedEllipticCurve(curve.clone()).into()),
+ Curve::Ed25519 =>
+ Err(Error::UnsupportedEllipticCurve(curve.clone()).into()),
+
+ Curve::NistP256 | Curve::NistP384 | Curve::NistP521 |
+ Curve::BrainpoolP256 |
+ Curve::Unknown(_) | // XXX: this is BrainpoolP384
+ Curve::BrainpoolP512 => {
+ // Obtain the recipient public key R
+ let R = &q.value();
+
+ // Generate an ephemeral key pair {v, V=vG}
+ let field_size = curve.field_size()?;
+ let v = Privkey::create("ECDH", curve.botan_name()?, &mut rng)?;
+ let Vx = v.pubkey()?.get_field("public_x")?;
+ let Vy = v.pubkey()?.get_field("public_y")?;
+
+ // Compute the shared point S = vR;
+ let S: Protected = v.agree(&R, 32, b"", "Raw")?.into();
+ let Sx: Protected = S[..field_size].into();
+
+ encrypt_wrap(recipient, session_key,
+ MPI::new_point(&Vx.to_bin()?, &Vy.to_bin()?,
+ field_size * 8),
+ &Sx.into())
+ }
+ }
+ } else {
+ Err(Error::InvalidArgument("Expected an ECDHPublicKey".into()).into())
+ }
+}
+
+/// Unwraps a session key using Elliptic Curve Diffie-Hellman.
+#[allow(non_snake_case)]
+pub fn decrypt<R>(recipient: &Key<key::PublicParts, R>,
+ recipient_sec: &SecretKeyMaterial,
+ ciphertext: &Ciphertext)
+ -> Result<SessionKey>
+ where R: key::KeyRole
+{
+ match (recipient.mpis(), recipient_sec, ciphertext) {
+ (PublicKey::ECDH { ref curve, ..},
+ SecretKeyMaterial::ECDH { ref scalar, },
+ 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()
+ },
+
+
+ // N/A
+ Curve::Unknown(_) if ! curve.is_brainpoolp384() => return
+ Err(Error::UnsupportedEllipticCurve(curve.clone()).into()),
+ Curve::Ed25519 => return
+ Err(Error::UnsupportedEllipticCurve(curve.clone()).into()),
+
+
+ Curve::NistP256 | Curve::NistP384 | Curve::NistP521 |
+ Curve::BrainpoolP256 |
+ Curve::Unknown(_) | // XXX: this is BrainpoolP384
+ Curve::BrainpoolP512 => {
+ // Get the public part V of the ephemeral key.
+ let V = &e.value();
+
+ // Get our secret key.
+ let r = Privkey::load_ecdh(
+ &botan::MPI::new_from_bytes(scalar.value())?,
+ curve.botan_name()?)?;
+
+ // Compute the shared point S = rV = rvG, where (r, R)
+ // is the recipient's key pair.
+ r.agree(V, curve.field_size()?, b"", "Raw")?.into()
+ },
+ };
+
+ decrypt_unwrap(recipient, &S, ciphertext)
+ }
+
+ _ =>
+ Err(Error::InvalidArgument("Expected an ECDHPublicKey".into()).into()),
+ }
+}