With ECDH (Elliptic Curve Diffie Hellman) we can generate a shared key. Alice generates a secret value (\(a\)) and Bob generates his secret value (\(b\)). Next Alice sends a public value of \(aG\) and Bob sends \(bG\), and where \(G\) is the base point on the curve. Alice then creates \(a(bG)\) and Bob will create \(b(aG)\), and which should be the same value. The curve used is P256. This is because the public key is an \((x,y)\) point on the curve. For P256 we use \(y^2 = x^3 - 3x + b \) over a 256-bit prime number field, and where \(b\)=41058363725152142 ... 256314039467401291. The prime number used is \(p = 2^{256} - 2^{224} + 2^{192} + 2^{96} - 1\).
ECDH in Rust using P256 |
Code
First we create the project with:
cargo new ecdh
We then go into the ecdsa folder, and add the following to the cargo.toml file:
[package] name = "ecdh" version = "1.0.0" authors = ["Bill"] [dependencies] p256={version = "0.9.0", features = ["ecdh"]} hex="0.4.3" rand_core="0.6.3"
Notice that to use the ECDH feature, we need to enable this in the toml file. For this we will use P256 for the key exchange:
use p256::{EncodedPoint, PublicKey, ecdh::EphemeralSecret}; use rand_core::OsRng; fn main() { let a = EphemeralSecret::random(&mut OsRng); let A = EncodedPoint::from(a.public_key()); let b = EphemeralSecret::random(&mut OsRng); let B = EncodedPoint::from(b.public_key()); let bob_public = PublicKey::from_sec1_bytes(B.as_ref()).expect("Bob's public key invalid"); let alice_public = PublicKey::from_sec1_bytes(A.as_ref()).expect("Alice's public key invalid"); let Abytes= A.as_ref(); println!("Alice public key {:x?}",hex::encode(Abytes)); let Bbytes= B.as_ref(); println!("Bob public key {:x?}",hex::encode(Bbytes)); let alice_shared = a.diffie_hellman(&bob_public); let bob_shared = b.diffie_hellman(&alice_public); let shared1= alice_shared.as_bytes(); println!("\nAlice shared key {:x?}",hex::encode(shared1)); let shared2= bob_shared.as_bytes(); println!("\nBob shared key {:x?}",hex::encode(shared2)); if (alice_shared.as_bytes()==bob_shared.as_bytes()){ println!("\nKeys are the same") } }
Finally we simply build with:
cargo build
A sample run is:
Alice public key "04e892942bb2d5c57d89ee47281fb9777ba70af80a6e7f559631dfdc6c2e71cafa761b757c4b4f9a764b91732ec4e767cbe32318c3f91c9c7f671f74f7908a6069" Bob public key "04c2441b9015cf137c86ca4b8e725440cab64b15ae651fe1ade7cd420afa7404665028407c1ebe899974068aff1cfe9629b8ae2fe7bcf3eee8a2ddd9d36a2d141a" Alice shared key "a9652e4361c81a84c8a387e97e2d613caf39e46d3b82103790b90bd9b1ca3c0b" Bob shared key "a9652e4361c81a84c8a387e97e2d613caf39e46d3b82103790b90bd9b1ca3c0b" Keys are the same
We can see that that the public key values are double the size of the shared key. This is because he public key is an \((x,y)\) point on the curve, and is a 512 bit value (rather than a 256-bit value for the private key).