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.