#include "precomp.h"
const UINT64 SymCryptAesKwDefaultICV = 0xA6A6A6A6A6A6A6A6;
const UINT32 SymCryptAesKwpDefaultICV = 0xA65959A6;
#define SYMCRYPT_AES_SEMIBLOCK_SIZE (SYMCRYPT_AES_BLOCK_SIZE / 2)
const SIZE_T SymCryptAesKWMinPlaintextLen = 16u;
const SIZE_T SymCryptAesKWMaxPlaintextLen = (1u<<31)-8;
const SIZE_T SymCryptAesKWMinCiphertextLen = 24u;
const SIZE_T SymCryptAesKWMaxCiphertextLen = (1u<<31);
const SIZE_T SymCryptAesKWPMinPlaintextLen = 1u;
const SIZE_T SymCryptAesKWPMaxPlaintextLen = (1u<<31)-8;
const SIZE_T SymCryptAesKWPMinCiphertextLen = 16u;
const SIZE_T SymCryptAesKWPMaxCiphertextLen = (1u<<31);
static
VOID
SYMCRYPT_CALL
SymCryptAesKwxInternalWrap(
_In_ PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
_Inout_updates_bytes_(cbBuf) PBYTE pbBuf,
UINT32 cbBuf )
{
SYMCRYPT_ALIGN BYTE activeBlock[SYMCRYPT_AES_BLOCK_SIZE];
const UINT32 nSemiBlocks = cbBuf / SYMCRYPT_AES_SEMIBLOCK_SIZE;
UINT64 encryptionIdx = 1;
UINT64 lowHalfTemp = 0;
SYMCRYPT_ASSERT((cbBuf & (SYMCRYPT_AES_SEMIBLOCK_SIZE-1)) == 0);
SYMCRYPT_ASSERT(cbBuf >= SymCryptAesKWMinCiphertextLen);
SYMCRYPT_ASSERT(cbBuf <= SymCryptAesKWMaxCiphertextLen);
memcpy( activeBlock, pbBuf, SYMCRYPT_AES_SEMIBLOCK_SIZE);
for( UINT32 outerLoopCnt = 0; outerLoopCnt < 6; outerLoopCnt++ )
{
for( UINT32 innerLoopCnt = 1; innerLoopCnt < nSemiBlocks; innerLoopCnt++ )
{
SIZE_T bufOffset = innerLoopCnt*SYMCRYPT_AES_SEMIBLOCK_SIZE;
memcpy( activeBlock+SYMCRYPT_AES_SEMIBLOCK_SIZE, pbBuf+bufOffset, SYMCRYPT_AES_SEMIBLOCK_SIZE);
SymCryptAesEncrypt( pExpandedKey, activeBlock, activeBlock );
memcpy( pbBuf+bufOffset, activeBlock+SYMCRYPT_AES_SEMIBLOCK_SIZE, SYMCRYPT_AES_SEMIBLOCK_SIZE );
lowHalfTemp = SYMCRYPT_LOAD_LSBFIRST64( activeBlock );
lowHalfTemp ^= SYMCRYPT_BSWAP64( encryptionIdx );
SYMCRYPT_STORE_LSBFIRST64( activeBlock, lowHalfTemp );
encryptionIdx++;
}
}
SYMCRYPT_ASSERT( (encryptionIdx-1) == (nSemiBlocks-1)*6 );
SYMCRYPT_STORE_LSBFIRST64( pbBuf, lowHalfTemp );
SymCryptWipeKnownSize( activeBlock, sizeof(activeBlock) );
}
static
VOID
SYMCRYPT_CALL
SymCryptAesKwxInternalUnwrap(
_In_ PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
_Inout_updates_bytes_(cbBuf) PBYTE pbBuf,
UINT32 cbBuf )
{
SYMCRYPT_ALIGN BYTE activeBlock[SYMCRYPT_AES_BLOCK_SIZE];
const UINT32 nSemiBlocks = cbBuf / SYMCRYPT_AES_SEMIBLOCK_SIZE;
UINT64 decryptionIdx = 6*(nSemiBlocks-1);
UINT64 lowHalfTemp = 0;
SYMCRYPT_ASSERT((cbBuf & (SYMCRYPT_AES_SEMIBLOCK_SIZE-1)) == 0);
SYMCRYPT_ASSERT(cbBuf >= SymCryptAesKWMinCiphertextLen);
SYMCRYPT_ASSERT(cbBuf <= SymCryptAesKWMaxCiphertextLen);
lowHalfTemp = SYMCRYPT_LOAD_LSBFIRST64( pbBuf );
for( UINT32 outerLoopCnt = 0; outerLoopCnt < 6; outerLoopCnt++ )
{
for( UINT32 innerLoopCnt = nSemiBlocks-1; innerLoopCnt > 0; innerLoopCnt-- )
{
SIZE_T bufOffset = innerLoopCnt*SYMCRYPT_AES_SEMIBLOCK_SIZE;
lowHalfTemp ^= SYMCRYPT_BSWAP64( decryptionIdx );
SYMCRYPT_STORE_LSBFIRST64( activeBlock, lowHalfTemp );
memcpy( activeBlock+SYMCRYPT_AES_SEMIBLOCK_SIZE, pbBuf+bufOffset, SYMCRYPT_AES_SEMIBLOCK_SIZE);
SymCryptAesDecrypt( pExpandedKey, activeBlock, activeBlock );
memcpy( pbBuf+bufOffset, activeBlock+SYMCRYPT_AES_SEMIBLOCK_SIZE, SYMCRYPT_AES_SEMIBLOCK_SIZE );
decryptionIdx--;
lowHalfTemp = SYMCRYPT_LOAD_LSBFIRST64( activeBlock );
}
}
SYMCRYPT_ASSERT( decryptionIdx == 0 );
SYMCRYPT_STORE_LSBFIRST64( pbBuf, lowHalfTemp );
SymCryptWipeKnownSize( activeBlock, sizeof(activeBlock) );
}
SYMCRYPT_ERROR
SYMCRYPT_CALL
SymCryptAesKwEncrypt(
_In_ PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
_In_reads_(cbSrc) PCBYTE pbSrc,
SIZE_T cbSrc,
_Out_writes_to_(cbDst, *pcbResult) PBYTE pbDst,
SIZE_T cbDst,
_Out_ SIZE_T* pcbResult )
{
SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
PBYTE pbScratch = NULL;
UINT32 cbScratch = 0;
if( (cbSrc < SymCryptAesKWMinPlaintextLen) ||
(cbSrc > SymCryptAesKWMaxPlaintextLen) ||
((cbSrc & (SYMCRYPT_AES_SEMIBLOCK_SIZE-1)) != 0) )
{
scError = SYMCRYPT_INVALID_ARGUMENT;
goto cleanup;
}
cbScratch = ((UINT32) cbSrc)+SYMCRYPT_AES_SEMIBLOCK_SIZE;
if( cbDst < cbScratch )
{
scError = SYMCRYPT_BUFFER_TOO_SMALL;
goto cleanup;
}
pbScratch = SymCryptCallbackAlloc( cbScratch );
if( pbScratch == NULL )
{
scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
goto cleanup;
}
SYMCRYPT_STORE_LSBFIRST64( pbScratch, SymCryptAesKwDefaultICV );
memcpy( pbScratch+8, pbSrc, cbSrc );
SymCryptAesKwxInternalWrap( pExpandedKey, pbScratch, cbScratch );
memcpy( pbDst, pbScratch, cbScratch );
*pcbResult = cbScratch;
cleanup:
if( pbScratch != NULL )
{
SymCryptWipe( pbScratch, cbScratch );
SymCryptCallbackFree( pbScratch );
}
return scError;
}
SYMCRYPT_ERROR
SYMCRYPT_CALL
SymCryptAesKwDecrypt(
_In_ PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
_In_reads_(cbSrc) PCBYTE pbSrc,
SIZE_T cbSrc,
_Out_writes_to_(cbDst, *pcbResult) PBYTE pbDst,
SIZE_T cbDst,
_Out_ SIZE_T* pcbResult )
{
SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
PBYTE pbScratch = NULL;
UINT32 cbScratch = 0;
if( (cbSrc < SymCryptAesKWMinCiphertextLen) ||
(cbSrc > SymCryptAesKWMaxCiphertextLen) ||
((cbSrc & (SYMCRYPT_AES_SEMIBLOCK_SIZE-1)) != 0) )
{
scError = SYMCRYPT_INVALID_ARGUMENT;
goto cleanup;
}
cbScratch = (UINT32) cbSrc;
if( cbDst < cbScratch-SYMCRYPT_AES_SEMIBLOCK_SIZE )
{
scError = SYMCRYPT_BUFFER_TOO_SMALL;
goto cleanup;
}
pbScratch = SymCryptCallbackAlloc( cbScratch );
if( pbScratch == NULL )
{
scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
goto cleanup;
}
memcpy( pbScratch, pbSrc, cbSrc );
SymCryptAesKwxInternalUnwrap( pExpandedKey, pbScratch, cbScratch );
if( SYMCRYPT_LOAD_LSBFIRST64( pbScratch ) != SymCryptAesKwDefaultICV )
{
scError = SYMCRYPT_AUTHENTICATION_FAILURE;
goto cleanup;
}
memcpy( pbDst, pbScratch+SYMCRYPT_AES_SEMIBLOCK_SIZE, cbScratch-SYMCRYPT_AES_SEMIBLOCK_SIZE );
*pcbResult = cbScratch-SYMCRYPT_AES_SEMIBLOCK_SIZE;
cleanup:
if( pbScratch != NULL )
{
SymCryptWipe( pbScratch, cbScratch );
SymCryptCallbackFree( pbScratch );
}
return scError;
}
SYMCRYPT_ERROR
SYMCRYPT_CALL
SymCryptAesKwpEncrypt(
_In_ PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
_In_reads_(cbSrc) PCBYTE pbSrc,
SIZE_T cbSrc,
_Out_writes_to_(cbDst, *pcbResult) PBYTE pbDst,
SIZE_T cbDst,
_Out_ SIZE_T* pcbResult )
{
SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
PBYTE pbScratch = NULL;
UINT32 cbScratch = 0;
UINT32 cbPad = 0;
if( (cbSrc < SymCryptAesKWPMinPlaintextLen) ||
(cbSrc > SymCryptAesKWPMaxPlaintextLen) )
{
scError = SYMCRYPT_INVALID_ARGUMENT;
goto cleanup;
}
cbPad = SYMCRYPT_AES_SEMIBLOCK_SIZE - ((UINT32) cbSrc & (SYMCRYPT_AES_SEMIBLOCK_SIZE-1));
if( cbPad == SYMCRYPT_AES_SEMIBLOCK_SIZE )
{
cbPad = 0;
}
cbScratch = (UINT32) cbSrc + SYMCRYPT_AES_SEMIBLOCK_SIZE + cbPad;
if( cbDst < cbScratch )
{
scError = SYMCRYPT_BUFFER_TOO_SMALL;
goto cleanup;
}
SYMCRYPT_ASSERT( cbScratch >= 16 );
pbScratch = SymCryptCallbackAlloc( cbScratch );
if( pbScratch == NULL )
{
scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
goto cleanup;
}
SYMCRYPT_STORE_LSBFIRST32( pbScratch, SymCryptAesKwpDefaultICV );
SYMCRYPT_STORE_MSBFIRST32( pbScratch+4, (UINT32) cbSrc );
SYMCRYPT_STORE_LSBFIRST64( pbScratch+cbScratch-SYMCRYPT_AES_SEMIBLOCK_SIZE, 0u );
memcpy( pbScratch+8, pbSrc, cbSrc );
if( cbScratch == SYMCRYPT_AES_BLOCK_SIZE )
{
SymCryptAesEncrypt( pExpandedKey, pbScratch, pbScratch );
} else {
SymCryptAesKwxInternalWrap( pExpandedKey, pbScratch, cbScratch );
}
memcpy( pbDst, pbScratch, cbScratch );
*pcbResult = cbScratch;
cleanup:
if( pbScratch != NULL )
{
SymCryptWipe( pbScratch, cbScratch );
SymCryptCallbackFree( pbScratch );
}
return scError;
}
SYMCRYPT_ERROR
SYMCRYPT_CALL
SymCryptAesKwpDecrypt(
_In_ PCSYMCRYPT_AES_EXPANDED_KEY pExpandedKey,
_In_reads_(cbSrc) PCBYTE pbSrc,
SIZE_T cbSrc,
_Out_writes_to_(cbDst, *pcbResult) PBYTE pbDst,
SIZE_T cbDst,
_Out_ SIZE_T* pcbResult )
{
SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
PBYTE pbScratch = NULL;
UINT32 cbScratch = 0;
UINT32 cbPlaintext = 0;
UINT32 cbPad = 0;
UINT32 mVerificationError = 0;
UINT32 mIsPlaintext = 0;
if( (cbSrc < SymCryptAesKWPMinCiphertextLen) ||
(cbSrc > SymCryptAesKWPMaxCiphertextLen) ||
((cbSrc & (SYMCRYPT_AES_SEMIBLOCK_SIZE-1)) != 0) )
{
scError = SYMCRYPT_INVALID_ARGUMENT;
goto cleanup;
}
cbScratch = (UINT32) cbSrc;
if( cbDst < cbScratch-SYMCRYPT_AES_SEMIBLOCK_SIZE-7 )
{
scError = SYMCRYPT_BUFFER_TOO_SMALL;
goto cleanup;
}
pbScratch = SymCryptCallbackAlloc( cbScratch );
if( pbScratch == NULL )
{
scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
goto cleanup;
}
memcpy( pbScratch, pbSrc, cbSrc );
if( cbScratch == SYMCRYPT_AES_BLOCK_SIZE )
{
SymCryptAesDecrypt( pExpandedKey, pbScratch, pbScratch );
} else {
SymCryptAesKwxInternalUnwrap( pExpandedKey, pbScratch, cbScratch );
}
mVerificationError |= SYMCRYPT_LOAD_LSBFIRST32( pbScratch ) ^ SymCryptAesKwpDefaultICV;
cbPlaintext = SYMCRYPT_LOAD_MSBFIRST32( pbScratch+4 );
cbPad = (UINT32) cbSrc - cbPlaintext - SYMCRYPT_AES_SEMIBLOCK_SIZE;
mVerificationError |= (cbPad & 0xfffffff8);
for( UINT32 i = 1; i<SYMCRYPT_AES_SEMIBLOCK_SIZE; i++ )
{
mIsPlaintext = SymCryptMask32LtU31(i, SYMCRYPT_AES_SEMIBLOCK_SIZE-(cbPad&7));
mVerificationError |= ((UINT32) pbScratch[ cbScratch-SYMCRYPT_AES_SEMIBLOCK_SIZE+i ]) & ~mIsPlaintext;
}
if( mVerificationError != 0 )
{
scError = SYMCRYPT_AUTHENTICATION_FAILURE;
goto cleanup;
}
if( cbDst < cbPlaintext )
{
scError = SYMCRYPT_BUFFER_TOO_SMALL;
goto cleanup;
}
memcpy( pbDst, pbScratch+SYMCRYPT_AES_SEMIBLOCK_SIZE, cbPlaintext );
*pcbResult = cbPlaintext;
cleanup:
if( pbScratch != NULL )
{
SymCryptWipe( pbScratch, cbScratch );
SymCryptCallbackFree( pbScratch );
}
return scError;
}