Elliptic Curve Diffie-Hellman
Elliptic Curve Diffie-Hellman (ECDH) is key agreement protocol performed using elliptical curves rather than traditional integers (see, for example DH and DH2). The protocol allows parties to create a secure channel for communications.
There are two variants of ECDH - ephemeral-ephemeral and ephemeral-static. ephemeral-ephemeral is anonymous and suffers Man in the Middle (MitM) attacks. ephemeral-static does not suffer the man in the middle, but the initiator (client) is not authenticated. For details on ephemeral-ephemeral and ephemeral-static, see NIST Special Publication 800-56A, Recommendation for Pair-Wise Key Establishment Schemes Using Discrete Logarithm Cryptography and Suite B Implementers' Guide to NIST SP 800-56A.
Finally, this page is concerned with two party key agreement. For group key agreement and multicast scenarios, see Multicast Security: A Taxonomy and Some Efficient Constructions and Provably Authenticated Group Diffie-Hellman Key Exchange.
Crypto++'s valdat2.cpp test file performs ECDH validation over Fp and F2m. See ValidateECP and ValidateEC2N for details.
Choice of Fields
ECDH is defined over both prime fields (Fp) and binary fields (F2m). To operate over a prime field, use ECP:
For binary fields, specify EC2N:
The code below performs key agreement using NIST's 256 bit curve over Fp. The agreed upon value is encoded as an Integer because the class overloads the output operator, which makes it easy to print. In practice, the shared secret is usually hashed before use, and then used as a Key Encryption Key (KEK) to transport a random session key; or used as a Content Encryption Key (CEK).
OID CURVE = secp256r1(); AutoSeededRandomPool rng; ECDH < ECP >::Domain dhA( CURVE ), dhB( CURVE ); SecByteBlock privA(dhA.PrivateKeyLength()), pubA(dhA.PublicKeyLength()); SecByteBlock privB(dhB.PrivateKeyLength()), pubB(dhB.PublicKeyLength()); dhA.GenerateKeyPair(rng, privA, pubA); dhB.GenerateKeyPair(rng, privB, pubB); if(dhA.AgreedValueLength() != dhB.AgreedValueLength()) throw runtime_error("Shared shared size mismatch"); SecByteBlock sharedA(dhA.AgreedValueLength()), sharedB(dhB.AgreedValueLength()); if(!dhA.Agree(sharedA, privA, pubB)) throw runtime_error("Failed to reach shared secret (A)"); if(!dhB.Agree(sharedB, privB, pubA)) throw runtime_error("Failed to reach shared secret (B)"); Integer ssa, ssb; ssa.Decode(sharedA.BytePtr(), sharedA.SizeInBytes()); cout << "(A): " << std::hex << ssa << endl; ssb.Decode(sharedB.BytePtr(), sharedB.SizeInBytes()); cout << "(B): " << std::hex << ssb << endl; if(ssa != ssb) throw runtime_error("Failed to reach shared secret (C)"); cout << "Agreed to shared secret" << endl;
In production, the test ssa != ssb cannot be performed since the values will be on different hosts. A problem with agreement will not be detected until data starts flowing - the first data packet received with not authenticate.
A typical run is shown below.
$ ./ecdh.exe (A): 2b21da66d49b8faa85abc71a4cb23298c9b0c035841eef618fded5eaeb551fb6h (B): 2b21da66d49b8faa85abc71a4cb23298c9b0c035841eef618fded5eaeb551fb6h Agreed to shared secret
ecdh-agree.zip - Elliptic Curve Diffie-Hellman (ECDH) key agreement (unauthenticated).