X25519 elliptic curve Diffie-Hellman key exchange in pure-Rust, using curve25519-dalek.

Overview

x25519-dalek

A pure-Rust implementation of x25519 elliptic curve Diffie-Hellman key exchange, with curve operations provided by curve25519-dalek.

This crate provides two levels of API: a bare byte-oriented x25519 function which matches the function specified in RFC7748, as well as a higher-level Rust API for static and ephemeral Diffie-Hellman.

Examples

Alice and Bob are two adorable kittens who have lost their mittens, and they wish to be able to send secret messages to each other to coordinate finding them, otherwise—if their caretaker cat finds out—they will surely be called naughty kittens and be given no pie!

But the two kittens are quite clever. Even though their paws are still too big and the rest of them is 90% fuzziness, these clever kittens have been studying up on modern public key cryptography and have learned a nifty trick called elliptic curve Diffie-Hellman key exchange. With the right incantations, the kittens will be able to secretly organise to find their mittens, and then spend the rest of the afternoon nomming some yummy pie!

First, Alice uses EphemeralSecret::new() and then PublicKey::from() to produce her secret and public keys:

use rand_core::OsRng;
use x25519_dalek::{EphemeralSecret, PublicKey};

let alice_secret = EphemeralSecret::new(OsRng);
let alice_public = PublicKey::from(&alice_secret);

Bob does the same:

# use rand_core::OsRng;
# use x25519_dalek::{EphemeralSecret, PublicKey};
let bob_secret = EphemeralSecret::new(OsRng);
let bob_public = PublicKey::from(&bob_secret);

Alice meows across the room, telling alice_public to Bob, and Bob loudly meows bob_public back to Alice. Alice now computes her shared secret with Bob by doing:

# use rand_core::OsRng;
# use x25519_dalek::{EphemeralSecret, PublicKey};
# let alice_secret = EphemeralSecret::new(OsRng);
# let alice_public = PublicKey::from(&alice_secret);
# let bob_secret = EphemeralSecret::new(OsRng);
# let bob_public = PublicKey::from(&bob_secret);
let alice_shared_secret = alice_secret.diffie_hellman(&bob_public);

Similarly, Bob computes a shared secret by doing:

# use rand_core::OsRng;
# use x25519_dalek::{EphemeralSecret, PublicKey};
# let alice_secret = EphemeralSecret::new(OsRng);
# let alice_public = PublicKey::from(&alice_secret);
# let bob_secret = EphemeralSecret::new(OsRng);
# let bob_public = PublicKey::from(&bob_secret);
let bob_shared_secret = bob_secret.diffie_hellman(&alice_public);

These secrets are the same:

# use rand_core::OsRng;
# use x25519_dalek::{EphemeralSecret, PublicKey};
# let alice_secret = EphemeralSecret::new(OsRng);
# let alice_public = PublicKey::from(&alice_secret);
# let bob_secret = EphemeralSecret::new(OsRng);
# let bob_public = PublicKey::from(&bob_secret);
# let alice_shared_secret = alice_secret.diffie_hellman(&bob_public);
# let bob_shared_secret = bob_secret.diffie_hellman(&alice_public);
assert_eq!(alice_shared_secret.as_bytes(), bob_shared_secret.as_bytes());

Voilá! Alice and Bob can now use their shared secret to encrypt their meows, for example, by using it to generate a key and nonce for an authenticated-encryption cipher.

This example used the ephemeral DH API, which ensures that secret keys cannot be reused; Alice and Bob could instead use the static DH API and load a long-term secret key.

Installation

To install, add the following to your project's Cargo.toml:

[dependencies]
x25519-dalek = "1.1"

Documentation

Documentation is available here.

Note

This code matches the RFC7748 test vectors. The elliptic curve operations are provided by curve25519-dalek, which makes a best-effort attempt to prevent software side-channels.

"Secret Messages" cover image and zine copyright © Amy Wibowo (@sailorhg)

