// // 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. // /////////////////////////////////////////////////////////////////////////////// // keyfile.cpp #include "stdtwcrypto.h" #include "keyfile.h" #include "crypto.h" #include "core/archive.h" #include "core/debug.h" #include "core/fileheader.h" #include "core/serializerimpl.h" // A magic number to identify key files // I just picked this at random (I took the current time (14:34) squared it, // converted it to hex and xor'ed with 0xffffffff, just in case you are wondering) - dmb const uint32 KEY_MAGIC_NUMBER = 0xffe09f5b; static const uint32 CURRENT_FIXED_VERSION = 0x02020000; static const uint32 TW_21_VERSION = 0x02010000; /////////////////////////////////////////////////////////////////////////////// // class cKeyFile cKeyFile::cKeyFile() { mpPrivateKeyMem = 0; mPrivateKeyMemLen = 0; mpPrivateKey = 0; mpPublicKey = 0; mPrivateKeyUseCount = 0; } cKeyFile::~cKeyFile() { ReleaseMem(); } void cKeyFile::ReleaseMem() { //ASSERT(mPrivateKeyUseCount == 0); // though not an error, you should have called RealeasePrivateKey delete [] mpPrivateKeyMem; delete mpPrivateKey; delete mpPublicKey; mpPrivateKeyMem = 0; mPrivateKeyMemLen = 0; mpPrivateKey = 0; mpPublicKey = 0; mPrivateKeyUseCount = 0; } /////////////////////////////////////////////////////////////////////////////// // GetFileHeaderID() /////////////////////////////////////////////////////////////////////////////// struct cKeyFileFHID { cFileHeaderID* KeyFileID; cKeyFileFHID() { KeyFileID = 0; } ~cKeyFileFHID() { delete KeyFileID; } } gKeyFileFHID; const cFileHeaderID& cKeyFile::GetFileHeaderID() { if (gKeyFileFHID.KeyFileID == 0) gKeyFileFHID.KeyFileID = new cFileHeaderID(_T("cKeyFile")); // sanity check ASSERT(*gKeyFileFHID.KeyFileID == cFileHeaderID(_T("cKeyFile"))); return *gKeyFileFHID.KeyFileID; } bool cKeyFile::KeysLoaded() const { ASSERT(mpPrivateKeyMem == 0 || mpPublicKey != 0); return (mpPrivateKeyMem != 0); } // Read and write keys to a keyfile void cKeyFile::ReadFile(const TCHAR* filename) // throw eKeyFile() { ReleaseMem(); int16 len; try { cFileArchive inFile; cFileHeader fileHeader; inFile.OpenRead(filename); try { cSerializerImpl fhSer(inFile, cSerializerImpl::S_READ, filename); fileHeader.Read(&fhSer); } catch (eError&) { throw eKeyFileInvalidFmt(); } // check id and version if (fileHeader.GetID() != cKeyFile::GetFileHeaderID()) { ASSERT(false); throw eKeyFileInvalidFmt(); } // NOTE:mdb 20 July 99 -- I changed this so that we can load both tw22 and // tw21 key files, since their formatting is exactly the same besides // the version number. // if( (fileHeader.GetVersion() != CURRENT_FIXED_VERSION) && (fileHeader.GetVersion() != TW_21_VERSION) ) { ASSERT(false); throw eKeyFileInvalidFmt(); } // read public key inFile.ReadInt16(len); if (len <= 0 || len >= 9000) { ASSERT(false); throw eKeyFileInvalidFmt(); } int8* publicMem = new int8[len]; if (inFile.ReadBlob(publicMem, len) != len) { ASSERT(false); delete [] publicMem; throw eKeyFileInvalidFmt(); } mpPublicKey = new cElGamalSigPublicKey(publicMem); delete [] publicMem; // read private key; inFile.ReadInt16(len); if (len <= 0 || len > 9000) { ASSERT(false); delete mpPublicKey; mpPublicKey = 0; throw eKeyFileInvalidFmt(); } mPrivateKeyMemLen = len; mpPrivateKeyMem = new int8[len]; if (inFile.ReadBlob(mpPrivateKeyMem, mPrivateKeyMemLen) < mPrivateKeyMemLen) { ASSERT(false); delete mpPublicKey; delete [] mpPrivateKeyMem; mpPublicKey = 0; mpPrivateKeyMem = 0; mPrivateKeyMemLen = 0; throw eKeyFileInvalidFmt(); } } catch(eArchive&) { delete mpPublicKey; delete [] mpPrivateKeyMem; mpPublicKey = 0; mpPrivateKeyMem = 0; mPrivateKeyMemLen = 0; throw eKeyFileArchive(filename); } catch(eKeyFile&) { throw; } } void cKeyFile::WriteFile(const TCHAR* filename) const // throw eKeyFile() { ASSERT(KeysLoaded()); try { cFileArchive outFile; cFileHeader fileHeader; outFile.OpenReadWrite(filename); fileHeader.SetID(cKeyFile::GetFileHeaderID()); // Set file version. // If we in the future we wish to support reading keys different versions, // we will have to move this fileHeader.SetVersion(CURRENT_FIXED_VERSION); fileHeader.SetEncoding(cFileHeader::NO_ENCODING); { cSerializerImpl fhSer(outFile, cSerializerImpl::S_WRITE, filename); fileHeader.Write(&fhSer); } // save public key int16 len = mpPublicKey->GetWriteLen(); int8* publicMem = new int8[len]; mpPublicKey->Write(publicMem); outFile.WriteInt16(len); outFile.WriteBlob(publicMem, len); delete [] publicMem; // save private key len = mPrivateKeyMemLen; outFile.WriteInt16(len); outFile.WriteBlob(mpPrivateKeyMem, mPrivateKeyMemLen); } catch(eArchive&) { throw eKeyFileArchive(filename); } } // Functions to read and write the key to memory. GetWriteLen() will throw an // exception if keys are not currently loaded. void cKeyFile::ReadMem(const int8* pMem) // throw eKeyFile() { int16 i16; int32 i32; ReleaseMem(); memcpy(&i32, pMem, sizeof(i32)); if ((unsigned int)tw_ntohl(i32) != KEY_MAGIC_NUMBER) throw eKeyFileInvalidFmt(); pMem += sizeof(int32); memcpy(&i32, pMem, sizeof(i32)); if (tw_ntohl(i32) != 1) // version check throw eKeyFileInvalidFmt(); pMem += sizeof(int32); memcpy(&i16, pMem, sizeof(i16)); int16 len = tw_ntohs(i16); if (len <= 0 || len > 9000) { ASSERT(false); throw eKeyFileInvalidFmt(); } mpPublicKey = new cElGamalSigPublicKey((void*)(pMem + sizeof(int16))); pMem += sizeof(int16) + len; memcpy(&i16, pMem, sizeof(i16)); mPrivateKeyMemLen = tw_ntohs(i16); if (mPrivateKeyMemLen <= 0 || mPrivateKeyMemLen > 9000) { ASSERT(false); delete mpPublicKey; mpPublicKey = 0; throw eKeyFileInvalidFmt(); } mpPrivateKeyMem = new int8[mPrivateKeyMemLen]; memcpy(mpPrivateKeyMem, pMem + sizeof(int16), mPrivateKeyMemLen); } void cKeyFile::WriteMem(int8* pMem) const // throw eKeyFile() { int16 i16; int32 i32; if (!KeysLoaded()) { ASSERT(false); throw eKeyFileUninitialized(_T("cKeyFile not initialized")); } // magic number and version i32 = tw_htonl(KEY_MAGIC_NUMBER); memcpy(pMem, &i32, sizeof(i32)); pMem += sizeof(int32); i32 = tw_htonl(1); memcpy(pMem, &i32, sizeof(i32)); pMem += sizeof(int32); // save public key int16 len = mpPublicKey->GetWriteLen(); i16 = tw_htons(len); memcpy(pMem, &i16, sizeof(i16)); pMem += sizeof(int16); mpPublicKey->Write(pMem); pMem += len; // save private key len = mPrivateKeyMemLen; i16 = tw_htons(len); memcpy(pMem, &i16, sizeof(i16)); pMem += sizeof(int16); memcpy(pMem, mpPrivateKeyMem, mPrivateKeyMemLen); } int cKeyFile::GetWriteLen() // throw eKeyFile() { if (!KeysLoaded()) { ASSERT(false); throw eKeyFileUninitialized(_T("cKeyFile not initialized")); } return sizeof(int32) + // the magic number sizeof(int32) + // version sizeof(int16) + // sizeof public key mpPublicKey->GetWriteLen() + // the public key sizeof(int16) + // sizeof private key mPrivateKeyMemLen; // the private key } void cKeyFile::ProtectKeys(int8* passphrase, int passphraseLen) // throw eKeyFile() { int i; int16 i16; // generate the hash value of the private key int len = mpPrivateKey->GetWriteLen(); int8* privateKeyBits = new int8[len]; mpPrivateKey->Write(privateKeyBits); cHashedKey128 privateHash(privateKeyBits, len); RandomizeBytes(privateKeyBits, len); delete [] privateKeyBits; // generate cipher for encrypting private key cHashedKey192 desKey(passphrase, passphraseLen); cTripleDES des; des.SetKey(iCipher::ENCRYPT, desKey); ASSERT(des.GetBlockSizeCipher() == des.GetBlockSizePlain()); // calculate size of the memory image of the private key mPrivateKeyMemLen = cHashedKey128::GetWriteLen() + sizeof(int16) + mpPrivateKey->GetWriteLen(); mPrivateKeyMemLen = (mPrivateKeyMemLen / des.GetBlockSizePlain() + 1) * des.GetBlockSizePlain(); int sluff = mPrivateKeyMemLen - cHashedKey128::GetWriteLen() - sizeof(int16) - mpPrivateKey->GetWriteLen(); // write the hash of the private key, the size of the private key, and the private key, // all as plaintext. mpPrivateKeyMem = new int8[mPrivateKeyMemLen]; privateHash.Write(mpPrivateKeyMem); i16 = tw_htons(mpPrivateKey->GetWriteLen()); memcpy(mpPrivateKeyMem + cHashedKey128::GetWriteLen(), &i16, sizeof(i16)); mpPrivateKey->Write(mpPrivateKeyMem + cHashedKey128::GetWriteLen() + sizeof(int16)); RandomizeBytes(mpPrivateKeyMem + mPrivateKeyMemLen - sluff, sluff); // delete the private key delete mpPrivateKey; mpPrivateKey = 0; // encrypt the plaintext for (i = 0; i < mPrivateKeyMemLen; i += des.GetBlockSizePlain()) { des.ProcessBlock(mpPrivateKeyMem + i, mpPrivateKeyMem + i); } #ifdef _DEBUG // try decoding it just to see if it worked if (GetPrivateKey(passphrase, passphraseLen) == 0) { throw eKeyFileInvalidFmt(); } ReleasePrivateKey(); #endif RandomizeBytes(passphrase, passphraseLen); } // Generate new keys void cKeyFile::GenerateKeys(int keySize, int8* passphrase, int passphraseLen) // throw eKeyFile() { ReleaseMem(); cElGamalSig rsa((cElGamalSig::KeySize)keySize); // generate both keys rsa.GenerateKeys(mpPrivateKey, mpPublicKey); ProtectKeys(passphrase, passphraseLen); } void cKeyFile::ChangePassphrase(int8* passphraseOld, int passphraseOldLen, int8* passphrase, int passphraseLen) // throw eKeyFile() { if (GetPrivateKey(passphraseOld, passphraseOldLen) == 0) { throw eKeyFileInvalidFmt(); } ProtectKeys(passphrase, passphraseLen); } // Access to the private key. Key is normally stored encrypted for safety. Call // ReleasePrivateKey() to destory the plaintext version of the key as soon as you // are done using the key. const cElGamalSigPrivateKey* cKeyFile::GetPrivateKey(int8* passphrase, int passphraseLen) { int16 i16; if (!KeysLoaded()) { ASSERT(false); throw eKeyFileUninitialized(_T("cKeyFile not initialized")); } ASSERT(mPrivateKeyUseCount >= 0); if (mPrivateKeyUseCount == 0) { // generate cipher for decrypting private key cHashedKey192 desKey(passphrase, passphraseLen); cTripleDES des; des.SetKey(iCipher::DECRYPT, desKey); ASSERT(des.GetBlockSizeCipher() == des.GetBlockSizePlain()); // get a copy of the ciphertext and decrypt it int8* plainPrivateKeyMem = new int8[mPrivateKeyMemLen]; memcpy(plainPrivateKeyMem, mpPrivateKeyMem, mPrivateKeyMemLen); ASSERT(mPrivateKeyMemLen % des.GetBlockSizePlain() == 0); int i; for (i = 0; i < mPrivateKeyMemLen; i += des.GetBlockSizePlain()) { des.ProcessBlock(plainPrivateKeyMem + i, plainPrivateKeyMem + i); } // calculate the hash on the private key memcpy(&i16, plainPrivateKeyMem + cHashedKey128::GetWriteLen(), sizeof(i16)); int len = tw_ntohs(i16); if (len + cHashedKey128::GetWriteLen() + sizeof(int16) > (unsigned int)mPrivateKeyMemLen) { RandomizeBytes(plainPrivateKeyMem, mPrivateKeyMemLen); delete [] plainPrivateKeyMem; RandomizeBytes(passphrase, passphraseLen); return 0; } cHashedKey128 privateHash(plainPrivateKeyMem + cHashedKey128::GetWriteLen() + sizeof(int16), len); // compare to the stored value if (memcmp(plainPrivateKeyMem, privateHash.GetKey(), cHashedKey128::GetWriteLen()) != 0) { // passphrase didn't do the job RandomizeBytes(plainPrivateKeyMem, mPrivateKeyMemLen); delete [] plainPrivateKeyMem; RandomizeBytes(passphrase, passphraseLen); return 0; } mpPrivateKey = new cElGamalSigPrivateKey(plainPrivateKeyMem + cHashedKey128::GetWriteLen() + sizeof(int16)); RandomizeBytes(plainPrivateKeyMem, mPrivateKeyMemLen); delete [] plainPrivateKeyMem; RandomizeBytes(passphrase, passphraseLen); } else { // TODO: make sure the passphrase is correct } ++mPrivateKeyUseCount; return mpPrivateKey; } void cKeyFile::ReleasePrivateKey() // throw eKeyFile() { if (!KeysLoaded()) { ASSERT(false); throw eKeyFileUninitialized(_T("cKeyFile not initialized")); } ASSERT(mPrivateKeyUseCount > 0); if (--mPrivateKeyUseCount == 0) { delete mpPrivateKey; mpPrivateKey = 0; } } const cElGamalSigPublicKey* cKeyFile::GetPublicKey() const // throw eKeyFile() { if (!KeysLoaded()) { ASSERT(false); throw eKeyFileUninitialized(_T("cKeyFile not initialized")); } return mpPublicKey; } /////////////////////////////////////////////////////////////////////////////// // class cPrivateKeyProxy bool cPrivateKeyProxy::AquireKey(cKeyFile& keyFile, int8* passphrase, int passphraseLen) { if (mpKey != 0) { ASSERT(mpKeyFile != 0); mpKeyFile->ReleasePrivateKey(); } mpKeyFile = &keyFile; mpKey = keyFile.GetPrivateKey(passphrase, passphraseLen); return mpKey != 0; } cPrivateKeyProxy::~cPrivateKeyProxy() { if (mpKey != 0) { ASSERT(mpKeyFile != 0); mpKeyFile->ReleasePrivateKey(); } } const cElGamalSigPrivateKey* cPrivateKeyProxy::GetKey() const // throw eKeyFile() { if (mpKey == 0) { // This is a programming error ASSERT(false); throw eKeyFileUninitialized(_T("Private Key not initialized")); } return mpKey; }