diff --git a/Cargo.toml b/Cargo.toml
index d66e10deddc4fe89d692691393ab47e4b8541147..b145e0e983c343d35c92ff5c2a62cde23bdd4003 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -43,5 +43,6 @@ default = ["dewif", "rand", "ser"]
 
 aes256 = ["aes"]
 dewif = ["aes256", "arrayvec"]
+x25519 = []
 rand = []
 ser = ["serde"]
diff --git a/src/keys/mod.rs b/src/keys/mod.rs
index 0f591565dc2d315b9bf26cc36ed9a7241c539961..61cafcc3b342581bda703406648f27d0d7de2dcb 100644
--- a/src/keys/mod.rs
+++ b/src/keys/mod.rs
@@ -52,6 +52,8 @@
 pub mod bin_signable;
 pub mod ed25519;
 pub mod text_signable;
+#[cfg(feature = "x25519")]
+pub(crate) mod x25519;
 
 pub use crate::seeds::Seed32;
 
diff --git a/src/keys/x25519.rs b/src/keys/x25519.rs
new file mode 100644
index 0000000000000000000000000000000000000000..00d4d2244ff378c26a116977f7f360389f9e2dd6
--- /dev/null
+++ b/src/keys/x25519.rs
@@ -0,0 +1,100 @@
+//  Copyright (C) 2020 Éloïs SANCHEZ.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+//! Provide x25519 tools:
+//! - Converters from ed25519 keys to x25519 keys
+//! - Diffie hellman exchange
+
+use super::ed25519::PublicKey;
+use crate::seeds::Seed32;
+use curve25519_dalek::edwards::CompressedEdwardsY;
+use curve25519_dalek::montgomery::MontgomeryPoint;
+use curve25519_dalek::scalar::Scalar;
+use ring::digest;
+use zeroize::Zeroize;
+
+/// x25519 public key
+#[derive(Clone, Copy, Debug)]
+pub(crate) struct X25519PublicKey(MontgomeryPoint);
+
+impl From<&PublicKey> for X25519PublicKey {
+    fn from(ed25519_public_key: &PublicKey) -> Self {
+        let compressed_edwards_y = CompressedEdwardsY::from_slice(ed25519_public_key.as_ref());
+        let edwards_point = compressed_edwards_y
+            .decompress()
+            .expect("dev error: invalid ed25519 public key");
+        X25519PublicKey(edwards_point.to_montgomery())
+    }
+}
+
+#[derive(Zeroize)]
+#[zeroize(drop)]
+pub(crate) struct X25519SecretKey([u8; 32]);
+
+impl From<&Seed32> for X25519SecretKey {
+    fn from(seed: &Seed32) -> Self {
+        let mut hash = [0u8; 64];
+        hash.copy_from_slice(digest::digest(&digest::SHA512, seed.as_ref()).as_ref());
+
+        let mut x25519_sk = [0; 32];
+        x25519_sk[..32].copy_from_slice(&hash[..32]);
+        x25519_sk[0] &= 248;
+        x25519_sk[31] &= 127;
+        x25519_sk[31] |= 64;
+
+        X25519SecretKey(x25519_sk)
+    }
+}
+
+pub(crate) fn diffie_hellman<F, R>(
+    my_secret_key: X25519SecretKey,
+    their_public_key: X25519PublicKey,
+    key_derivation_function: F,
+) -> R
+where
+    F: FnOnce(&[u8]) -> R,
+{
+    key_derivation_function((Scalar::from_bits(my_secret_key.0) * their_public_key.0).as_bytes())
+}
+
+#[cfg(test)]
+mod tests {
+
+    use super::*;
+    use crate::keys::ed25519::KeyPairFromSeed32Generator;
+    use crate::keys::KeyPair;
+
+    #[test]
+    fn test_dh() -> Result<(), crate::rand::UnspecifiedRandError> {
+        let keypair1 = KeyPairFromSeed32Generator::generate(Seed32::random()?);
+        let keypair2 = KeyPairFromSeed32Generator::generate(Seed32::random()?);
+
+        let shared_secret_1 = diffie_hellman(
+            X25519SecretKey::from(keypair1.seed()),
+            X25519PublicKey::from(&keypair2.public_key()),
+            |key_material| key_material.to_vec(),
+        );
+
+        let shared_secret_2 = diffie_hellman(
+            X25519SecretKey::from(keypair2.seed()),
+            X25519PublicKey::from(&keypair1.public_key()),
+            |key_material| key_material.to_vec(),
+        );
+
+        assert_eq!(shared_secret_1, shared_secret_2,);
+
+        Ok(())
+    }
+}