Files
WjCryptLib/lib/CryptLib_AesCtr.c
zebra f75d43bb19 Version 2.0.0
* Added AES and AES-CTR modules. AES-CTR conforms to the same counter
mode used with AES in *OpenSSL*.
* All algorithms now work on Big-Endian architectures.
* Now uses CMake for building rather than make files and Visual Studio
projects. CMake will generate whatever system is required.
* Input function parameters are now marked `const`
* File names have been changed to have the prefix `CryptLib_` rather
than `Lib`.
* Various formatting changes to the files.
2017-12-02 00:16:32 +11:00

284 lines
12 KiB
C

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CryptLib_AesCtr
//
// Implementation of AES CTR stream cipher.
//
// Depends on: CryptoLib_Aes
//
// AES CTR is a stream cipher using the AES block cipher in counter mode.
// This implementation works on both little and big endian architectures.
//
// This is free and unencumbered software released into the public domain - November 2017 waterjuice.org
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// IMPORTS
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include "CryptLib_AesCtr.h"
#include "CryptLib_Aes.h"
#include <stdint.h>
#include <memory.h>
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// MACROS
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#define MIN( x, y ) ( ((x)<(y))?(x):(y) )
#define STORE64H( x, y ) \
{ (y)[0] = (uint8_t)(((x)>>56)&255); (y)[1] = (uint8_t)(((x)>>48)&255); \
(y)[2] = (uint8_t)(((x)>>40)&255); (y)[3] = (uint8_t)(((x)>>32)&255); \
(y)[4] = (uint8_t)(((x)>>24)&255); (y)[5] = (uint8_t)(((x)>>16)&255); \
(y)[6] = (uint8_t)(((x)>>8)&255); (y)[7] = (uint8_t)((x)&255); }
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// INTERNAL FUNCTIONS
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CreateCurrentCipherBlock
//
// Takes the IV and the counter in the AesCtrContext and produces the cipher block (CurrentCipherBlock). The cipher
// block is produced by first creating a 128 bit block with the IV as first 64 bits and the CurrentCipherBlockIndex
// stored as the remaining 64bits in Network byte order (Big Endian)
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static
void
CreateCurrentCipherBlock
(
AesCtrContext* Context // [in out]
)
{
// Build block by first copying in the IV
memcpy( Context->CurrentCipherBlock, Context->IV, AES_CTR_IV_SIZE );
// Now place in the counter in Big Endian form
STORE64H( Context->CurrentCipherBlockIndex, Context->CurrentCipherBlock + AES_CTR_IV_SIZE );
// Perform AES encryption on the block
AesEncryptInPlace( &Context->Aes, Context->CurrentCipherBlock );
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// XorBuffer
//
// Takes two Source buffers and XORs them together and puts the result in DestinationBuffer
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static
void
XorBuffers
(
uint8_t const* SourceBuffer1, // [in]
uint8_t const* SourceBuffer2, // [in]
uint8_t* DestinationBuffer, // [out]
uint32_t Amount // [in]
)
{
uint32_t i;
for( i=0; i<Amount; i++ )
{
DestinationBuffer[i] = SourceBuffer1[i] ^ SourceBuffer2[i];
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// AesCtrInitialise
//
// Initialises an AesCtrContext with an already initialised AesContext and a IV. This function can quickly be used
// to change the IV without requiring the more length processes of reinitialising an AES key.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void
AesCtrInitialise
(
AesContext const* InitialisedAesContext, // [in]
uint8_t const IV [AES_CTR_IV_SIZE], // [in]
AesCtrContext* Context // [out]
)
{
// Setup context values
Context->Aes = *InitialisedAesContext;
memcpy( Context->IV, IV, AES_CTR_IV_SIZE );
Context->StreamIndex = 0;
Context->CurrentCipherBlockIndex = 0;
// Generate the first cipher block of the stream.
CreateCurrentCipherBlock( Context );
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// AesCtrInitialiseWithKey
//
// Initialises an AesCtrContext with an AES Key and an IV. This combines the initialising an AES Context and then
// running AesCtrInitialise. KeySize must be 16, 24, or 32 (for 128, 192, or 256 bit key size)
// Returns 0 if successful, or -1 if invalid KeySize provided
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
int
AesCtrInitialiseWithKey
(
uint8_t const* Key, // [in]
uint32_t KeySize, // [in]
uint8_t const IV [AES_CTR_IV_SIZE], // [in]
AesCtrContext* Context // [out]
)
{
AesContext aes;
// Initialise AES Context
switch( KeySize )
{
case AES_KEY_SIZE_128: AesInitialise128( Key, &aes ); break;
case AES_KEY_SIZE_192: AesInitialise192( Key, &aes ); break;
case AES_KEY_SIZE_256: AesInitialise256( Key, &aes ); break;
default:
// Invalid key size
return -1;
}
// Now set-up AesCtrContext
AesCtrInitialise( &aes, IV, Context );
return 0;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// AesCtrSetStreamIndex
//
// Sets the current stream index to any arbitrary position. Setting to 0 sets it to the beginning of the stream. Any
// subsequent output will start from this position
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void
AesCtrSetStreamIndex
(
AesCtrContext* Context, // [in out]
uint64_t StreamIndex // [in]
)
{
uint64_t blockIndex = StreamIndex / AES_BLOCK_SIZE;
Context->StreamIndex = StreamIndex;
if( blockIndex != Context->CurrentCipherBlockIndex )
{
// Update block index and generate new cipher block as the new StreamIndex is inside a different block to the
// one we currently had.
Context->CurrentCipherBlockIndex = blockIndex;
CreateCurrentCipherBlock( Context );
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// AesCtrXor
//
// XORs the stream of byte of the AesCtrContext from its current stream position onto the specified buffer. This will
// advance the stream index by that number of bytes.
// Use once over data to encrypt it. Use it a second time over the same data from the same stream position and the
// data will be decrypted.
// InBuffer and OutBuffer can point to the same location for inplace encrypting/decrypting
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void
AesCtrXor
(
AesCtrContext* Context, // [in out]
void const* InBuffer, // [in]
void* OutBuffer, // [out]
uint32_t Size // [in]
)
{
uint32_t amountLeft = Size;
uint32_t outputOffset = 0;
uint32_t chunkSize;
uint32_t amountAvailableInBlock;
// First determine how much is available in the current block.
amountAvailableInBlock = AES_BLOCK_SIZE - (Context->StreamIndex % AES_BLOCK_SIZE);
// Determine how much of the current block we will take, either all that is available, or less
// if the amount requested is smaller.
chunkSize = MIN( amountAvailableInBlock, amountLeft );
// XOR the bytes from the cipher block
XorBuffers( InBuffer, Context->CurrentCipherBlock + (AES_BLOCK_SIZE - amountAvailableInBlock), OutBuffer, chunkSize );
amountLeft -= chunkSize;
outputOffset += chunkSize;
// Now start generating new cipher blocks as required.
while( amountLeft > 0 )
{
// Increment block index and regenerate cipher block
Context->CurrentCipherBlockIndex += 1;
CreateCurrentCipherBlock( Context );
// Determine how much of the current block we need and XOR it out onto the buffer
chunkSize = MIN( amountLeft, AES_BLOCK_SIZE );
XorBuffers( (uint8_t*)InBuffer + outputOffset, Context->CurrentCipherBlock, (uint8_t*)OutBuffer + outputOffset, chunkSize );
amountLeft -= chunkSize;
outputOffset += chunkSize;
}
// All data read out now, so update index in the context.
Context->StreamIndex += Size;
// If we ended up completely reading the last cipher block we need to generate a new one for next time.
if( AES_BLOCK_SIZE == chunkSize )
{
Context->CurrentCipherBlockIndex += 1;
CreateCurrentCipherBlock( Context );
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// AesCtrOutput
//
// Outputs the stream of byte of the AesCtrContext from its current stream position. This will advance the stream
// index by that number of bytes.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void
AesCtrOutput
(
AesCtrContext* Context, // [in out]
void* Buffer, // [out]
uint32_t Size // [in]
)
{
memset( Buffer, 0, Size );
AesCtrXor( Context, Buffer, Buffer, Size );
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// AesCtrXorWithKey
//
// This function combines AesCtrInitialiseWithKey and AesCtrXor. This is suitable when encrypting/decypting data in
// one go with a key that is not going to be reused.
// This will used the provided Key and IV and generate a stream that is XORed over Buffer.
// InBuffer and OutBuffer can point to the same location for inplace encrypting/decrypting
// Returns 0 if successful, or -1 if invalid KeySize provided
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
int
AesCtrXorWithKey
(
uint8_t const* Key, // [in]
uint32_t KeySize, // [in]
uint8_t const IV [AES_CTR_IV_SIZE], // [in]
void const* InBuffer, // [in]
void* OutBuffer, // [out]
uint32_t BufferSize // [in]
)
{
int error;
AesCtrContext context;
error = AesCtrInitialiseWithKey( Key, KeySize, IV, &context );
if( 0 == error )
{
AesCtrXor( &context, InBuffer, OutBuffer, BufferSize );
}
return error;
}