tripwire-open-source/src/twcrypto/crypto.cpp

1272 lines
36 KiB
C++

//
// The developer of the original code and/or files is Tripwire, Inc.
// Portions created by Tripwire, Inc. are copyright (C) 2000 Tripwire,
// Inc. Tripwire is a registered trademark of Tripwire, Inc. All rights
// reserved.
//
// This program is free software. The contents of this file are subject
// to the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 2 of the License, or (at your
// option) any later version. You may redistribute it and/or modify it
// only in compliance with the GNU General Public License.
//
// This program is distributed in the hope that it will be useful.
// However, this program is distributed AS-IS WITHOUT ANY
// WARRANTY; INCLUDING THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS
// FOR A PARTICULAR PURPOSE. Please see the GNU General Public License
// for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
// USA.
//
// Nothing in the GNU General Public License or any other license to use
// the code or files shall permit you to use Tripwire's trademarks,
// service marks, or other intellectual property without Tripwire's
// prior written consent.
//
// If you have any questions, please contact Tripwire, Inc. at either
// info@tripwire.org or www.tripwire.org.
//
///////////////////////////////////////////////////////////////////////////////
// crypto.cpp -- Tripwire crypto implementation
//
#include "stdtwcrypto.h"
#include "crypto.h"
#include "core/errorgeneral.h"
#include "time.h"
#include "core/archive.h"
#include "cryptlib/sha.h"
#include "cryptlib/rng.h"
#include "cryptlib/des.h"
#include "cryptlib/integer.h"
#include "cryptlib/elgamal.h"
#include "cryptlib/des.h"
#ifdef _IDEA_ENCRYPTION
#include "cryptlib/idea.h"
#endif
#ifdef _RSA_ENCRYPTION
#include "cryptlib/rsa.h"
#endif
const uint32 EL_GAMAL_SIG_PUBLIC_MAGIC_NUM = 0x7ae2c945;
const uint32 EL_GAMAL_SIG_PRIVATE_MAGIC_NUM = 0x0d0ffa12;
///////////////////////////////////////////////////////////////////////////////
// macros for reading and writing integers
#define WRITE_INTEGER(I) \
len = I.MinEncodedSize(Integer::UNSIGNED); \
ASSERT(len >=0 && len < 9000); \
i32 = tw_htonl(len); \
memcpy(pOut, &i32, sizeof(i32)); \
pOut += sizeof(int32); \
I.Encode(pOut, len, Integer::UNSIGNED); \
pOut += len;
#define READ_INTEGER(I) \
memcpy(&i32, pIn, sizeof(i32)); \
len = tw_ntohl(i32); \
ASSERT(len >= 0 && len < 9000); \
pIn += sizeof(int32); \
I.Decode(pIn, len, Integer::UNSIGNED); \
pIn += len;
///////////////////////////////////////////////////////////////////////////////
// class cNullCipher -- no encryption cipher
int cNullCipher::GetBlockSizePlain()
{
// lets use the blocksize of TripleDES_Encryption since that may be
// better than picking a blocksize at random
return TripleDES_Encryption::BLOCKSIZE;
}
// return the size of data blocks for plaintext and cipertext
int cNullCipher::GetBlockSizeCipher()
{
return TripleDES_Encryption::BLOCKSIZE;
}
// process a block of data. indata and outdata may be the same memory
void cNullCipher::ProcessBlock(const void* indata, void* outdata)
{
memmove(outdata, indata, TripleDES_Encryption::BLOCKSIZE);
}
//////////////////////////////////////////////////////////////////////////////
// class cIDEA
//////////////////////////////////////////////////////////////////////////////
#ifdef _IDEA_ENCRYPTION
class cIDEA_i
{
public:
IDEA* mpIDEA;
};
cIDEA::cIDEA()
{
mpData = new cIDEA_i;
mpData->mpIDEA = 0;
}
cIDEA::~cIDEA()
{
delete mpData->mpIDEA;
delete mpData;
mpData = 0;
}
void cIDEA::SetKey(iCipher::EncryptionDir dir, const cHashedKey128& key)
{
// some things we are assuming
ASSERT(IDEA::KEYLENGTH == 16);
ASSERT(mpData);
delete mpData->mpIDEA;
mpData->mpIDEA = new IDEA((byte*)key.GetKey(), dir == iCipher::ENCRYPT ? ENCRYPTION : DECRYPTION);
}
// return the size of data block this crypter works on
int cIDEA::GetBlockSizePlain()
{
return IDEA::BLOCKSIZE;
}
int cIDEA::GetBlockSizeCipher()
{
return IDEA::BLOCKSIZE;
}
// process a block of data. indata and outdata may be the same memory
void cIDEA::ProcessBlock(const void* indata, void* outdata)
{
ASSERT(mpData->mpIDEA);
if (!mpData->mpIDEA)
{
ThrowAndAssert(eInternal(_T("Key not set in symmetric encryption.")));
}
mpData->mpIDEA->ProcessBlock((byte*)indata, (byte*)outdata);
}
#endif // _IDEA_ENCRYPTION
//////////////////////////////////////////////////////////////////////////////
// class cTripleDES
//////////////////////////////////////////////////////////////////////////////
class cTripleDES_i
{
public:
TripleDES_Encryption* mpEncryptor;
TripleDES_Decryption* mpDecryptor;
};
cTripleDES::cTripleDES()
{
mpData = new cTripleDES_i;
mpData->mpEncryptor = 0;
mpData->mpDecryptor = 0;
}
cTripleDES::~cTripleDES()
{
delete mpData->mpEncryptor;
delete mpData->mpDecryptor;
delete mpData;
mpData = 0;
}
void cTripleDES::SetKey(iCipher::EncryptionDir dir, const cHashedKey192& key)
{
// some things we are assuming
ASSERT(TripleDES_Encryption::KEYLENGTH == 24);
ASSERT(cHashedKey192::GetWriteLen() == 24);
ASSERT(mpData != 0);
if (dir == iCipher::ENCRYPT)
{
delete mpData->mpEncryptor;
delete mpData->mpDecryptor;
mpData->mpDecryptor = 0;
mpData->mpEncryptor = new TripleDES_Encryption((byte*)key.GetKey());
}
else
{
delete mpData->mpEncryptor;
delete mpData->mpDecryptor;
mpData->mpEncryptor = 0;
mpData->mpDecryptor = new TripleDES_Decryption((byte*)key.GetKey());
}
}
// return the size of data block this crypter works on
int cTripleDES::GetBlockSizePlain()
{
ASSERT(TripleDES_Encryption::BLOCKSIZE == TripleDES_Decryption::BLOCKSIZE);
return TripleDES_Encryption::BLOCKSIZE;
}
int cTripleDES::GetBlockSizeCipher()
{
ASSERT(TripleDES_Encryption::BLOCKSIZE == TripleDES_Decryption::BLOCKSIZE);
return TripleDES_Encryption::BLOCKSIZE;
}
// process a block of data. indata and outdata may be the same memory
void cTripleDES::ProcessBlock(const void* indata, void* outdata)
{
ASSERT(mpData != 0);
ASSERT(mpData->mpEncryptor || mpData->mpDecryptor);
if (!mpData->mpEncryptor && !mpData->mpDecryptor)
{
ThrowAndAssert(INTERNAL_ERROR("crypto.cpp")); //cTWError::E_INTERNAL, TSTRING(_T("Key not set in symmetric encryption."))));
}
if (mpData->mpEncryptor)
mpData->mpEncryptor->ProcessBlock((byte*)indata, (byte*)outdata);
else
mpData->mpDecryptor->ProcessBlock((byte*)indata, (byte*)outdata);
}
///////////////////////////////////////////////////////////////////////////////
// class cRSA
#ifdef _RSA_ENCRYPTION
// class cRSAPrivateKey
class cRSAPrivateKey_i
{
public:
int16 mKeyLength;
RSAPrivateKey* mpKey;
};
cRSAPrivateKey::cRSAPrivateKey()
{
mpData = new cRSAPrivateKey_i;
mpData->mpKey = 0;
mpData->mKeyLength = 0;
}
cRSAPrivateKey::cRSAPrivateKey(void* pDataStream)
{
mpData = new cRSAPrivateKey_i;
int32 len;
int32 i32;
int16 i16;
byte* pIn = (byte*)pDataStream;
memcpy(&i16, pIn, sizeof(i16));
mpData->mKeyLength = tw_ntohs(i16);
pIn += sizeof(int16);
Integer n, e, d, p, q, dp, dq, u;
READ_INTEGER(n);
READ_INTEGER(e);
READ_INTEGER(d);
READ_INTEGER(p);
READ_INTEGER(q);
READ_INTEGER(dp);
READ_INTEGER(dq);
READ_INTEGER(u);
mpData->mpKey = new RSAPrivateKey(n, e, d, p, q, dp, dq, u);
}
cRSAPrivateKey::~cRSAPrivateKey()
{
if (mpData)
{
delete mpData->mpKey;
delete mpData;
}
}
int cRSAPrivateKey::GetWriteLen() const
{
ASSERT(mpData->mpKey);
ASSERT(mpData->mpKey->GetTrapdoorFunction().GetModulus().IsPositive());
ASSERT(mpData->mpKey->GetTrapdoorFunction().GetExponent().IsPositive());
ASSERT(mpData->mpKey->GetTrapdoorFunction().GetPrime1().IsPositive());
ASSERT(mpData->mpKey->GetTrapdoorFunction().GetPrime2().IsPositive());
ASSERT(mpData->mpKey->GetTrapdoorFunction().GetDecryptionExponent().IsPositive());
ASSERT(mpData->mpKey->GetTrapdoorFunction().GetParameterDP().IsPositive());
ASSERT(mpData->mpKey->GetTrapdoorFunction().GetParameterDQ().IsPositive());
ASSERT(mpData->mpKey->GetTrapdoorFunction().GetParameterU().IsPositive());
int len = sizeof(int16) +
mpData->mpKey->GetTrapdoorFunction().GetModulus().MinEncodedSize(Integer::UNSIGNED) + sizeof(int32) +
mpData->mpKey->GetTrapdoorFunction().GetExponent().MinEncodedSize(Integer::UNSIGNED) + sizeof(int32) +
mpData->mpKey->GetTrapdoorFunction().GetPrime1().MinEncodedSize(Integer::UNSIGNED) + sizeof(int32) +
mpData->mpKey->GetTrapdoorFunction().GetPrime2().MinEncodedSize(Integer::UNSIGNED) + sizeof(int32) +
mpData->mpKey->GetTrapdoorFunction().GetDecryptionExponent().MinEncodedSize(Integer::UNSIGNED) + sizeof(int32) +
mpData->mpKey->GetTrapdoorFunction().GetParameterDP().MinEncodedSize(Integer::UNSIGNED) + sizeof(int32) +
mpData->mpKey->GetTrapdoorFunction().GetParameterDQ().MinEncodedSize(Integer::UNSIGNED) + sizeof(int32) +
mpData->mpKey->GetTrapdoorFunction().GetParameterU().MinEncodedSize(Integer::UNSIGNED) + sizeof(int32);
return len;
}
void cRSAPrivateKey::Write(void* pDataStream) const
{
ASSERT(mpData->mpKey);
ASSERT(mpData->mpKey->GetTrapdoorFunction().GetModulus().IsPositive());
ASSERT(mpData->mpKey->GetTrapdoorFunction().GetExponent().IsPositive());
ASSERT(mpData->mpKey->GetTrapdoorFunction().GetPrime1().IsPositive());
ASSERT(mpData->mpKey->GetTrapdoorFunction().GetPrime2().IsPositive());
ASSERT(mpData->mpKey->GetTrapdoorFunction().GetDecryptionExponent().IsPositive());
ASSERT(mpData->mpKey->GetTrapdoorFunction().GetParameterDP().IsPositive());
ASSERT(mpData->mpKey->GetTrapdoorFunction().GetParameterDQ().IsPositive());
ASSERT(mpData->mpKey->GetTrapdoorFunction().GetParameterU().IsPositive());
byte* pOut = (byte*)pDataStream;
int16 i16;
int32 i32;
i16 = tw_htons(mpData->mKeyLength);
memcpy(pOut, &i16, sizeof(i16));
pOut += sizeof(int16);
Integer n, e, d, p, q, dp, dq, u;
n = mpData->mpKey->GetTrapdoorFunction().GetModulus();
e = mpData->mpKey->GetTrapdoorFunction().GetExponent();
p = mpData->mpKey->GetTrapdoorFunction().GetPrime1();
q = mpData->mpKey->GetTrapdoorFunction().GetPrime2();
d = mpData->mpKey->GetTrapdoorFunction().GetDecryptionExponent();
dp = mpData->mpKey->GetTrapdoorFunction().GetParameterDP();
dq = mpData->mpKey->GetTrapdoorFunction().GetParameterDQ();
u = mpData->mpKey->GetTrapdoorFunction().GetParameterU();
int32 len;
#define WRITE_INTEGER(I) \
len = I.MinEncodedSize(Integer::UNSIGNED); \
i32 = tw_htonl(len); \
memcpy(pOut, &i32, sizeof(i32)); \
pOut += sizeof(int32); \
I.Encode(pOut, len, Integer::UNSIGNED); \
pOut += len;
WRITE_INTEGER(n);
WRITE_INTEGER(e);
WRITE_INTEGER(d);
WRITE_INTEGER(p);
WRITE_INTEGER(q);
WRITE_INTEGER(dp);
WRITE_INTEGER(dq);
WRITE_INTEGER(u);
}
// class cRSAPublicKey
class cRSAPublicKey_i
{
public:
int16 mKeyLength;
RSAPublicKey* mpKey;
};
cRSAPublicKey::cRSAPublicKey()
{
mpData = new cRSAPublicKey_i;
mpData->mpKey = 0;
mpData->mKeyLength = 0;
}
cRSAPublicKey::cRSAPublicKey(void* pDataStream)
{
mpData = new cRSAPublicKey_i;
Integer n, e;
int32 len;
int16 i16;
int32 i32;
byte* pIn = (byte*)pDataStream;
memcpy(&i16, pIn, sizeof(i16));
mpData->mKeyLength = tw_ntohs(i16);
pIn += sizeof(int16);
READ_INTEGER(n);
READ_INTEGER(e);
//std::cout << "cRSAPublicKey n = " << n << std::endl;
//std::cout << "cRSAPublicKey e = " << e << std::endl;
mpData->mpKey = new RSAPublicKey(n, e);
}
cRSAPublicKey::cRSAPublicKey(const cRSAPrivateKey& privateKey)
{
mpData = new cRSAPublicKey_i;
ASSERT(privateKey.mpData);
ASSERT(privateKey.mpData->mpKey);
mpData->mKeyLength = privateKey.mpData->mKeyLength;
mpData->mpKey = new RSAPublicKey(*privateKey.mpData->mpKey);
}
cRSAPublicKey::~cRSAPublicKey()
{
if (mpData)
{
delete mpData->mpKey;
delete mpData;
}
}
int cRSAPublicKey::GetWriteLen() const
{
ASSERT(mpData->mpKey);
ASSERT(mpData->mpKey->GetTrapdoorFunction().GetModulus().IsPositive());
ASSERT(mpData->mpKey->GetTrapdoorFunction().GetExponent().IsPositive());
int len = sizeof(int16) +
mpData->mpKey->GetTrapdoorFunction().GetModulus().MinEncodedSize(Integer::UNSIGNED) + sizeof(int32) +
mpData->mpKey->GetTrapdoorFunction().GetExponent().MinEncodedSize(Integer::UNSIGNED) + sizeof(int32);
return len;
}
void cRSAPublicKey::Write(void* pDataStream) const
{
ASSERT(mpData->mpKey);
ASSERT(mpData->mpKey->GetTrapdoorFunction().GetModulus().IsPositive());
ASSERT(mpData->mpKey->GetTrapdoorFunction().GetExponent().IsPositive());
int16 i16;
int32 i32;
byte* pOut = (byte*)pDataStream;
i16 = tw_htons(mpData->mKeyLength);
memcpy(pOut, &i16, sizeof(i16));
pOut += sizeof(int16);
Integer n, e;
n = mpData->mpKey->GetTrapdoorFunction().GetModulus();
e = mpData->mpKey->GetTrapdoorFunction().GetExponent();
int32 len;
WRITE_INTEGER(n);
WRITE_INTEGER(e);
}
#ifdef DEBUG
void cRSAPublicKey::TraceContents()
{
cDebug d("cRSAPublicKey::TraceContents");
{
std::ostringstream os;
os << mpData->mpKey->GetTrapdoorFunction().GetModulus();
d.TraceDebug("Modulus = %s\n", os.str().c_str());
}
{
std::ostringstream os;
os << mpData->mpKey->GetTrapdoorFunction().GetExponent();
d.TraceDebug("Exponent = %s\n", os.str().c_str());
}
}
#endif
// class cRSA
class cRSA_i
{
public:
int mKeyBits;
enum Action { ENCRYPT, DECRYPT, SIGN, VERIFY };
Action mAction;
const cRSAPrivateKey* mpPrivateKey;
const cRSAPublicKey* mpPublicKey;
X917RNG* mpRNG;
};
cRSA::cRSA(KeySize keysize)
{
Init(keysize);
}
cRSA::cRSA(const cRSAPublicKey& publicKey)
{
ASSERT(publicKey.mpData);
Init((KeySize)publicKey.mpData->mKeyLength);
}
cRSA::cRSA(const cRSAPrivateKey& privateKey)
{
ASSERT(privateKey.mpData);
Init((KeySize)privateKey.mpData->mKeyLength);
}
void cRSA::Init(KeySize keysize)
{
mpData = new cRSA_i;
mpData->mpPrivateKey = 0;
mpData->mpPublicKey = 0;
mpData->mKeyBits = (keysize == KEY256) ? 256 :
(keysize == KEY512) ? 512 :
(keysize == KEY1024) ? 1024 :
(keysize == KEY2048) ? 2048 :
256;
// Create a random seed and a key
byte seed[MD5::DATASIZE];
byte deskey[TripleDES_Encryption::KEYLENGTH];
RandomizeBytes((int8*)seed, MD5::DATASIZE);
RandomizeBytes((int8*)deskey, TripleDES_Encryption::KEYLENGTH);
mpData->mpRNG = new X917RNG(new TripleDES_Encryption(deskey), seed);
}
cRSA::~cRSA()
{
if (mpData)
{
delete mpData->mpRNG;
delete mpData;
mpData = 0;
}
}
void cRSA::SetEncrypting(const cRSAPublicKey* pKey)
{
ASSERT(pKey->mpData->mKeyLength == mpData->mKeyBits);
if (pKey->mpData->mKeyLength != mpData->mKeyBits)
ThrowAndAssert(eInternal(_T("RSA Key length mismatch.")));
mpData->mAction = cRSA_i::ENCRYPT;
mpData->mpPublicKey = pKey;
mpData->mpPrivateKey = 0;
}
void cRSA::SetDecrypting(const cRSAPrivateKey* pKey)
{
ASSERT(pKey->mpData->mKeyLength == mpData->mKeyBits);
if (pKey->mpData->mKeyLength != mpData->mKeyBits)
ThrowAndAssert(eInternal(_T("RSA Key length mismatch.")));
mpData->mAction = cRSA_i::DECRYPT;
mpData->mpPrivateKey = pKey;
mpData->mpPublicKey = 0;
}
void cRSA::SetSigning(const cRSAPrivateKey* pKey)
{
ASSERT(pKey->mpData->mKeyLength == mpData->mKeyBits);
if (pKey->mpData->mKeyLength != mpData->mKeyBits)
ThrowAndAssert(eInternal(_T("RSA Key length mismatch.")));
mpData->mAction = cRSA_i::SIGN;
mpData->mpPrivateKey = pKey;
mpData->mpPublicKey = 0;
}
void cRSA::SetVerifying(const cRSAPublicKey* pKey)
{
ASSERT(pKey->mpData->mKeyLength == mpData->mKeyBits);
if (pKey->mpData->mKeyLength != mpData->mKeyBits)
ThrowAndAssert(eInternal(_T("RSA Key length mismatch.")));
mpData->mAction = cRSA_i::VERIFY;
mpData->mpPublicKey = pKey;
mpData->mpPrivateKey = 0;
}
// Lets encrypt in 128 bit chunks, even though the crypto
// lib implementation can do arbitrary length plaintext
// encryptions. We will most likely only use this to
// encrypt a 128 bit random number anyway.
int cRSA::GetBlockSizePlain()
{
//Integer i = mpData->mKeyBits - 1;
return (mpData->mKeyBits >> 3) - 11;
}
int cRSA::GetBlockSizeCipher()
{
return mpData->mKeyBits >> 3;
}
void cRSA::ProcessBlock(const void* indata, void* outdata)
{
ASSERT(mpData);
ASSERT(mpData->mpPrivateKey != 0 || mpData->mpPublicKey != 0);
if (mpData->mpPrivateKey == 0 && mpData->mpPublicKey == 0)
ThrowAndAssert(eInternal(_T("RSA Key length mismatch.")));
unsigned int l;
switch (mpData->mAction)
{
case cRSA_i::ENCRYPT:
{
ASSERT(mpData->mpPublicKey);
ASSERT(mpData->mpPublicKey->mpData->mpKey->MaxPlainTextLength() == GetBlockSizePlain());
ASSERT(mpData->mpPublicKey->mpData->mpKey->CipherTextLength() == GetBlockSizeCipher());
mpData->mpPublicKey->mpData->mpKey->Encrypt(*mpData->mpRNG, (const byte *)indata, GetBlockSizePlain(), (byte *)outdata);
break;
}
case cRSA_i::DECRYPT:
{
ASSERT(mpData->mpPrivateKey);
ASSERT(mpData->mpPrivateKey->mpData->mpKey->CipherTextLength() == GetBlockSizeCipher());
l = mpData->mpPrivateKey->mpData->mpKey->Decrypt((const byte *)indata, (byte *)outdata);
if (l != GetBlockSizePlain())
throw eArchiveCrypto();
break;
}
case cRSA_i::SIGN:
{
ASSERT(mpData->mpPrivateKey);
ASSERT(mpData->mpPrivateKey->mpData->mpKey->MaxMessageLength() == GetBlockSizePlain());
ASSERT(mpData->mpPrivateKey->mpData->mpKey->SignatureLength() == GetBlockSizeCipher());
mpData->mpPrivateKey->mpData->mpKey->Sign(*mpData->mpRNG, (const byte *)indata, GetBlockSizePlain(), (byte *)outdata);
break;
}
case cRSA_i::VERIFY:
{
ASSERT(mpData->mpPublicKey);
ASSERT(mpData->mpPublicKey->mpData->mpKey->SignatureLength() == GetBlockSizeCipher());
l = mpData->mpPublicKey->mpData->mpKey->Recover((const byte *)indata, (byte *)outdata);
if (l != GetBlockSizePlain())
throw eArchiveCrypto();
break;
}
default:
{
ASSERT(false);
break;
}
}
}
void cRSA::GenerateKeys(cRSAPrivateKey*& retPrivate, cRSAPublicKey*& retPublic)
{
RSAPrivateKey* pNewPrivateKey = new RSAPrivateKey(*mpData->mpRNG, mpData->mKeyBits);
RSAPublicKey* pNewPublicKey = new RSAPublicKey(*pNewPrivateKey);
retPrivate = new cRSAPrivateKey();
retPrivate->mpData->mpKey = pNewPrivateKey;
retPrivate->mpData->mKeyLength = mpData->mKeyBits;
retPublic = new cRSAPublicKey();
retPublic->mpData->mpKey = pNewPublicKey;
retPublic->mpData->mKeyLength = mpData->mKeyBits;
#ifdef DEBUG
int l;
l = retPublic->mpData->mpKey->MaxPlainTextLength();
ASSERT(l == GetBlockSizePlain());
l = retPublic->mpData->mpKey->CipherTextLength();
ASSERT(l == GetBlockSizeCipher());
l = retPrivate->mpData->mpKey->CipherTextLength();
ASSERT(l == GetBlockSizeCipher());
#endif
}
#endif // _RSA_ENCRYPTION
///////////////////////////////////////////////////////////////////////////////
// class cElGamalSig
// class cElGamalSigPrivateKey
class cElGamalSigPrivateKey_i
{
public:
int16 mKeyLength;
ElGamalSigPrivateKey* mpKey;
};
cElGamalSigPrivateKey::cElGamalSigPrivateKey()
{
mpData = new cElGamalSigPrivateKey_i;
mpData->mpKey = 0;
mpData->mKeyLength = 0;
}
cElGamalSigPrivateKey::cElGamalSigPrivateKey(void* pDataStream)
{
mpData = new cElGamalSigPrivateKey_i;
int32 len;
int32 i32;
int16 i16;
uint32 magicNum;
byte* pIn = (byte*)pDataStream;
memcpy(&i16, pIn, sizeof(i16));
mpData->mKeyLength = tw_ntohs(i16);
pIn += sizeof(int16);
memcpy(&i32, pIn, sizeof(i32));
magicNum = tw_ntohl(i32);
pIn += sizeof(int32);
if (magicNum != EL_GAMAL_SIG_PRIVATE_MAGIC_NUM)
ThrowAndAssert( eArchiveOpen() );
Integer p, q, g, y, x;
READ_INTEGER(p);
READ_INTEGER(q);
READ_INTEGER(g);
READ_INTEGER(y);
READ_INTEGER(x);
mpData->mpKey = new ElGamalSigPrivateKey(p, q, g, y, x);
}
cElGamalSigPrivateKey::~cElGamalSigPrivateKey()
{
if (mpData)
{
delete mpData->mpKey;
delete mpData;
}
}
int cElGamalSigPrivateKey::GetWriteLen() const
{
ASSERT(mpData->mpKey != 0);
ASSERT(mpData->mpKey->GetPrime().IsPositive());
ASSERT(mpData->mpKey->GetParameterQ().IsPositive());
ASSERT(mpData->mpKey->GetParameterG().IsPositive());
ASSERT(mpData->mpKey->GetParameterY().IsPositive());
ASSERT(mpData->mpKey->GetParameterX().IsPositive());
int len = sizeof(int16) + sizeof(int32) +
mpData->mpKey->GetPrime().MinEncodedSize(Integer::UNSIGNED) + sizeof(int32) +
mpData->mpKey->GetParameterQ().MinEncodedSize(Integer::UNSIGNED) + sizeof(int32) +
mpData->mpKey->GetParameterG().MinEncodedSize(Integer::UNSIGNED) + sizeof(int32) +
mpData->mpKey->GetParameterY().MinEncodedSize(Integer::UNSIGNED) + sizeof(int32) +
mpData->mpKey->GetParameterX().MinEncodedSize(Integer::UNSIGNED) + sizeof(int32);
return len;
}
void cElGamalSigPrivateKey::Write(void* pDataStream) const
{
ASSERT(mpData->mpKey != 0);
ASSERT(mpData->mpKey->GetPrime().IsPositive());
ASSERT(mpData->mpKey->GetParameterQ().IsPositive());
ASSERT(mpData->mpKey->GetParameterG().IsPositive());
ASSERT(mpData->mpKey->GetParameterY().IsPositive());
ASSERT(mpData->mpKey->GetParameterX().IsPositive());
byte* pOut = (byte*)pDataStream;
int16 i16;
int32 i32;
i16 = tw_htons(mpData->mKeyLength);
memcpy(pOut, &i16, sizeof(i16));
pOut += sizeof(int16);
i32 = tw_htonl(EL_GAMAL_SIG_PRIVATE_MAGIC_NUM);
memcpy(pOut, &i32, sizeof(i32));
pOut += sizeof(int32);
Integer p, q, g, y, x;
p = mpData->mpKey->GetPrime();
q = mpData->mpKey->GetParameterQ();
g = mpData->mpKey->GetParameterG();
y = mpData->mpKey->GetParameterY();
x = mpData->mpKey->GetParameterX();
int32 len;
WRITE_INTEGER(p);
WRITE_INTEGER(q);
WRITE_INTEGER(g);
WRITE_INTEGER(y);
WRITE_INTEGER(x);
}
// class cElGamalSigPublicKey
class cElGamalSigPublicKey_i
{
public:
int16 mKeyLength;
ElGamalSigPublicKey* mpKey;
};
cElGamalSigPublicKey::cElGamalSigPublicKey()
{
mpData = new cElGamalSigPublicKey_i;
mpData->mpKey = 0;
mpData->mKeyLength = 0;
}
cElGamalSigPublicKey::cElGamalSigPublicKey(void* pDataStream)
{
mpData = new cElGamalSigPublicKey_i;
Integer p, q, g, y;
int32 len;
int16 i16;
int32 i32;
uint32 magicNum;
byte* pIn = (byte*)pDataStream;
memcpy(&i16, pIn, sizeof(i16));
mpData->mKeyLength = tw_ntohs(i16);
pIn += sizeof(int16);
memcpy(&i32, pIn, sizeof(i32));
magicNum = tw_ntohl(i32);
pIn += sizeof(int32);
if (magicNum != EL_GAMAL_SIG_PUBLIC_MAGIC_NUM)
ThrowAndAssert( eArchiveOpen() );
READ_INTEGER(p);
READ_INTEGER(q);
READ_INTEGER(g);
READ_INTEGER(y);
//std::cout << "cElGamalSigPublicKey n = " << n << std::endl;
//std::cout << "cElGamalSigPublicKey e = " << e << std::endl;
mpData->mpKey = new ElGamalSigPublicKey(p, q, g, y);
}
cElGamalSigPublicKey::cElGamalSigPublicKey(const cElGamalSigPrivateKey& privateKey)
{
mpData = new cElGamalSigPublicKey_i;
ASSERT(privateKey.mpData != 0);
ASSERT(privateKey.mpData->mpKey != 0);
mpData->mKeyLength = privateKey.mpData->mKeyLength;
mpData->mpKey = new ElGamalSigPublicKey(*privateKey.mpData->mpKey);
}
cElGamalSigPublicKey::~cElGamalSigPublicKey()
{
if (mpData)
{
delete mpData->mpKey;
delete mpData;
}
}
int cElGamalSigPublicKey::GetWriteLen() const
{
ASSERT(mpData->mpKey != 0);
ASSERT(mpData->mpKey->GetPrime().IsPositive());
ASSERT(mpData->mpKey->GetParameterQ().IsPositive());
ASSERT(mpData->mpKey->GetParameterG().IsPositive());
ASSERT(mpData->mpKey->GetParameterY().IsPositive());
int len = sizeof(int16) + sizeof(int32) +
mpData->mpKey->GetPrime().MinEncodedSize(Integer::UNSIGNED) + sizeof(int32) +
mpData->mpKey->GetParameterQ().MinEncodedSize(Integer::UNSIGNED) + sizeof(int32) +
mpData->mpKey->GetParameterG().MinEncodedSize(Integer::UNSIGNED) + sizeof(int32) +
mpData->mpKey->GetParameterY().MinEncodedSize(Integer::UNSIGNED) + sizeof(int32);
return len;
}
void cElGamalSigPublicKey::Write(void* pDataStream) const
{
ASSERT(mpData->mpKey != 0);
ASSERT(mpData->mpKey->GetPrime().IsPositive());
ASSERT(mpData->mpKey->GetParameterQ().IsPositive());
ASSERT(mpData->mpKey->GetParameterG().IsPositive());
ASSERT(mpData->mpKey->GetParameterY().IsPositive());
byte* pOut = (byte*)pDataStream;
int16 i16;
int32 i32;
i16 = tw_htons(mpData->mKeyLength);
memcpy(pOut, &i16, sizeof(i16));
pOut += sizeof(int16);
i32 = tw_htonl(EL_GAMAL_SIG_PUBLIC_MAGIC_NUM);
memcpy(pOut, &i32, sizeof(i32));
pOut += sizeof(int32);
Integer p, q, g, y;
p = mpData->mpKey->GetPrime();
q = mpData->mpKey->GetParameterQ();
g = mpData->mpKey->GetParameterG();
y = mpData->mpKey->GetParameterY();
int32 len;
WRITE_INTEGER(p);
WRITE_INTEGER(q);
WRITE_INTEGER(g);
WRITE_INTEGER(y);
}
bool cElGamalSigPublicKey::IsEqual(const cElGamalSigPublicKey& rhs) const
{
return this->mpData->mpKey->GetPrime() == rhs.mpData->mpKey->GetPrime() &&
this->mpData->mpKey->GetParameterQ() == rhs.mpData->mpKey->GetParameterQ() &&
this->mpData->mpKey->GetParameterG() == rhs.mpData->mpKey->GetParameterG() &&
this->mpData->mpKey->GetParameterY() == rhs.mpData->mpKey->GetParameterY()
;
}
#ifdef DEBUG
void cElGamalSigPublicKey::TraceContents()
{
cDebug d("cElGamalSigPublicKey::TraceContents");
{
std::ostringstream os;
os << mpData->mpKey->GetPrime();
d.TraceDebug("Prime = %s\n", os.str().c_str());
}
{
std::ostringstream os;
os << mpData->mpKey->GetParameterQ();
d.TraceDebug("Q = %s\n", os.str().c_str());
}
{
std::ostringstream os;
os << mpData->mpKey->GetParameterG();
d.TraceDebug("G = %s\n", os.str().c_str());
}
{
std::ostringstream os;
os << mpData->mpKey->GetParameterY();
d.TraceDebug("Y = %s\n", os.str().c_str());
}
}
#endif
// class cElGamalSig
class cElGamalSig_i
{
public:
int mKeyBits;
enum Action { /*ENCRYPT, DECRYPT,*/ SIGN, VERIFY };
Action mAction;
const cElGamalSigPrivateKey* mpPrivateKey;
const cElGamalSigPublicKey* mpPublicKey;
SHA mSHA;
X917RNG* mpRNG;
};
cElGamalSig::cElGamalSig(KeySize keysize)
{
Init(keysize);
}
cElGamalSig::cElGamalSig(const cElGamalSigPublicKey& publicKey)
{
ASSERT(publicKey.mpData != 0);
Init((KeySize)publicKey.mpData->mKeyLength);
}
cElGamalSig::cElGamalSig(const cElGamalSigPrivateKey& privateKey)
{
ASSERT(privateKey.mpData != 0);
Init((KeySize)privateKey.mpData->mKeyLength);
}
void cElGamalSig::Init(KeySize keysize)
{
mpData = new cElGamalSig_i;
mpData->mpPrivateKey = 0;
mpData->mpPublicKey = 0;
mpData->mKeyBits = (keysize == KEY256) ? 256 :
(keysize == KEY512) ? 512 :
(keysize == KEY1024) ? 1024 :
(keysize == KEY2048) ? 2048 :
256;
// Create a random seed and a key
byte seed[SHA::DATASIZE];
byte deskey[TripleDES_Encryption::KEYLENGTH];
RandomizeBytes((int8*)seed, SHA::DATASIZE);
RandomizeBytes((int8*)deskey, TripleDES_Encryption::KEYLENGTH);
mpData->mpRNG = new X917RNG(new TripleDES_Encryption(deskey), seed);
}
cElGamalSig::~cElGamalSig()
{
if (mpData)
{
delete mpData->mpRNG;
delete mpData;
mpData = 0;
}
}
void cElGamalSig::SetSigning(const cElGamalSigPrivateKey* pKey)
{
ASSERT(pKey->mpData->mKeyLength == mpData->mKeyBits);
if (pKey->mpData->mKeyLength != mpData->mKeyBits)
ThrowAndAssert(eInternal(_T("Signature Key length mismatch.")));
mpData->mAction = cElGamalSig_i::SIGN;
mpData->mpPrivateKey = pKey;
mpData->mpPublicKey = 0;
}
void cElGamalSig::SetVerifying(const cElGamalSigPublicKey* pKey)
{
ASSERT(pKey->mpData->mKeyLength == mpData->mKeyBits);
if (pKey->mpData->mKeyLength != mpData->mKeyBits)
ThrowAndAssert(eInternal(_T("Signature Key length mismatch.")));
mpData->mAction = cElGamalSig_i::VERIFY;
mpData->mpPublicKey = pKey;
mpData->mpPrivateKey = 0;
}
int cElGamalSig::GetBlockSizePlain()
{
return PLAIN_BLOCK_SIZE;
}
int cElGamalSig::GetBlockSizeCipher()
{
return PLAIN_BLOCK_SIZE + (NumberTheory::DiscreteLogWorkFactor(mpData->mKeyBits) >> 1) + 4;
// got this from nbtheory.cpp in crypto++ lib
// El Gamal's sig size = 2 * 2 * (DiscreteLogWorkFactor() >> 3)
}
void cElGamalSig::ProcessBlock(const void* indata, void* outdata)
{
ASSERT(mpData != 0);
ASSERT(mpData->mpPrivateKey != 0 || mpData->mpPublicKey != 0);
int8 shaSig[SHA::DIGESTSIZE];
if (mpData->mpPrivateKey == 0 && mpData->mpPublicKey == 0)
ThrowAndAssert(eInternal(_T("Signature Key length mismatch.")));
switch (mpData->mAction)
{
case cElGamalSig_i::SIGN:
{
ASSERT(mpData->mpPrivateKey != 0);
ASSERT((int)mpData->mpPrivateKey->mpData->mpKey->SignatureLength() + PLAIN_BLOCK_SIZE <= GetBlockSizeCipher());
memmove(outdata, indata, PLAIN_BLOCK_SIZE);
mpData->mSHA.CalculateDigest((byte*)shaSig, (byte*)outdata, PLAIN_BLOCK_SIZE);
RandomizeBytes((int8 *)outdata + PLAIN_BLOCK_SIZE, GetBlockSizeCipher() - PLAIN_BLOCK_SIZE);
mpData->mpPrivateKey->mpData->mpKey->Sign(*mpData->mpRNG, (const byte *)shaSig, SHA::DIGESTSIZE, (byte *)outdata + PLAIN_BLOCK_SIZE);
/*
Integer m((const byte*)indata, PLAIN_BLOCK_SIZE);
std::cout << "Signing:\n";
std::cout << "M = " << m << std::endl;
const byte* signature = (const byte *)outdata + PLAIN_BLOCK_SIZE;
int qLen = mpData->mpPrivateKey->mpData->mpKey->q.ByteCount();
Integer rs(signature, qLen);
Integer ss(signature+qLen, qLen);
std::cout << "Stored R = " << rs << std::endl;
std::cout << "Stored S = " << ss << std::endl;
*/
break;
}
case cElGamalSig_i::VERIFY:
{
ASSERT(mpData->mpPublicKey != 0);
ASSERT((int)mpData->mpPublicKey->mpData->mpKey->SignatureLength() + PLAIN_BLOCK_SIZE <= GetBlockSizeCipher());
mpData->mSHA.CalculateDigest((byte*)shaSig, (byte*)indata, PLAIN_BLOCK_SIZE);
/*
const byte* signature = (const byte *)indata + PLAIN_BLOCK_SIZE;
int qLen = mpData->mpPublicKey->mpData->mpKey->q.ByteCount();
Integer m((const byte*)indata, PLAIN_BLOCK_SIZE);
Integer r(signature, qLen);
Integer s(signature+qLen, qLen);
std::cout << "Verifying:\n";
std::cout << "M = " << m << std::endl;
std::cout << "R = " << r << std::endl;
std::cout << "S = " << s << std::endl;
*/
if (mpData->mpPublicKey->mpData->mpKey->Verify((const byte *)shaSig, SHA::DIGESTSIZE, (const byte *)indata + PLAIN_BLOCK_SIZE) == false)
throw eArchiveCrypto();
memmove(outdata, indata, PLAIN_BLOCK_SIZE);
break;
}
default:
{
ASSERT(false);
break;
}
}
}
void cElGamalSig::GenerateKeys(cElGamalSigPrivateKey*& retPrivate, cElGamalSigPublicKey*& retPublic)
{
ElGamalSigPrivateKey* pNewPrivateKey = new ElGamalSigPrivateKey(*mpData->mpRNG, mpData->mKeyBits);
ElGamalSigPublicKey* pNewPublicKey = new ElGamalSigPublicKey(*pNewPrivateKey);
retPrivate = new cElGamalSigPrivateKey();
retPrivate->mpData->mpKey = pNewPrivateKey;
retPrivate->mpData->mKeyLength = (int16)mpData->mKeyBits;
retPublic = new cElGamalSigPublicKey();
retPublic->mpData->mpKey = pNewPublicKey;
retPublic->mpData->mKeyLength = (int16)mpData->mKeyBits;
#ifdef DEBUG
int l;
l = retPublic->mpData->mpKey->SignatureLength();
ASSERT(l + PLAIN_BLOCK_SIZE <= GetBlockSizeCipher());
l = retPrivate->mpData->mpKey->SignatureLength();
ASSERT(l + PLAIN_BLOCK_SIZE <= GetBlockSizeCipher());
#endif
}
///////////////////////////////////////////////////////////////////////////////
// class cHashedKey128 -- A 128 bit key based on the hash value of some data
cHashedKey128::cHashedKey128(const TSTRING& data)
{
SHA sha;
ASSERT(SHA::DIGESTSIZE >= KEYLEN);
sha.CalculateDigest((byte*)mKey, (byte*)data.data(), data.length() * sizeof(TCHAR));
}
cHashedKey128::cHashedKey128(void* pData, int dataLen)
{
SHA sha;
ASSERT(SHA::DIGESTSIZE >= KEYLEN);
ASSERT(SHA::DIGESTSIZE <= BUFSIZE);
sha.CalculateDigest((byte*)mKey, (byte*)pData, dataLen);
}
cHashedKey128::~cHashedKey128()
{
RandomizeBytes(mKey, KEYLEN);
}
///////////////////////////////////////////////////////////////////////////////
// class cHashedKey192 -- A 192 bit key based on the hash value of some data
cHashedKey192::cHashedKey192(const TSTRING& data)
{
SHA sha;
ASSERT(SHA::DIGESTSIZE == 20);
ASSERT(40 >= KEYLEN);
byte localKey[40];
sha.CalculateDigest(localKey, (byte*)data.data(), data.length() * sizeof(TCHAR));
sha.CalculateDigest(localKey + 20, localKey, 20);
memcpy(mKey, localKey, KEYLEN);
}
cHashedKey192::cHashedKey192(void* pData, int dataLen)
{
SHA sha;
ASSERT(SHA::DIGESTSIZE == 20);
ASSERT(40 >= KEYLEN);
byte localKey[40];
sha.CalculateDigest(localKey, (byte*)pData, dataLen);
sha.CalculateDigest(localKey + 20, localKey, 20);
memcpy(mKey, localKey, KEYLEN);
}
cHashedKey192::~cHashedKey192()
{
RandomizeBytes(mKey, KEYLEN);
}
///////////////////////////////////////////////////////////////////////////////
// void RandomizeBytes(byte* destbuf, int len) -- Fill a buffer with random bytes
static bool gRandomizeBytesSeeded = false;
void RandomizeBytes(int8* destbuf, int len)
{
if (!gRandomizeBytesSeeded)
{
// generate a rancom number from processor timing.
// this should be fairly unpredictable.
time_t mask, start = time(NULL);
for (mask = 0xb147688c; time(NULL) - start < 1; mask += 0x8984cc88)
;
#ifdef DEBUG
time_t t = time(NULL);
t ^= mask;
//time_t t = 920492046 - 3600; // try to the same random values each time
//std::cerr << (*(time_t*)mask()) << ":" << t << std::endl;
srand( t );
#else
srand( time(NULL) ^ mask );
#endif
gRandomizeBytesSeeded = true;
}
int i;
for (i = 0; i < len; ++i)
destbuf[i] = (byte)( (rand() * 256 / RAND_MAX) ^ 0xdc ); // 0xdc came from random.org
}