See also

  • crypto_box: pure Rust public-key authenticated encryption compatible with the NaCl family of encryption libraries (libsodium, TweetNaCl) which uses x25519-dalek for key agreement
Comments
  • Update rand_core to 0.6

    Update rand_core to 0.6

    https://github.com/rust-random/rand/blob/master/rand_core/CHANGELOG.md This new version makes using rand_core on wasm seamless (thanks to the update of getrandom to v0.2) The crate compiles well with this PR, but since some rand_core traits are publicly exposed in this crate's API, this is strictly speaking a breaking change.

    opened by PaulGrandperrin 7
  • Serializing EphemeralPublic

    Serializing EphemeralPublic

    We need to serialize EphemeralPublic (over here) to an array of bytes so we can share it between network participants.

    I see that the underlying MontgomeryPoint has a to_bytes() function, but this is inaccessible due to the fact that EphemeralPublic doesn't expose any such function, and the underlying MontgomeryPoint in the EphemeralPublic tuple is private outside of its crate.

    Is there some other way to access a to_bytes() function for the EphemeralPublic struct that has the same effect?

    This was easier in older versions of x25519-dalek, but the older subtle dependency being yanked (as per #19) prevents us from continuing to use the older version of the package.

    opened by thanethomson 7
  • Use zeroize 1 or newer

    Use zeroize 1 or newer

    Version =1.3 was set so the library builds on older rust versions. But this won't allow to build the library if some other dependency requires a newer version.

    See https://gitlab.gnome.org/GNOME/fractal/-/issues/1016#note_1442659

    opened by A6GibKm 6
  • Feedback/Suggestion

    Feedback/Suggestion

    Hey!

    I've been implementing Noise with x25519-dalek and I have a few suggestions:

    • for ephemeral keys, the diffie-hellman operation consumes the private key, which doesn't work if I need to use the ephemeral keys in more than one Diffie-Hellman operation (common with Noise)
    • a public_key(&self) operation on private keys would be really handy
    • a try_from<&[u8]> operation for private and public keys would be really handy

    (happy to take a stab at a PR if you want me to)

    opened by mimoo 6
  • subtl dependency broken again

    subtl dependency broken again

    subtle 0.7 was yanked, but x25519-dalek requires exactly curve25519-dalek 0.19, which depends on exactly subtle 0.7

        Updating git repository `https://github.com/quininer/rust-hacl-star.git`
        Updating crates.io index
    error: failed to select a version for the requirement `subtle = "^0.7"`
      candidate versions found which didn't match: 2.0.0, 1.0.0
      location searched: crates.io index
    required by package `curve25519-dalek v0.19.0`
        ... which is depended on by `x25519-dalek v0.3.0`
        ... which is depended on by `carrier v0.8.0 (/home/aep/proj/devguard/carrier2)`
    
    
    opened by aep 5
  • Types with Drop to hold the secret keys (and others if needed)

    Types with Drop to hold the secret keys (and others if needed)

    libsodium provides a mem-zeroing functionality which gets called from the rust versions (e.g. rust_sodium) for at-least all the secret keys which are user-defined types with Drop. There is no such management in dalek. Just wandering on what the opinions are on this:

    • It is important and we need it and dalek is ready to provide it
    • It is important but the user is expected to do the wrapping but dalek wouldn't see this as its goal
    • It's not needed (if so any reasons would be good to know)
    enhancement help wanted good first issue 
    opened by ustulation 5
  • Unable to build docs with nightly flag

    Unable to build docs with nightly flag

    When building with no-default-feautures, u64_backend std and nightly

        Checking x25519-dalek v0.5.2
    error[E0432]: unresolved import `packed_simd`
      --> /Users/dignifiedquire/.cargo/registry/src/github.com-1ecc6299db9ec823/curve25519-dalek-1.1.4/src/backend/vector/avx2/field.rs:43:5
       |
    43 | use packed_simd::{i32x8, u32x8, u64x4, IntoBits};
       |     ^^^^^^^^^^^ maybe a missing `extern crate packed_simd;`?
    
    opened by dignifiedquire 4
  • Create new types for keys and clear values on drop

    Create new types for keys and clear values on drop

    PR for issue #9

    Created Ephemeral & SharedSecret types to use for keys. diffie_hellman, generate_public, & generate_secret are now methods on Ephemeral. Both types use clear in their respective drop implementations & Ephemeral has an implementation for Mul.

    opened by DebugSteven 4
  • Bump MSRV in `2.0.0` instead of requiring `zeroize =1.3.0`

    Bump MSRV in `2.0.0` instead of requiring `zeroize =1.3.0`

    Hello, I saw you released the first pre-release for 2.0 but I couldn't find the branch to send the PR myself, so I'll write this here: could version 2 bump it's MSRV instead of requiring zeroize =1.3.0 ? This will break compiling with other crates that require zeroize >1.3.0

    opened by paolobarbolini 3
  • Consider zeroize on drop for PublicKey

    Consider zeroize on drop for PublicKey

    Would you consider adding the zeroize on drop feature for the PublicKey type? This feature implies not auto deriving the Copy trait for PublicKey which might break compatibility for some projects using this crate.

    I think this is an important feature and unfortunately it's a very common belief that it's only important to zeroize private key materials. Whereas we would like to achieve the plausibly deniability properties that come from destroying public keys as well.

    The context for this request is steeped in the slow crypto train movement: a slow messaging system, similar to agl's Pond in some ways, should have a "panic button" that removes all ciphertext, public and private key materials and so on. People in high risk situations might like to press such a panic button when the secret police show up.

    What do you think? I'm more than happy to submit a pull request if this is something you'd merge.

    opened by david415 3
  • example code for Transform ed25519 secret/public keys into x25519 secret/public keys

    example code for Transform ed25519 secret/public keys into x25519 secret/public keys

    opened by gcxfd 3
  • Document what you're supposed to do with the SharedSecret

    Document what you're supposed to do with the SharedSecret

    I recently implemented (thankfully only for a school project) an E2E chat system that directly took the SharedSecret from x25519-dalek and used it as a (chacha20poly1305) key. I've now come across this SE answer which claims that such a construction is risky, and I should really have some sort of hashing step between the DH exchange and the actual cipher key.

    Right, ok, rolled my own crypto and got bitten in the ass. Lesson learned, thank god it wasn't for anything real.

    That being said, the docs for SharedSecret currently don't provide any guidance for what one should do with the result. In the spirit of building misuse-resistant tools, it'd be great if there was a little more detail in the docs about what properties it's expected to have, and what it is and isn't safe to be used for.

    opened by Gaelan 1
  • Non-semver pin on `zeroize =1.3` leads to incompatibility with any crate that requires `zeroize >1.4`

    Non-semver pin on `zeroize =1.3` leads to incompatibility with any crate that requires `zeroize >1.4`

    Cargo normally lets you install and use two concurrent versions of a crate, but only if they are not semver compatible (https://github.com/rust-lang/cargo/issues/6584). Therefore the current hard requirement on zeroize =1.3 is inconvenient as it prevents using any crates that depend on newer versions of that crate.

    Please would it be possible to lift this requirement so that later versions of the zeroize crate can be used? As far as I know semver compatibility means that should be OK — am I missing something (does zeroize not follow semver?).

    error: failed to select a version for `zeroize`.
        ... required by package `elliptic-curve v0.12.3`
        ... which satisfies dependency `elliptic-curve = "^0.12"` of package `ecdsa v0.14.8`
        ... which satisfies dependency `ecdsa-core = "^0.14"` of package `p256 v0.11.1`
        ... which satisfies dependency `p256 = "^0.11.1"` of package `webrtc-dtls v0.6.0`
        ... which satisfies dependency `dtls = "^0.6.0"` of package `webrtc v0.5.1`
        ... which satisfies dependency `webrtc = "^0.5.1"` of package `matrix_voip_echo v0.1.0 (/home/rei/repo/utils/matrix_voip_echo)`
    versions that meet the requirements `^1.5` are: 1.5.7, 1.5.6, 1.5.5, 1.5.4, 1.5.3
    
    all possible versions conflict with previously selected packages.
    
      previously selected package `zeroize v1.3.0`
        ... which satisfies dependency `zeroize = "=1.3"` of package `x25519-dalek v1.2.0`
        ... which satisfies dependency `x25519-dalek = "^1.2.0"` of package `vodozemac v0.3.0`
        ... which satisfies dependency `vodozemac = "^0.3.0"` of package `matrix-sdk-crypto v0.6.0`
        ... which satisfies dependency `matrix-sdk-crypto = "^0.6.0"` of package `matrix-sdk-base v0.6.1`
        ... which satisfies dependency `matrix-sdk-base = "^0.6.1"` of package `matrix-sdk v0.6.2`
        ... which satisfies dependency `matrix-sdk = "^0.6.2"` of package `matrix_voip_echo v0.1.0 (/home/rei/repo/utils/matrix_voip_echo)`
    
    opened by reivilibre 4
  • Do you really need to borrow self here?

    Do you really need to borrow self here?

    https://github.com/dalek-cryptography/x25519-dalek/blob/53bb1a3989874ffe526a90fd225b5cb1c18f06c3/src/x25519.rs#L78

    I am trying to use this function however my ephemeral key is a pointer and it has to be that way due to some other packages I am using. Is there a reason that the self on this line of code can't just be &self?

    P.S, I am sorry if I did something wrong here. This is my first time submitting an issue on GitHub.

    opened by BoisterousCoder 1
  • `x25519_dalek::PublicKey::from` generates a different public key compared to OpenSSL when using the same private key

    `x25519_dalek::PublicKey::from` generates a different public key compared to OpenSSL when using the same private key

    I am testing this crate (v1.2.0) using a ED25519 key pair generated by OpenSSL 1.1.1o FIPS 3 May 2022, and checking for correct DH shared secret generation against a x25519_dalek::EphemeralSecret based key pair.

    The issue I am seeing is that starting from the private key generated by OpenSSL, the pubic key generated by PublicKey::from for that private key is different from what OpenSSL generated. Am I using the APIs correctly? What is the correct way to use existing key pairs?

    // Parse existing private key
    let alice_priv_key = pkcs8::KeypairBytes::from_pkcs8_pem(PRIV_KEY).unwrap();
    // Parse existing public key
    let alice_pub_key = pkcs8::PublicKeyBytes::from_public_key_pem(PUB_KEY).unwrap();
    
    // Define Alice's ed25519 pair
    let alice_secret = StaticSecret::from(alice_priv_key.secret_key);
    let alice_public = PublicKey::from(&alice_secret);
    
    let parsed_existing_public = PublicKey::from(alice_pub_key.to_bytes());
    // This should not fail
    assert_eq!(alice_public.to_bytes()[..], parsed_existing_public.to_bytes()[..]);
    

    The complete test code

    #[cfg(test)]
    mod tests {
        fn init() {
            let _ = env_logger::builder().is_test(true).try_init();
        }
    
        /* Creating the ED25519 key pairs
        $ openssl genpkey -algorithm ED25519 > private.pem
        $ openssl pkey -outform PEM -pubout -in private.pem > public.pem
        */
    
        static PUB_KEY: &str = "-----BEGIN PUBLIC KEY-----
    MCowBQYDK2VwAyEADOuQ5lqOanpHOtLV2gqqcYuYkJrdpNueHJ7M9ejY3M0=
    -----END PUBLIC KEY-----";
    
        static PRIV_KEY: &str = "-----BEGIN PRIVATE KEY-----
    MC4CAQAwBQYDK2VwBCIEINLDN5YuEgo6Z5G+ww0kTv33KyQrPN1O+vdeet74+cVm
    -----END PRIVATE KEY-----";
    
        mod test_ed25519_dh {
            use ed25519::pkcs8;
            use ed25519::pkcs8::{DecodePrivateKey, DecodePublicKey, EncodePublicKey};
            use rand_core::OsRng;
            use x25519_dalek::StaticSecret;
            use x25519_dalek::{EphemeralSecret, PublicKey};
    
            #[test]
            fn test_ed25519_dh() {
                super::init();
    
                // Parse existing private key
                let mut alice_priv_key = pkcs8::KeypairBytes::from_pkcs8_pem(super::PRIV_KEY).unwrap();
                // Parse existing public key
                let alice_pub_key = pkcs8::PublicKeyBytes::from_public_key_pem(super::PUB_KEY).unwrap();
    
                // Define Alice's ed25519 pair
                let alice_secret = StaticSecret::from(alice_priv_key.secret_key);
                let alice_public = PublicKey::from(&alice_secret);
    
                // Dump the generated public key to stdout
                alice_priv_key.public_key = Some(alice_public.to_bytes());
                let alice_public_other_form = pkcs8::PublicKeyBytes::try_from(&alice_priv_key).unwrap();
                log::debug!(
                    "Alice generated public key as PEM:\n{}",
                    alice_public_other_form
                        .to_public_key_pem(base64ct::LineEnding::LF)
                        .unwrap()
                );
                /* This is not what OpenSSL generated, shown in PUB_KEY
                   -----BEGIN PUBLIC KEY-----
                   MCowBQYDK2VwAyEAUSd2eTv0CdgCpnccB3re+kxjo9miMQV9Di9SFHc/PQ4=
                   -----END PUBLIC KEY-----
                */
    
                let parsed_existing_public = PublicKey::from(alice_pub_key.to_bytes());
                // This should not fail
                assert_eq!(
                    alice_public.to_bytes()[..],
                    parsed_existing_public.to_bytes()[..]
                );
    
                // Create Bob's ed25519 pair
                let bob_secret = EphemeralSecret::new(OsRng);
                let bob_public = PublicKey::from(&bob_secret);
    
                // Define the shared secrets
                let alice_shared_secret = alice_secret.diffie_hellman(&bob_public);
                let bob_shared_secret = bob_secret.diffie_hellman(&alice_public);
    
                assert_eq!(
                    alice_shared_secret.as_bytes()[..],
                    bob_shared_secret.as_bytes()[..]
                );
            }
        }
    }
    
    opened by alwitt 0
Owner
dalek cryptography
Fast, safe, pure-rust elliptic curve cryptography
dalek cryptography
Incremental hashing based on curve25519-dalek

A toy project on building an incremental hash function using the Ristretto elliptic curve for me to learn Rust. Example code from examples/main.rs: us

Alin Tomescu 2 Apr 1, 2022
Plutonium is a two-device chat application that utilises WebSockets and a X25519 ECDH Key Exchange

Plutonium is a two-device chat application that utilises WebSockets and a X25519 ECDH Key Exchange, in addition to AES-256 to securely communicate between the two clients.

brxken 0 Jul 29, 2022
Multi Party Key Management System (KMS) for Secp256k1 Elliptic curve based digital signatures.

Key Management System (KMS) for curve Secp256k1 Multi Party Key Management System (KMS) for Secp256k1 Elliptic curve based digital signatures. Introdu

[ZenGo X] 61 Dec 28, 2022
Private key finder based on the (Bitcoin) secp256k1 elliptic curve.

keyripper keyripper is a powerful tool developed in Rust to assist in the recovery of Bitcoin private keys by leveraging the Baby-Step Giant-Step (BSG

Denzy 12 Sep 27, 2024
Rust implementation of {t,n}-threshold ECDSA (elliptic curve digital signature algorithm).

Multi-party ECDSA This project is a Rust implementation of {t,n}-threshold ECDSA (elliptic curve digital signature algorithm). Threshold ECDSA include

[ZenGo X] 706 Jan 5, 2023
Implementation of the BLS12-381 pairing-friendly elliptic curve group

bls12_381 This crate provides an implementation of the BLS12-381 pairing-friendly elliptic curve construction. This implementation has not been review

Zero-knowledge Cryptography in Rust 183 Dec 27, 2022
Elliptic curve cryptography on Soroban.

Elliptic Curve Cryptography on Soroban Contract examples and reusable primitives. Groth 16 verifier. This crate provides a SorobanGroth16Verifier obje

Xycloo Labs 5 Feb 10, 2023
A pure-Rust implementation of group operations on Ristretto and Curve25519

curve25519-dalek A pure-Rust implementation of group operations on Ristretto and Curve25519. curve25519-dalek is a library providing group operations

dalek cryptography 611 Dec 25, 2022
An implementation of the OPAQUE password-authenticated key exchange protocol

The OPAQUE key exchange protocol OPAQUE is an asymmetric password-authenticated key exchange protocol. It allows a client to authenticate to a server

Novi 178 Jan 9, 2023
A safe implementation of the secure remote password authentication and key-exchange protocol (SRP), SRP6a and legacy are as features available.

Secure Remote Password (SRP 6 / 6a) A safe implementation of the secure remote password authentication and key-exchange protocol (SRP version 6a). Ver

Sven Assmann 10 Nov 3, 2022
Fast Hilbert space-filling curve transformation using a LUT

Fast Hilbert Fast Hilbert 2D curve computation using an efficient Lookup Table (LUT). Convert from discrete 2D space to 1D hilbert space and reverse V

Armin Becher 20 Nov 3, 2022
Polkadex - An Orderbook-based Decentralized Exchange using the Substrate Blockchain Framework.

What is Polkadex? ?? Polkadex is a Open Source, Decentralized Exchange Platform made using Substrate Blockchain Framework that provides traders with t

Polkadex 243 Dec 16, 2022
Implements ERC-5564 for the bn254 curve using arkworks-rs

erc-5564-bn254 Uses the arkworks-rs suite of libraries, and utilities from rln Usage Note: this scheme should be used with the fork of circom-rln. use

Aaryamann Challani 21 Sep 8, 2023
Bulletproofs and Bulletproofs+ Rust implementation for Aggregated Range Proofs over multiple elliptic curves

Bulletproofs This library implements Bulletproofs+ and Bulletproofs aggregated range proofs with multi-exponent verification. The library supports mul

[ZenGo X] 62 Dec 13, 2022
Rust implementation of multi-party Schnorr signatures over elliptic curves.

Multi Party Schnorr Signatures This library contains several Rust implementations of multi-signature Schnorr schemes. Generally speaking, these scheme

[ZenGo X] 148 Dec 15, 2022
L2 validity rollup combined with blind signatures over elliptic curves inside zkSNARK, to provide offchain anonymous voting with onchain binding execution on Ethereum

blind-ovote Blind-OVOTE is a L2 voting solution which combines the validity rollup ideas with blind signatures over elliptic curves inside zkSNARK, to

Aragon ZK Research 3 Nov 18, 2022
Making Token Exchange program with Solana(Rust), Web3, and Vue

Escrow program for Solana blockchain Tech stack Program (Smart Contract) Rust crates: solana-program, spl-token Solana CLI for test validator UI Types

Hoon Wee 3 May 10, 2022
Implementation of the Grumpkin curve in Rust.

Grumpkin curve implementation in Rust This repository implements the Grumpkin curve for use in Rust, by building off of the code provided by ZCash and

Jules 3 Dec 26, 2022
Pure Rust implementations of the key-committing (and context-committing) AEADs

kc-aeads Pure Rust implementations of the key-committing (and context-committing) AEADs defined in Bellare and Hoang '22. Crash course on the paper: T

Michael Rosenberg 2 Aug 10, 2022