XTR-DH
Documentation |
#include <cryptopp/xtr.h>
|
XTR-DH is a public key cryptosystem by Arjen Lenstra and Eric Verheul. The cryptosystem uses a compact subgroup trace representation to map elements of a subgroup of [math]\displaystyle{ GF(p^6) }[/math] to a trace over [math]\displaystyle{ GF(p^2) }[/math]. XTR-DH is a key agreement scheme so it uses SimpleKeyAgreementDomain
and nearly everything from Diffie-Hellman applies; only the field and operations change.
As far as we know there are two questions on Stack Overflow for XTR key agreement. They are Parsing the GFP2Element in Crypto++ and Key agreement with XTR-DH Crypto++.
Crypto++ Validation
Crypto++ demonstrates the use of XTR-DH in validate7.cpp
, functions ValidateXTR_DH
and SimpleKeyAgreementValidate
. SimpleKeyAgreementValidate
has the following signature.
bool SimpleKeyAgreementValidate(SimpleKeyAgreementDomain &d)
The body for SimpleKeyAgreementValidate
is shown next. The function validates the group parameters and then performs a pairwise consistency check that ensures two parties can arrive at the same shared secret.
bool SimpleKeyAgreementValidate(SimpleKeyAgreementDomain &d) { if (d.GetCryptoParameters().Validate(GlobalRNG(), 3)) std::cout << "passed ..." << std::endl; else { std::cout << "FAILED ..." << std::endl; return false; } SecByteBlock priv1(d.PrivateKeyLength()), priv2(d.PrivateKeyLength()); SecByteBlock pub1(d.PublicKeyLength()), pub2(d.PublicKeyLength()); SecByteBlock val1(d.AgreedValueLength()), val2(d.AgreedValueLength()); d.GenerateKeyPair(GlobalRNG(), priv1, pub1); d.GenerateKeyPair(GlobalRNG(), priv2, pub2); memset(val1.begin(), 0x10, val1.size()); memset(val2.begin(), 0x11, val2.size()); if (!(d.Agree(val1, priv1, pub2) && d.Agree(val2, priv2, pub1))) { std::cout << "FAILED ..." << std::endl; return false; } if (memcmp(val1.begin(), val2.begin(), d.AgreedValueLength())) { std::cout << "FAILED ..." << std::endl; return false; } std::cout << "passed ..." << std::endl; return true; }
Constructors
XTR-DH has three constructors shown below.
XTR_DH (const Integer &p, const Integer &q, const GFP2Element &g)
The constructor above initializes a XTR_DH
object from existing integers p
and q
, and field element g
.
XTR_DH (RandomNumberGenerator &rng, unsigned int pbits, unsigned int qbits)
The constructor above creates a new XTR_DH
object with the required parameters.
XTR_DH (BufferedTransformation &domainParams)
The constructor above initializes a XTR_DH
object from a serialized object created with void DEREncode (BufferedTransformation &domainParams)
.
Domain Parameters
The following creates XTR-DH domain parameters with security equivalent to a RSA-2048 modulus. It uses p=340
and q=224
(q=256
would work as well). The domain parameters should be generated once and shared between both parties.
AutoSeededRandomPool prng; XTR_DH xtr_params(prng, 340, 224);
The constructor eventually calls XTR_FindPrimesAndGenerator
from xtr.cpp
.
The parameters can be serialized with DEREncode (BufferedTransformation &domainParams)
.
You can print the domain parameters with the following code:
$ cat test.cxx #include "cryptlib.h" #include "osrng.h" #include "xtrcrypt.h" #include <iostream> int main(int argc, char* argv[]) { using namespace CryptoPP; AutoSeededRandomPool aSRP; XTR_DH xtr(aSRP, 340, 224); std::cout << "Prime: " << std::hex << xtr.GetModulus() << std::endl; std::cout << "Order: " << std::hex << xtr.GetSubgroupOrder() << std::endl; std::cout << "Generator" << std::endl; std::cout << " c1: " << std::hex << xtr.GetSubgroupGenerator().c1 << std::endl; std::cout << " c2: " << std::hex << xtr.GetSubgroupGenerator().c2 << std::endl; return 0; }
Running the program with artificially small parameters results in:
$ ./test.exe Prime: 2d4c4f9f4de9e32e84a7be42f019a1a4139e0fe7489h Order: 89ab07fa5115443f51ce9a74283affaae2d7748fh Generator c1: 684fedbae519cb297f3448d5e564838ede5ed1fb81h c2: 39112823212ccd7b01f10377536f51bf855752c7a3h
An easy way to serialize your domain parameters is to save them using ASN.1 as shown below.
int main(int argc, char* argv[]) { using namespace CryptoPP; AutoSeededRandomPool prng; XTR_DH xtr(prng, 170, 160); xtrA.DEREncode(FileSink("params.der").Ref()); ... return 0; }
Once the parameters have been generated then all clients use the same parameters during key agreement.
int main(int argc, char* argv[]) { using namespace CryptoPP; AutoSeededRandomPool prng; XTR_DH xtrA(FileSource("params.der", true).Ref()); XTR_DH xtrB(FileSource("params.der", true).Ref()); if(xtrA.Validate(prng, 3) == false) throw std::runtime_error("Failed to validate parameters"); if(xtrB.Validate(prng, 3) == false) throw std::runtime_error("Failed to validate parameters"); ... return 0; }
You can inspect the serialized domain parameters with tools like dumpasn1
like shown below.
$ dumpasn1 params.der 0 94: SEQUENCE { 2 22: INTEGER 02 EE 07 6B 32 54 C1 52 01 51 BB E0 39 1A 77 97 1F 92 E2 77 BA 37 26 21: INTEGER 00 F7 67 4A 8C 2D D6 8D 32 C3 DA 8E 74 87 4A 48 B9 AD F0 0F CB 49 21: INTEGER 2D 46 9E 63 B4 74 AC 45 57 8A 00 27 A3 88 64 F3 03 FA D0 3B A9 72 22: INTEGER 01 D5 E5 71 4B C1 9E F2 5E EE 05 35 58 41 76 88 9D F8 F2 6C 48 02 : }
Key Pair
The following creates a XTR-DH ephemeral keypair for an instance run using the domain parameters. Recall that "ephemeral" means "transient" or "temporary", and each run of the protocol should generate a new ephemeral keypair. The keys are not saved for subsequent use.
AutoSeededRandomPool prng; XTR_DH params(prng, 340, 224); SecByteBlock priv(params.PrivateKeyLength()), pub(params.PublicKeyLength()); params.GenerateKeyPair(GlobalRNG(), priv, pub);
You can then use Agree
to arrive at a shared secret with the other party as shown in Crypto++ Validation. Note that the exchange is not authenticated so you should protect the exchange with signatures.