ext-cryptopp/validat6.cpp
Jeffrey Walton c9ef9420e7
Fix ECP leakage in Add() and Double() (GH #869, PR #871)
This check-in provides the fix for leaks in ECP's Add() and Double(). The fixes were taken from Joost Renes, Craig Costello, and Lejla Batina's [Complete addition formulas for prime order elliptic curves](https://eprint.iacr.org/2015/1060.pdf).

The Pull Request includes two additional changes that were related to testing the primary fix. First, an `AuthenticatedKeyAgreementWithRolesValidate` interface was added. It allows us to test key agreement when roles are involved. Roles are "client", "server", "initiator", "recipient", etc.

Second, `SetGlobalSeed` was added to `test.cpp` to help with reproducible results. We had code in two different places that set the seed value for the random number generator. But it was sloppy and doing a poor job since results could not be reproduced under some circumstances.
2019-08-05 03:51:58 -04:00

377 lines
13 KiB
C++

// validat6.cpp - originally written and placed in the public domain by Wei Dai
// CryptoPP::Test namespace added by JW in February 2017.
// Source files split in July 2018 to expedite compiles.
#include "pch.h"
#define CRYPTOPP_ENABLE_NAMESPACE_WEAK 1
#include "cryptlib.h"
#include "cpu.h"
#include "validate.h"
#include "asn.h"
#include "oids.h"
#include "blumshub.h"
#include "eccrypto.h"
#include <iostream>
#include <iomanip>
#include <sstream>
// Aggressive stack checking with VS2005 SP1 and above.
#if (_MSC_FULL_VER >= 140050727)
# pragma strict_gs_check (on)
#endif
#if CRYPTOPP_MSC_VERSION
# pragma warning(disable: 4505 4355)
#endif
NAMESPACE_BEGIN(CryptoPP)
NAMESPACE_BEGIN(Test)
bool CryptoSystemValidate(PK_Decryptor &priv, PK_Encryptor &pub, bool thorough)
{
bool pass = true, fail;
fail = !pub.GetMaterial().Validate(GlobalRNG(), thorough ? 3 : 2) || !priv.GetMaterial().Validate(GlobalRNG(), thorough ? 3 : 2);
pass = pass && !fail;
std::cout << (fail ? "FAILED " : "passed ");
std::cout << "cryptosystem key validation\n";
const byte message[] = "test message";
const int messageLen = 12;
SecByteBlock ciphertext(priv.CiphertextLength(messageLen));
SecByteBlock plaintext(priv.MaxPlaintextLength(ciphertext.size()));
pub.Encrypt(GlobalRNG(), message, messageLen, ciphertext);
fail = priv.Decrypt(GlobalRNG(), ciphertext, priv.CiphertextLength(messageLen), plaintext) != DecodingResult(messageLen);
fail = fail || memcmp(message, plaintext, messageLen);
pass = pass && !fail;
std::cout << (fail ? "FAILED " : "passed ");
std::cout << "encryption and decryption\n";
return pass;
}
bool SimpleKeyAgreementValidate(SimpleKeyAgreementDomain &d)
{
if (d.GetCryptoParameters().Validate(GlobalRNG(), 3))
std::cout << "passed simple key agreement domain parameters validation" << std::endl;
else
{
std::cout << "FAILED simple key agreement domain parameters invalid" << 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 simple key agreement failed" << std::endl;
return false;
}
if (memcmp(val1.begin(), val2.begin(), d.AgreedValueLength()))
{
std::cout << "FAILED simple agreed values not equal" << std::endl;
return false;
}
std::cout << "passed simple key agreement" << std::endl;
return true;
}
bool AuthenticatedKeyAgreementValidate(AuthenticatedKeyAgreementDomain &d)
{
if (d.GetCryptoParameters().Validate(GlobalRNG(), 3))
std::cout << "passed authenticated key agreement domain parameters validation" << std::endl;
else
{
std::cout << "FAILED authenticated key agreement domain parameters invalid" << std::endl;
return false;
}
SecByteBlock spriv1(d.StaticPrivateKeyLength()), spriv2(d.StaticPrivateKeyLength());
SecByteBlock epriv1(d.EphemeralPrivateKeyLength()), epriv2(d.EphemeralPrivateKeyLength());
SecByteBlock spub1(d.StaticPublicKeyLength()), spub2(d.StaticPublicKeyLength());
SecByteBlock epub1(d.EphemeralPublicKeyLength()), epub2(d.EphemeralPublicKeyLength());
SecByteBlock val1(d.AgreedValueLength()), val2(d.AgreedValueLength());
d.GenerateStaticKeyPair(GlobalRNG(), spriv1, spub1);
d.GenerateStaticKeyPair(GlobalRNG(), spriv2, spub2);
d.GenerateEphemeralKeyPair(GlobalRNG(), epriv1, epub1);
d.GenerateEphemeralKeyPair(GlobalRNG(), epriv2, epub2);
memset(val1.begin(), 0x10, val1.size());
memset(val2.begin(), 0x11, val2.size());
if (d.Agree(val1, spriv1, epriv1, spub2, epub2) && d.Agree(val2, spriv2, epriv2, spub1, epub1))
{
std::cout << "passed authenticated key agreement protocol execution" << std::endl;
}
else
{
std::cout << "FAILED authenticated key agreement protocol execution" << std::endl;
return false;
}
if (memcmp(val1.begin(), val2.begin(), d.AgreedValueLength()))
{
std::cout << "FAILED authenticated agreed values not equal" << std::endl;
return false;
}
std::cout << "passed authenticated key agreement" << std::endl;
return true;
}
bool AuthenticatedKeyAgreementWithRolesValidate(AuthenticatedKeyAgreementDomain &initiator, AuthenticatedKeyAgreementDomain &recipient)
{
if (initiator.GetCryptoParameters().Validate(GlobalRNG(), 3))
std::cout << "passed authenticated key agreement domain parameters validation (initiator)" << std::endl;
else
{
std::cout << "FAILED authenticated key agreement domain parameters invalid (initiator)" << std::endl;
return false;
}
if (recipient.GetCryptoParameters().Validate(GlobalRNG(), 3))
std::cout << "passed authenticated key agreement domain parameters validation (recipient)" << std::endl;
else
{
std::cout << "FAILED authenticated key agreement domain parameters invalid (recipient)" << std::endl;
return false;
}
if (initiator.StaticPrivateKeyLength() != recipient.StaticPrivateKeyLength() ||
initiator.EphemeralPrivateKeyLength() != recipient.EphemeralPrivateKeyLength() ||
initiator.StaticPublicKeyLength() != recipient.StaticPublicKeyLength() ||
initiator.EphemeralPublicKeyLength() != recipient.EphemeralPublicKeyLength() ||
initiator.AgreedValueLength() != recipient.AgreedValueLength())
{
std::cout << "FAILED authenticated key agreement domain parameter consistency" << std::endl;
return false;
}
else
{
std::cout << "passed authenticated key agreement domain parameter consistency" << std::endl;
}
SecByteBlock spriv1(initiator.StaticPrivateKeyLength()), spriv2(recipient.StaticPrivateKeyLength());
SecByteBlock epriv1(initiator.EphemeralPrivateKeyLength()), epriv2(recipient.EphemeralPrivateKeyLength());
SecByteBlock spub1(initiator.StaticPublicKeyLength()), spub2(recipient.StaticPublicKeyLength());
SecByteBlock epub1(initiator.EphemeralPublicKeyLength()), epub2(recipient.EphemeralPublicKeyLength());
SecByteBlock val1(initiator.AgreedValueLength()), val2(recipient.AgreedValueLength());
initiator.GenerateStaticKeyPair(GlobalRNG(), spriv1, spub1);
recipient.GenerateStaticKeyPair(GlobalRNG(), spriv2, spub2);
initiator.GenerateEphemeralKeyPair(GlobalRNG(), epriv1, epub1);
recipient.GenerateEphemeralKeyPair(GlobalRNG(), epriv2, epub2);
memset(val1.begin(), 0x10, val1.size());
memset(val2.begin(), 0x11, val2.size());
if (initiator.Agree(val1, spriv1, epriv1, spub2, epub2) && recipient.Agree(val2, spriv2, epriv2, spub1, epub1))
{
std::cout << "passed authenticated key agreement protocol execution" << std::endl;
}
else
{
std::cout << "FAILED authenticated key agreement protocol execution" << std::endl;
return false;
}
if (memcmp(val1.begin(), val2.begin(), initiator.AgreedValueLength()))
{
std::cout << "FAILED authenticated agreed values not equal" << std::endl;
return false;
}
std::cout << "passed authenticated key agreement shared secret" << std::endl;
return true;
}
bool SignatureValidate(PK_Signer &priv, PK_Verifier &pub, bool thorough)
{
bool pass = true, fail;
fail = !pub.GetMaterial().Validate(GlobalRNG(), thorough ? 3 : 2) || !priv.GetMaterial().Validate(GlobalRNG(), thorough ? 3 : 2);
pass = pass && !fail;
std::cout << (fail ? "FAILED " : "passed ");
std::cout << "signature key validation\n";
const byte message[] = "test message";
const int messageLen = 12;
SecByteBlock signature(priv.MaxSignatureLength());
size_t signatureLength = priv.SignMessage(GlobalRNG(), message, messageLen, signature);
fail = !pub.VerifyMessage(message, messageLen, signature, signatureLength);
pass = pass && !fail;
std::cout << (fail ? "FAILED " : "passed ");
std::cout << "signature and verification\n";
++signature[0];
fail = pub.VerifyMessage(message, messageLen, signature, signatureLength);
pass = pass && !fail;
std::cout << (fail ? "FAILED " : "passed ");
std::cout << "checking invalid signature" << std::endl;
if (priv.MaxRecoverableLength() > 0)
{
signatureLength = priv.SignMessageWithRecovery(GlobalRNG(), message, messageLen, NULLPTR, 0, signature);
SecByteBlock recovered(priv.MaxRecoverableLengthFromSignatureLength(signatureLength));
DecodingResult result = pub.RecoverMessage(recovered, NULLPTR, 0, signature, signatureLength);
fail = !(result.isValidCoding && result.messageLength == messageLen && memcmp(recovered, message, messageLen) == 0);
pass = pass && !fail;
std::cout << (fail ? "FAILED " : "passed ");
std::cout << "signature and verification with recovery" << std::endl;
++signature[0];
result = pub.RecoverMessage(recovered, NULLPTR, 0, signature, signatureLength);
fail = result.isValidCoding;
pass = pass && !fail;
std::cout << (fail ? "FAILED " : "passed ");
std::cout << "recovery with invalid signature" << std::endl;
}
return pass;
}
bool ValidateBBS()
{
std::cout << "\nBlumBlumShub validation suite running...\n\n";
Integer p("212004934506826557583707108431463840565872545889679278744389317666981496005411448865750399674653351");
Integer q("100677295735404212434355574418077394581488455772477016953458064183204108039226017738610663984508231");
Integer seed("63239752671357255800299643604761065219897634268887145610573595874544114193025997412441121667211431");
BlumBlumShub bbs(p, q, seed);
bool pass = true, fail;
int j;
const byte output1[] = {
0x49,0xEA,0x2C,0xFD,0xB0,0x10,0x64,0xA0,0xBB,0xB9,
0x2A,0xF1,0x01,0xDA,0xC1,0x8A,0x94,0xF7,0xB7,0xCE};
const byte output2[] = {
0x74,0x45,0x48,0xAE,0xAC,0xB7,0x0E,0xDF,0xAF,0xD7,
0xD5,0x0E,0x8E,0x29,0x83,0x75,0x6B,0x27,0x46,0xA1};
byte buf[20];
std::ostringstream oss;
bbs.GenerateBlock(buf, 20);
fail = memcmp(output1, buf, 20) != 0;
pass = pass && !fail;
oss << (fail ? "FAILED " : "passed ");
for (j=0;j<20;j++)
oss << std::setw(2) << std::setfill('0') << std::hex << (int)buf[j];
oss << std::endl;
bbs.Seek(10);
bbs.GenerateBlock(buf, 10);
fail = memcmp(output1+10, buf, 10) != 0;
pass = pass && !fail;
oss << (fail ? "FAILED " : "passed ");
for (j=0;j<10;j++)
oss << std::setw(2) << std::setfill('0') << std::hex << (int)buf[j];
oss << std::endl;
bbs.Seek(1234567);
bbs.GenerateBlock(buf, 20);
fail = memcmp(output2, buf, 20) != 0;
pass = pass && !fail;
oss << (fail ? "FAILED " : "passed ");
for (j=0;j<20;j++)
oss << std::setw(2) << std::setfill('0') << std::hex << (int)buf[j];
oss << std::endl;
std::cout << oss.str();
return pass;
}
bool ValidateECP()
{
// Remove word recommend. Some ECP curves may not be recommended depending
// on whom you ask. ECP is more descriptive item in this case.
std::cout << "\nTesting SEC 2, NIST and Brainpool ECP curves...\n\n";
bool pass = true; OID oid;
while (!(oid = DL_GroupParameters_EC<ECP>::GetNextRecommendedParametersOID(oid)).GetValues().empty())
{
DL_GroupParameters_EC<ECP> params(oid);
bool fail = !params.Validate(GlobalRNG(), 2);
std::cout << (fail ? "FAILED" : "passed") << " " << std::dec << params.GetCurve().GetField().MaxElementBitLength() << " bits\n";
pass = pass && !fail;
}
std::cout << "\nECP validation suite running...\n\n";
return ValidateECP_Agreement() && ValidateECP_Encrypt() && ValidateECP_NULLDigest_Encrypt() && ValidateECP_Sign() && pass;
}
bool ValidateEC2N()
{
// Remove word recommend. Binary curves may not be recommended depending
// on whom you ask. EC2N is more descriptive item in this case.
std::cout << "\nTesting SEC 2 EC2N curves...\n\n";
bool pass = true; OID oid;
#if 1 // TODO: turn this back on when I make EC2N faster for pentanomial basis
while (!(oid = DL_GroupParameters_EC<EC2N>::GetNextRecommendedParametersOID(oid)).GetValues().empty())
{
DL_GroupParameters_EC<EC2N> params(oid);
bool fail = !params.Validate(GlobalRNG(), 2);
std::cout << (fail ? "FAILED" : "passed") << " " << params.GetCurve().GetField().MaxElementBitLength() << " bits\n";
pass = pass && !fail;
}
#endif
std::cout << "\nEC2N validation suite running...\n\n";
return ValidateEC2N_Agreement() && ValidateEC2N_Encrypt() && ValidateEC2N_Sign() && pass;
}
bool ValidateRSA()
{
std::cout << "\nRSA validation suite running...\n\n";
return ValidateRSA_Encrypt() && ValidateRSA_Sign();
}
bool ValidateLUC()
{
std::cout << "\nLUC validation suite running...\n\n";
return ValidateLUC_Encrypt() && ValidateLUC_Sign();
}
bool ValidateLUC_DL()
{
// Prologue printed in each function
return ValidateLUC_DL_Encrypt() && ValidateLUC_DL_Sign();
}
bool ValidateRabin()
{
std::cout << "\nRabin validation suite running...\n\n";
return ValidateRabin_Encrypt() && ValidateRabin_Sign();
}
NAMESPACE_END // Test
NAMESPACE_END // CryptoPP