mirror of
https://github.com/ValveSoftware/GameNetworkingSockets.git
synced 2026-05-29 16:20:34 +00:00
Add AES-GCM to crypto layer using OpenSSL EVP.
Also add a EVP_CIPHER_CTX_safe structure to manage allocating and cleaning up EVP_CIPHER_CTX struct. IN older versions of OpenSSL you could just declare an EVP_CIPHER_CTX struct on the stack. Now you have to go through a generic allocation mechanism!? SO we're going to be doing a heap allocation for every single packet. That is....terrible. We should try to find a way around this, even if it's gross, because doing a heap allocation per packet is not acceptible.
This commit is contained in:
+240
-15
@@ -26,6 +26,7 @@
|
||||
#include "tier0/memdbgoff.h"
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/evp.h>
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
#include "opensslwrapper.h"
|
||||
@@ -41,6 +42,30 @@ void OneTimeCryptoInitOpenSSL()
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate a EVP_CIPHER_CTX, and clean it up securely on scope exit
|
||||
// using RAII
|
||||
struct EVP_CIPHER_CTX_safe
|
||||
{
|
||||
// #if OPENSSL_API_LEVEL < 2
|
||||
//
|
||||
// // Nice, we can allocate on the stack, super simple and fast
|
||||
// EVP_CIPHER_CTX_safe() { EVP_CIPHER_CTX_init( &ctx ); }
|
||||
// ~EVP_CIPHER_CTX_safe() { EVP_CIPHER_CTX_cleanup( &ctx ); }
|
||||
// inline EVP_CIPHER_CTX *Ptr() { return &ctx; }
|
||||
// EVP_CIPHER_CTX ctx;
|
||||
// #else
|
||||
|
||||
// Ug, we have to go through a generic allocator! What the heck
|
||||
// guys, we need a way to do this efficiently! I don't want to be
|
||||
// doing heap allocations just to encrypt 1000 bytes! Do they
|
||||
// expect you to use a thread-local allocation and reuse it?
|
||||
EVP_CIPHER_CTX_safe() { ctx = EVP_CIPHER_CTX_new(); }
|
||||
~EVP_CIPHER_CTX_safe() { EVP_CIPHER_CTX_free( ctx ); }
|
||||
inline EVP_CIPHER_CTX *Ptr() { return ctx; }
|
||||
EVP_CIPHER_CTX *ctx;
|
||||
// #endif
|
||||
};
|
||||
|
||||
void CCrypto::Init()
|
||||
{
|
||||
OneTimeCryptoInitOpenSSL();
|
||||
@@ -84,10 +109,6 @@ static bool SymmetricEncryptHelper( const uint8 *pubPlaintextData, const uint32
|
||||
if ( cubEncryptedData < cubTotalOutput )
|
||||
return false;
|
||||
|
||||
EVPCTXPointer<EVP_CIPHER_CTX *, EVP_CIPHER_CTX_free> ctx(EVP_CIPHER_CTX_new());
|
||||
|
||||
if (!ctx.ctx)
|
||||
return false;
|
||||
|
||||
const EVP_CIPHER *cipher = NULL;
|
||||
switch(cubKey * 8) {
|
||||
@@ -98,16 +119,17 @@ static bool SymmetricEncryptHelper( const uint8 *pubPlaintextData, const uint32
|
||||
if (!cipher)
|
||||
return false;
|
||||
|
||||
if (EVP_EncryptInit_ex(ctx.ctx, cipher, NULL, pubKey, pIV) != 1)
|
||||
EVP_CIPHER_CTX_safe ctx;
|
||||
if (EVP_EncryptInit_ex( ctx.Ptr(), cipher, NULL, pubKey, pIV) != 1)
|
||||
return false;
|
||||
|
||||
int ciphertext_len, len;
|
||||
|
||||
if (EVP_EncryptUpdate(ctx.ctx, pubEncryptedData, &len, pubPlaintextData, cubPlaintextData) != 1)
|
||||
if (EVP_EncryptUpdate(ctx.Ptr(), pubEncryptedData, &len, pubPlaintextData, cubPlaintextData) != 1)
|
||||
return false;
|
||||
ciphertext_len = len;
|
||||
|
||||
if (EVP_EncryptFinal(ctx.ctx, pubEncryptedData + len, &len) != 1)
|
||||
if (EVP_EncryptFinal(ctx.Ptr(), pubEncryptedData + len, &len) != 1)
|
||||
return false;
|
||||
ciphertext_len += len;
|
||||
|
||||
@@ -161,11 +183,6 @@ static bool BDecryptAESUsingOpenSSL( const uint8 *pubEncryptedData,
|
||||
if ( *pcubPlaintextData < cubEncryptedData - k_nSymmetricBlockSize )
|
||||
return false;
|
||||
|
||||
EVPCTXPointer<EVP_CIPHER_CTX *, EVP_CIPHER_CTX_free> ctx(EVP_CIPHER_CTX_new());
|
||||
|
||||
if (!ctx.ctx)
|
||||
return false;
|
||||
|
||||
const EVP_CIPHER *cipher = NULL;
|
||||
switch(cubKey * 8) {
|
||||
case 128: cipher = EVP_aes_128_cbc(); break;
|
||||
@@ -175,16 +192,17 @@ static bool BDecryptAESUsingOpenSSL( const uint8 *pubEncryptedData,
|
||||
if (!cipher)
|
||||
return false;
|
||||
|
||||
if (EVP_DecryptInit_ex(ctx.ctx, cipher, NULL, pubKey, pIV) != 1)
|
||||
EVP_CIPHER_CTX_safe ctx;
|
||||
if (EVP_DecryptInit_ex(ctx.Ptr(), cipher, NULL, pubKey, pIV) != 1)
|
||||
return false;
|
||||
|
||||
int plaintext_len, len;
|
||||
|
||||
if (EVP_DecryptUpdate(ctx.ctx, pubPlaintextData, &len, pubEncryptedData, cubEncryptedData) != 1)
|
||||
if (EVP_DecryptUpdate(ctx.Ptr(), pubPlaintextData, &len, pubEncryptedData, cubEncryptedData) != 1)
|
||||
return false;
|
||||
plaintext_len = len;
|
||||
|
||||
if (EVP_DecryptFinal(ctx.ctx, pubPlaintextData + plaintext_len, &len) != 1)
|
||||
if (EVP_DecryptFinal(ctx.Ptr(), pubPlaintextData + plaintext_len, &len) != 1)
|
||||
return false;
|
||||
plaintext_len += len;
|
||||
|
||||
@@ -231,6 +249,213 @@ bool CCrypto::SymmetricDecryptWithIV( const uint8 *pubEncryptedData, uint32 cubE
|
||||
return BDecryptAESUsingOpenSSL( pubEncryptedData, cubEncryptedData, pubPlaintextData, pcubPlaintextData, pubKey, cubKey, pIV, bVerifyPaddingBytes );
|
||||
}
|
||||
|
||||
static const EVP_CIPHER *GetAESGCMCipherForKeyLength( size_t cbKey )
|
||||
{
|
||||
switch ( cbKey )
|
||||
{
|
||||
case 128/8: return EVP_aes_128_gcm();
|
||||
case 192/8: return EVP_aes_192_gcm();
|
||||
case 256/8: return EVP_aes_256_gcm();
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CCrypto::SymmetricAuthEncryptChosenIV(
|
||||
const void *pPlaintextData, size_t cbPlaintextData,
|
||||
const void *pIV, size_t cbIV,
|
||||
void *pEncryptedDataAndTag, uint32 *pcbEncryptedDataAndTag,
|
||||
const void *pKey, size_t cbKey,
|
||||
const void *pAdditionalAuthenticationData, size_t cbAuthenticationData,
|
||||
size_t cbTag
|
||||
) {
|
||||
|
||||
// Calculate size of encrypted data. Note that GCM does not use padding.
|
||||
uint32 cbEncryptedWithoutTag = (uint32)cbPlaintextData;
|
||||
uint32 cbEncryptedTotal = cbEncryptedWithoutTag + (uint32)cbTag;
|
||||
|
||||
// Make sure their buffer is big enough
|
||||
if ( cbEncryptedTotal > *pcbEncryptedDataAndTag )
|
||||
{
|
||||
AssertMsg( false, "Buffer isn't big enough to hold padded+encrypted data and tag" );
|
||||
return false;
|
||||
}
|
||||
|
||||
// This function really shouldn't fail unless you have a bug
|
||||
// and pass a bad IV, key, or tag size. So people might not
|
||||
// check the return value. So make sure if we do fail, they
|
||||
// don't think anything was encrypted.
|
||||
*pcbEncryptedDataAndTag = 0;
|
||||
|
||||
// Select the cipher based on the size of the key
|
||||
const EVP_CIPHER *cipher = GetAESGCMCipherForKeyLength( cbKey );
|
||||
if ( cipher == nullptr )
|
||||
{
|
||||
AssertMsg( false, "Invalid AES-GCM key size" );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Reference:
|
||||
// https://wiki.openssl.org/index.php/EVP_Authenticated_Encryption_and_Decryption
|
||||
|
||||
// Setup a context. Don't set the IV right now, since the default size
|
||||
// might not be the size of the IV they are using.
|
||||
EVP_CIPHER_CTX_safe ctx;
|
||||
VerifyFatal( EVP_EncryptInit_ex( ctx.Ptr(), cipher, nullptr, nullptr, nullptr ) == 1 );
|
||||
|
||||
// Set IV length
|
||||
if ( EVP_CIPHER_CTX_ctrl( ctx.Ptr(), EVP_CTRL_GCM_SET_IVLEN, (int)cbIV, NULL) != 1 )
|
||||
{
|
||||
AssertMsg( false, "Bad IV size" );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set key and IV
|
||||
VerifyFatal( EVP_EncryptInit_ex( ctx.Ptr(), nullptr, nullptr, (const uint8*)pKey, (const uint8*)pIV ) == 1 );
|
||||
|
||||
int nBytesWritten;
|
||||
|
||||
// AAD, if any
|
||||
if ( cbAuthenticationData > 0 && pAdditionalAuthenticationData )
|
||||
{
|
||||
VerifyFatal( EVP_EncryptUpdate( ctx.Ptr(), nullptr, &nBytesWritten, (const uint8*)pAdditionalAuthenticationData, (int)cbAuthenticationData ) == 1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert( cbAuthenticationData == 0 );
|
||||
}
|
||||
|
||||
// Now the actual plaintext to be encrypted
|
||||
uint8 *pOut = (uint8 *)pEncryptedDataAndTag;
|
||||
VerifyFatal( EVP_EncryptUpdate( ctx.Ptr(), pOut, &nBytesWritten, (const uint8*)pPlaintextData, (int)cbPlaintextData ) == 1 );
|
||||
pOut += nBytesWritten;
|
||||
|
||||
// Finish up
|
||||
VerifyFatal( EVP_EncryptFinal_ex( ctx.Ptr(), pOut, &nBytesWritten ) == 1 );
|
||||
pOut += nBytesWritten;
|
||||
|
||||
// Make sure that we have the expected number of encrypted bytes at this point
|
||||
VerifyFatal( (uint8 *)pEncryptedDataAndTag + cbEncryptedWithoutTag == pOut );
|
||||
|
||||
// Append the tag
|
||||
if ( EVP_CIPHER_CTX_ctrl( ctx.Ptr(), EVP_CTRL_GCM_GET_TAG, (int)cbTag, pOut ) != 1 )
|
||||
{
|
||||
AssertMsg( false, "Bad tag size" );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Give the caller back the size of everything
|
||||
*pcbEncryptedDataAndTag = cbEncryptedTotal;
|
||||
|
||||
// Success.
|
||||
// NOTE: EVP_CIPHER_CTX_safe destructor cleans up
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CCrypto::SymmetricAuthDecryptWithIV(
|
||||
const void *pEncryptedDataAndTag, size_t cbEncryptedDataAndTag,
|
||||
const void *pIV, size_t cbIV,
|
||||
void *pPlaintextData, uint32 *pcbPlaintextData,
|
||||
const void *pKey, size_t cbKey,
|
||||
const void *pAdditionalAuthenticationData, size_t cbAuthenticationData,
|
||||
size_t cbTag
|
||||
) {
|
||||
|
||||
// Make sure buffer and tag sizes aren't totally bogus
|
||||
if ( cbTag > cbEncryptedDataAndTag )
|
||||
{
|
||||
//AssertMsg( false, "Encrypted size doesn't make sense for tag size" );
|
||||
*pcbPlaintextData = 0;
|
||||
return false;
|
||||
}
|
||||
uint32 cbEncryptedDataWithoutTag = uint32( cbEncryptedDataAndTag - cbTag );
|
||||
|
||||
// Make sure their buffer is big enough. Remember that in GCM mode,
|
||||
// there is no padding, so if this fails, we indeed would have overflowed
|
||||
if ( cbEncryptedDataWithoutTag > *pcbPlaintextData )
|
||||
{
|
||||
AssertMsg( false, "Buffer might not be big enough to hold decrypted data" );
|
||||
return false;
|
||||
}
|
||||
|
||||
// People really have to check the return value, but in case they
|
||||
// don't, make sure they don't think we decrypted any data
|
||||
*pcbPlaintextData = 0;
|
||||
|
||||
// Select the cipher based on the size of the key
|
||||
const EVP_CIPHER *cipher = GetAESGCMCipherForKeyLength( cbKey );
|
||||
if ( cipher == nullptr )
|
||||
{
|
||||
AssertMsg( false, "Invalid AES-GCM key size" );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Setup a context. Don't set the IV right now, since the default size
|
||||
// might not be the size of the IV they are using.
|
||||
EVP_CIPHER_CTX_safe ctx;
|
||||
VerifyFatal( EVP_DecryptInit_ex( ctx.Ptr(), cipher, nullptr, nullptr, nullptr ) == 1 );
|
||||
|
||||
// Set IV length
|
||||
if ( EVP_CIPHER_CTX_ctrl( ctx.Ptr(), EVP_CTRL_GCM_SET_IVLEN, (int)cbIV, NULL) != 1 )
|
||||
{
|
||||
AssertMsg( false, "Bad IV size" );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set key and IV
|
||||
VerifyFatal( EVP_DecryptInit_ex( ctx.Ptr(), nullptr, nullptr, (const uint8*)pKey, (const uint8*)pIV ) == 1 );
|
||||
|
||||
int nBytesWritten;
|
||||
|
||||
// AAD, if any
|
||||
if ( cbAuthenticationData > 0 && pAdditionalAuthenticationData )
|
||||
{
|
||||
// I don't think it's actually possible to failed here, but
|
||||
// since the caller really must be checking the return value,
|
||||
// let's not make this fatal
|
||||
if ( EVP_DecryptUpdate( ctx.Ptr(), nullptr, &nBytesWritten, (const uint8*)pAdditionalAuthenticationData, (int)cbAuthenticationData ) != 1 )
|
||||
{
|
||||
AssertMsg( false, "EVP_DecryptUpdate failed?" );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert( cbAuthenticationData == 0 );
|
||||
}
|
||||
|
||||
uint8 *pOut = (uint8 *)pPlaintextData;
|
||||
const uint8 *pIn = (const uint8 *)pEncryptedDataAndTag;
|
||||
|
||||
// Now the actual ciphertext to be decrypted
|
||||
if ( EVP_DecryptUpdate( ctx.Ptr(), pOut, &nBytesWritten, pIn, (int)cbEncryptedDataWithoutTag ) != 1 )
|
||||
return false;
|
||||
pOut += nBytesWritten;
|
||||
pIn += cbEncryptedDataWithoutTag;
|
||||
|
||||
// Set expected tag value
|
||||
if( EVP_CIPHER_CTX_ctrl( ctx.Ptr(), EVP_CTRL_GCM_SET_TAG, (int)cbTag, const_cast<uint8*>( pIn ) ) != 1)
|
||||
{
|
||||
AssertMsg( false, "Bad tag size" );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Finish up, and check tag
|
||||
if ( EVP_DecryptFinal_ex( ctx.Ptr(), pOut, &nBytesWritten ) <= 0 )
|
||||
return false; // data has been tamped with
|
||||
pOut += nBytesWritten;
|
||||
|
||||
// Make sure we got back the size we expected, and return the size
|
||||
VerifyFatal( pOut == (uint8 *)pPlaintextData + cbEncryptedDataWithoutTag );
|
||||
*pcbPlaintextData = cbEncryptedDataWithoutTag;
|
||||
|
||||
// Success.
|
||||
// NOTE: EVP_CIPHER_CTX_safe destructor cleans up
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Generate a SHA256 hash
|
||||
// Input: pchInput - Plaintext string of item to hash (null terminated)
|
||||
|
||||
@@ -51,6 +51,26 @@ namespace CCrypto
|
||||
const uint8 * pIV, uint32 cubIV,
|
||||
uint8 * pubPlaintextData, uint32 * pcubPlaintextData,
|
||||
const uint8 * pubKey, uint32 cubKey, bool bVerifyPaddingBytes = true );
|
||||
|
||||
// Symmetric encryption and authentication using AES-GCM.
|
||||
bool SymmetricAuthEncryptChosenIV(
|
||||
const void *pPlaintextData, size_t cbPlaintextData,
|
||||
const void *pIV, size_t cbIV,
|
||||
void *pEncryptedDataAndTag, uint32 *pcbEncryptedDataAndTag,
|
||||
const void *pKey, size_t cbKey,
|
||||
const void *pAdditionalAuthenticationData, size_t cbAuthenticationData, // Optional additional authentication data. Not encrypted, but will be included in the tag, so it can be authenticated.
|
||||
size_t cbTag // Number of tag bytes to append to the end of the buffer
|
||||
);
|
||||
|
||||
// Symmetric decryption and check authentication tag using AES-GCM.
|
||||
bool SymmetricAuthDecryptWithIV(
|
||||
const void *pEncryptedDataAndTag, size_t cbEncryptedDataAndTag,
|
||||
const void *pIV, size_t cbIV,
|
||||
void *pPlaintextData, uint32 *pcbPlaintextData,
|
||||
const void *pKey, size_t cbKey,
|
||||
const void *pAdditionalAuthenticationData, size_t cbAuthenticationData, // Optional additional authentication data. Not encrypted, but will be included in the tag, so it can be authenticated.
|
||||
size_t cbTag // Last N bytes in your buffer are assumed to be a tag, and will be checked
|
||||
);
|
||||
|
||||
//
|
||||
// Secure key exchange (curve25519 elliptic-curve Diffie-Hellman key exchange)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,2 @@
|
||||
https://csrc.nist.gov/Projects/Cryptographic-Algorithm-Validation-Program/CAVP-TESTING-BLOCK-CIPHER-MODES
|
||||
https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Algorithm-Validation-Program/documents/mac/gcmtestvectors.zip
|
||||
@@ -1,4 +1,5 @@
|
||||
#include <assert.h>
|
||||
#include <string>
|
||||
|
||||
#include <tier1/utlbuffer.h>
|
||||
#include <crypto.h>
|
||||
@@ -8,6 +9,8 @@
|
||||
// A little compatibility glue so I don't have to make any changes to them.
|
||||
#define CHECK(x) Assert(x)
|
||||
#define CHECK_EQUAL(a,b) Assert((a)==(b))
|
||||
#define RETURNIFNOT(x) { if ( !(x) ) { AssertMsg( false, #x ); return; } }
|
||||
#define RETURNFALSEIFNOT(x) { if ( !(x) ) { AssertMsg( false, #x ); return false; } }
|
||||
const int k_cSmallBuff = 255; // smallish buffer
|
||||
const int k_cMedBuff = 1024;
|
||||
|
||||
@@ -219,6 +222,136 @@ void TestSymmetricCrypto()
|
||||
CHECK( !V_strcmp( rgchSrc, (const char *)rgubInplace ) );
|
||||
}
|
||||
|
||||
// https://csrc.nist.gov/Projects/Cryptographic-Algorithm-Validation-Program/CAVP-TESTING-BLOCK-CIPHER-MODES
|
||||
// https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Algorithm-Validation-Program/documents/mac/gcmtestvectors.zip
|
||||
class NISTTestVectorFile
|
||||
{
|
||||
FILE *f = nullptr;
|
||||
public:
|
||||
NISTTestVectorFile( const char *pszFilename )
|
||||
{
|
||||
f = fopen( pszFilename, "rt" );
|
||||
CHECK(f);
|
||||
}
|
||||
~NISTTestVectorFile() { if ( f ) fclose( f ); }
|
||||
|
||||
bool FindNextTest()
|
||||
{
|
||||
if ( !f )
|
||||
return false;
|
||||
char line[2048];
|
||||
while ( fgets( line, sizeof(line), f ) )
|
||||
{
|
||||
if ( V_strnicmp( line, "count", 5 ) == 0 )
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GetBinaryBlob( const char *pszTag, std::string &blob )
|
||||
{
|
||||
char line[2048];
|
||||
RETURNFALSEIFNOT( fgets( line, sizeof(line), f ) );
|
||||
int lTag = V_strlen(pszTag);
|
||||
RETURNFALSEIFNOT( V_strnicmp( line, pszTag, lTag ) == 0 );
|
||||
const char *p = line + lTag;
|
||||
while ( isspace(*p) )
|
||||
++p;
|
||||
CHECK_EQUAL( *p, '=' );
|
||||
++p;
|
||||
while ( isspace(*p) )
|
||||
++p;
|
||||
|
||||
uint8 decodedData[ 1024 ];
|
||||
uint32 cbDecodedData = sizeof(decodedData);
|
||||
RETURNFALSEIFNOT( CCrypto::HexDecode( p, decodedData, &cbDecodedData ) );
|
||||
blob.assign( (char *)decodedData, cbDecodedData );
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
void TestSymmetricAuthCrypto_EncryptTestVectorFile( const char *pszFilename )
|
||||
{
|
||||
NISTTestVectorFile file( pszFilename );
|
||||
|
||||
while ( file.FindNextTest() )
|
||||
{
|
||||
std::string key;
|
||||
RETURNIFNOT( file.GetBinaryBlob( "key", key ) );
|
||||
std::string iv;
|
||||
RETURNIFNOT( file.GetBinaryBlob( "iv", iv ) );
|
||||
std::string pt;
|
||||
RETURNIFNOT( file.GetBinaryBlob( "pt", pt ) );
|
||||
std::string aad;
|
||||
RETURNIFNOT( file.GetBinaryBlob( "aad", aad ) );
|
||||
std::string ct;
|
||||
RETURNIFNOT( file.GetBinaryBlob( "ct", ct ) );
|
||||
std::string tag;
|
||||
RETURNIFNOT( file.GetBinaryBlob( "tag", tag ) );
|
||||
|
||||
uint8 encrypted[ 2048 ];
|
||||
uint32 cbEncrypted = sizeof(encrypted);
|
||||
RETURNIFNOT( ct.length() <= sizeof(encrypted) );
|
||||
|
||||
// Encrypt it
|
||||
CHECK( CCrypto::SymmetricAuthEncryptChosenIV(
|
||||
pt.c_str(), pt.length(),
|
||||
iv.c_str(), iv.length(),
|
||||
encrypted, &cbEncrypted,
|
||||
key.c_str(), key.length(),
|
||||
aad.c_str(), aad.length(),
|
||||
tag.length()
|
||||
) );
|
||||
|
||||
// Confirm it matches the test vector
|
||||
CHECK( cbEncrypted == ct.length() + tag.length() );
|
||||
CHECK( memcmp( ct.c_str(), encrypted, ct.length() ) == 0 );
|
||||
CHECK( memcmp( tag.c_str(), encrypted+ct.length(), tag.length() ) == 0 );
|
||||
|
||||
// Make sure we can decrypt it successfully
|
||||
uint8 decrypted[ 2048 ];
|
||||
uint32 cbDecrypted = sizeof(decrypted);
|
||||
CHECK( CCrypto::SymmetricAuthDecryptWithIV(
|
||||
encrypted, cbEncrypted,
|
||||
iv.c_str(), iv.length(),
|
||||
decrypted, &cbDecrypted,
|
||||
key.c_str(), key.length(),
|
||||
aad.c_str(), aad.length(),
|
||||
tag.length()
|
||||
) );
|
||||
|
||||
CHECK( cbDecrypted == pt.length() );
|
||||
CHECK( memcmp( pt.c_str(), decrypted, cbDecrypted ) == 0 );
|
||||
|
||||
// Flip a random bit in the ciphertext+tag blob
|
||||
encrypted[ rand() % cbEncrypted ] ^= ( 1 << (rand() & 7 ) );
|
||||
|
||||
// It should fail to decrypt
|
||||
cbDecrypted = sizeof(decrypted);
|
||||
CHECK( !CCrypto::SymmetricAuthDecryptWithIV(
|
||||
encrypted, cbEncrypted,
|
||||
iv.c_str(), iv.length(),
|
||||
decrypted, &cbDecrypted,
|
||||
key.c_str(), key.length(),
|
||||
aad.c_str(), aad.length(),
|
||||
tag.length()
|
||||
) );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Test AES-GCM crypto against known vectors
|
||||
//-----------------------------------------------------------------------------
|
||||
void TestSymmetricAuthCryptoVectors()
|
||||
{
|
||||
#define TEST_VECTOR_DIR "../../tests/aesgcmtestvectors/"
|
||||
|
||||
// Check against known test vectors
|
||||
TestSymmetricAuthCrypto_EncryptTestVectorFile( TEST_VECTOR_DIR "gcmEncryptExtIV128.rsp" );
|
||||
TestSymmetricAuthCrypto_EncryptTestVectorFile( TEST_VECTOR_DIR "gcmEncryptExtIV192.rsp" );
|
||||
TestSymmetricAuthCrypto_EncryptTestVectorFile( TEST_VECTOR_DIR "gcmEncryptExtIV256.rsp" );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Test elliptic-curve primitives (ed25519 signing, curve25519 key exchange)
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -633,6 +766,7 @@ int main()
|
||||
|
||||
TestCryptoEncoding();
|
||||
TestSymmetricCrypto();
|
||||
TestSymmetricAuthCryptoVectors();
|
||||
TestEllipticCrypto();
|
||||
TestOpenSSHEd25519();
|
||||
TestEllipticPerf();
|
||||
|
||||
Reference in New Issue
Block a user