Path: blob/master/libs/symcrypt/lib/blockciphermodes.c
15010 views
//1// BlockCipherModes.c generic implementation of all block cipher modes2//3// Copyright (c) Microsoft Corporation. Licensed under the MIT license.4//56#include "precomp.h"78VOID9SYMCRYPT_CALL10SymCryptEcbEncrypt(11_In_ PCSYMCRYPT_BLOCKCIPHER pBlockCipher,12_In_ PCVOID pExpandedKey,13_In_reads_( cbData ) PCBYTE pbSrc,14_Out_writes_( cbData ) PBYTE pbDst,15SIZE_T cbData )16{17SIZE_T i;18SIZE_T cbToDo = cbData & ~(pBlockCipher->blockSize - 1);1920if( pBlockCipher->ecbEncryptFunc != NULL )21{22//23// Use optimized implementation if available24//25(*pBlockCipher->ecbEncryptFunc)( pExpandedKey, pbSrc, pbDst, cbData );26return;27}2829//30// To avoid buffer overruns we truncate the work to an integral number of blocks.31//3233for( i=0; i<cbToDo; i+= pBlockCipher->blockSize )34{35(*pBlockCipher->encryptFunc)( pExpandedKey, pbSrc + i, pbDst + i );36}37}3839VOID40SYMCRYPT_CALL41SymCryptEcbDecrypt(42_In_ PCSYMCRYPT_BLOCKCIPHER pBlockCipher,43_In_ PCVOID pExpandedKey,44_In_reads_( cbData ) PCBYTE pbSrc,45_Out_writes_( cbData ) PBYTE pbDst,46SIZE_T cbData )47{48SIZE_T i;49SIZE_T cbToDo = cbData & ~(pBlockCipher->blockSize - 1);5051if( pBlockCipher->ecbDecryptFunc != NULL )52{53//54// Use optimized implementation if available55//56(*pBlockCipher->ecbDecryptFunc)( pExpandedKey, pbSrc, pbDst, cbData );57return;58}5960for( i=0; i<cbToDo; i+= pBlockCipher->blockSize )61{62(*pBlockCipher->decryptFunc)( pExpandedKey, pbSrc + i, pbDst + i );63}64}656667//68// SymCryptCbcEncrypt69//70// Generic CBC encryption routine for block ciphers.71// The following restrictions must be obeyed:72// - blockSize <= 32 and must be a power of 273// - cbData must be a multiple of the block size74//75VOID76SYMCRYPT_CALL77SymCryptCbcEncrypt(78_In_ PCSYMCRYPT_BLOCKCIPHER pBlockCipher,79_In_ PCVOID pExpandedKey,80_Inout_updates_( pBlockCipher->blockSize )81PBYTE pbChainingValue,82_In_reads_( cbData ) PCBYTE pbSrc,83_Out_writes_( cbData ) PBYTE pbDst,84SIZE_T cbData )85{86SYMCRYPT_ALIGN BYTE buf[SYMCRYPT_MAX_BLOCK_SIZE];87SIZE_T blockSize;88PCBYTE pbSrcEnd;89PCBYTE pSrc = pbSrc;90PBYTE pDst = pbDst;9192if( pBlockCipher->cbcEncryptFunc != NULL )93{94//95// Use optimized implementation if available96//97(*pBlockCipher->cbcEncryptFunc)( pExpandedKey, pbChainingValue, pSrc, pDst, cbData );98return;99}100101blockSize = pBlockCipher->blockSize;102103SYMCRYPT_ASSERT( blockSize <= SYMCRYPT_MAX_BLOCK_SIZE );104105106//107// Compute the end of the data, rounding the size down to a multiple of the block size.108//109pbSrcEnd = &pbSrc[ cbData & ~(blockSize - 1) ];110111//112// We keep the chaining state in a local buffer to enforce the read-once write-once rule.113//114memcpy( buf, pbChainingValue, blockSize );115while( pSrc < pbSrcEnd )116{117SYMCRYPT_ASSERT( pSrc <= pbSrc + cbData - blockSize ); // help PreFast118SYMCRYPT_ASSERT( pDst <= pbDst + cbData - blockSize ); // help PreFast119SYMCRYPT_ASSERT( blockSize <= cbData ); // help PreFast120SymCryptXorBytes( pSrc, buf, buf, blockSize );121(*pBlockCipher->encryptFunc)( pExpandedKey, buf, buf );122memcpy( pDst, buf, blockSize );123pSrc += blockSize;124pDst += blockSize;125}126127memcpy( pbChainingValue, buf, blockSize );128129SymCryptWipeKnownSize( buf, sizeof( buf ));130}131132//133// SymCryptCbcDecrypt134//135// Generic CBC decryption routine for block ciphers.136// The following restrictions must be obeyed:137// - blockSize <= 32 and must be a power of 2138// - cbData must be a multiple of the block size139//140VOID141SYMCRYPT_CALL142SymCryptCbcDecrypt(143_In_ PCSYMCRYPT_BLOCKCIPHER pBlockCipher,144_In_ PCVOID pExpandedKey,145_Inout_updates_( pBlockCipher->blockSize )146PBYTE pbChainingValue,147_In_reads_( cbData ) PCBYTE pbSrc,148_Out_writes_( cbData ) PBYTE pbDst,149SIZE_T cbData )150{151SYMCRYPT_ALIGN BYTE buf[3 * SYMCRYPT_MAX_BLOCK_SIZE];152PBYTE chain = &buf[0];153PBYTE ciphertext = &buf[SYMCRYPT_MAX_BLOCK_SIZE];154PBYTE tmp = &buf[2*SYMCRYPT_MAX_BLOCK_SIZE];155156SIZE_T blockSize;157PCBYTE pbSrcEnd;158159if( pBlockCipher->cbcDecryptFunc != NULL )160{161(*pBlockCipher->cbcDecryptFunc)( pExpandedKey, pbChainingValue, pbSrc, pbDst, cbData );162return;163}164165blockSize = pBlockCipher->blockSize;166SYMCRYPT_ASSERT( blockSize <= SYMCRYPT_MAX_BLOCK_SIZE );167168//169// Compute the end of the data, rounding the size down to a multiple of the block size.170//171pbSrcEnd = &pbSrc[ cbData & ~(blockSize-1) ];172173#pragma warning(suppress: 22105)174memcpy( chain, pbChainingValue, blockSize );175176//177// Loop structured to obey the read-once/write-once rule178//179while( pbSrc < pbSrcEnd )180{181SYMCRYPT_ASSERT( pbSrc <= pbSrcEnd - blockSize ); // help PreFast182memcpy( ciphertext, pbSrc, blockSize );183(*pBlockCipher->decryptFunc)( pExpandedKey, ciphertext, tmp );184SymCryptXorBytes( tmp, chain, pbDst, blockSize );185memcpy( chain, ciphertext, blockSize );186pbDst += blockSize;187pbSrc += blockSize;188}189190memcpy( pbChainingValue, chain, blockSize );191192SymCryptWipeKnownSize( buf, sizeof( buf ));193}194195VOID196SYMCRYPT_CALL197SymCryptCbcMac(198_In_ PCSYMCRYPT_BLOCKCIPHER pBlockCipher,199_In_ PCVOID pExpandedKey,200_Inout_updates_( pBlockCipher->blockSize )201PBYTE pbChainingValue,202_In_reads_( cbData ) PCBYTE pbSrc,203SIZE_T cbData )204{205SYMCRYPT_ALIGN BYTE buf[32];206SIZE_T blockSize;207PCBYTE pbSrcEnd;208PCBYTE p;209210if( pBlockCipher->cbcMacFunc != NULL )211{212//213// Use optimized implementation if available214//215(*pBlockCipher->cbcMacFunc)( pExpandedKey, pbChainingValue, pbSrc, cbData );216return;217}218219blockSize = pBlockCipher->blockSize;220SYMCRYPT_ASSERT( blockSize <= SYMCRYPT_MAX_BLOCK_SIZE );221222//223// Compute the end of the data, rounding the size down to a multiple of the block size.224//225pbSrcEnd = &pbSrc[ cbData & ~(blockSize - 1) ];226227//228// We keep the chaining state in a local buffer to enforce the read-once write-once rule.229// It also improves memory locality.230//231memcpy( buf, pbChainingValue, blockSize );232p = pbSrc;233while( p < pbSrcEnd )234{235SYMCRYPT_ASSERT( p <= pbSrc + cbData - blockSize );236SymCryptXorBytes( p, buf, buf, blockSize );237(*pBlockCipher->encryptFunc)( pExpandedKey, buf, buf );238p += blockSize;239}240241memcpy( pbChainingValue, buf, blockSize );242243SymCryptWipeKnownSize( buf, sizeof( buf ));244}245246VOID247SYMCRYPT_CALL248SymCryptCtrMsb32(249_In_ PCSYMCRYPT_BLOCKCIPHER pBlockCipher,250_In_ PCVOID pExpandedKey,251_Inout_updates_( pBlockCipher->blockSize )252PBYTE pbChainingValue,253_In_reads_( cbData ) PCBYTE pbSrc,254_Out_writes_( cbData ) PBYTE pbDst,255SIZE_T cbData )256{257SYMCRYPT_ALIGN BYTE buf[2 * SYMCRYPT_MAX_BLOCK_SIZE];258PBYTE count = &buf[0];259PBYTE keystream= &buf[SYMCRYPT_MAX_BLOCK_SIZE];260SIZE_T blockSize;261PCBYTE pbSrcEnd;262263blockSize = pBlockCipher->blockSize;264SYMCRYPT_ASSERT( blockSize <= SYMCRYPT_MAX_BLOCK_SIZE );265266//267// Compute the end of the data, rounding the size down to a multiple of the block size.268//269pbSrcEnd = &pbSrc[ cbData & ~(blockSize - 1) ];270271//272// We keep the chaining state in a local buffer to enforce the read-once write-once rule.273// It also improves memory locality.274//275#pragma warning(suppress: 22105)276memcpy( count, pbChainingValue, blockSize );277while( pbSrc < pbSrcEnd )278{279SYMCRYPT_ASSERT( pbSrc <= pbSrcEnd - blockSize ); // help PreFast280(*pBlockCipher->encryptFunc)( pExpandedKey, count, keystream );281SymCryptXorBytes( keystream, pbSrc, pbDst, blockSize );282283//284// We only need to increment the last 32 bits of the counter value.285//286SYMCRYPT_STORE_MSBFIRST32( &count[ blockSize-4 ], 1 + SYMCRYPT_LOAD_MSBFIRST32( &count[ blockSize-4 ] ) );287288pbSrc += blockSize;289pbDst += blockSize;290}291292memcpy( pbChainingValue, count, blockSize );293294SymCryptWipeKnownSize( buf, sizeof( buf ));295}296297VOID298SYMCRYPT_CALL299SymCryptCtrMsb64(300_In_ PCSYMCRYPT_BLOCKCIPHER pBlockCipher,301_In_ PCVOID pExpandedKey,302_Inout_updates_( pBlockCipher->blockSize )303PBYTE pbChainingValue,304_In_reads_( cbData ) PCBYTE pbSrc,305_Out_writes_( cbData ) PBYTE pbDst,306SIZE_T cbData )307{308SYMCRYPT_ALIGN BYTE buf[2 * SYMCRYPT_MAX_BLOCK_SIZE];309PBYTE count = &buf[0];310PBYTE keystream= &buf[SYMCRYPT_MAX_BLOCK_SIZE];311SIZE_T blockSize;312PCBYTE pbSrcEnd;313314if( pBlockCipher->ctrMsb64Func != NULL )315{316//317// Use optimized implementation if available318//319(*pBlockCipher->ctrMsb64Func)( pExpandedKey, pbChainingValue, pbSrc, pbDst, cbData );320return;321}322323blockSize = pBlockCipher->blockSize;324SYMCRYPT_ASSERT( blockSize <= SYMCRYPT_MAX_BLOCK_SIZE );325326//327// Compute the end of the data, rounding the size down to a multiple of the block size.328//329pbSrcEnd = &pbSrc[ cbData & ~(blockSize - 1) ];330331//332// We keep the chaining state in a local buffer to enforce the read-once write-once rule.333// It also improves memory locality.334//335#pragma warning(suppress: 22105)336memcpy( count, pbChainingValue, blockSize );337while( pbSrc < pbSrcEnd )338{339SYMCRYPT_ASSERT( pbSrc <= pbSrcEnd - blockSize ); // help PreFast340(*pBlockCipher->encryptFunc)( pExpandedKey, count, keystream );341SymCryptXorBytes( keystream, pbSrc, pbDst, blockSize );342343//344// We only need to increment the last 64 bits of the counter value.345//346SYMCRYPT_STORE_MSBFIRST64( &count[ blockSize-8 ], 1 + SYMCRYPT_LOAD_MSBFIRST64( &count[ blockSize-8 ] ) );347348pbSrc += blockSize;349pbDst += blockSize;350}351352memcpy( pbChainingValue, count, blockSize );353354SymCryptWipeKnownSize( buf, sizeof( buf ));355}356357VOID358SYMCRYPT_CALL359SymCryptCfbEncrypt(360_In_ PCSYMCRYPT_BLOCKCIPHER pBlockCipher,361SIZE_T cbShift,362_In_ PCVOID pExpandedKey,363_Inout_updates_( pBlockCipher->blockSize )364PBYTE pbChainingValue,365_In_reads_( cbData ) PCBYTE pbSrc,366_Out_writes_( cbData ) PBYTE pbDst,367SIZE_T cbData )368//369// Encrypt a buffer using the CFB cipher mode.370//371// This implements the CFB mode using a 1-byte feedback shift.372// This requires a block cipher encryption call for each byte, which is very slow.373// Use of this cipher mode is not recommended.374//375// - pBlockCipher is a pointer to the block cipher description table.376// Suitable description tables for all ciphers in this library have been pre-defined.377// - pExpandedKey points to the expanded key to use. This generic function uses PVOID so there378// is no type safety to ensure that the expanded key and the encryption function match.379// - pbChainingValue points to the chaining value. On entry and exit it380// contains the last blockSize ciphertext bytes.381// - pbSrc is the input data buffer that will be encrypted/decrypted.382// - cbData. Number of bytes to encrypt/decrypt. This must be a multiple of the block size.383// - pbDst is the output buffer that receives the encrypted/decrypted data. The input and output384// buffers may be the same or non-overlapping, but may not partially overlap.385//386{387SYMCRYPT_ALIGN BYTE buf[2*SYMCRYPT_MAX_BLOCK_SIZE];388PBYTE chain = &buf[0];389PBYTE tmp = &buf[SYMCRYPT_MAX_BLOCK_SIZE];390SIZE_T blockSize;391392blockSize = pBlockCipher->blockSize;393SYMCRYPT_ASSERT( blockSize <= SYMCRYPT_MAX_BLOCK_SIZE );394395// Force cbShift to either be 1 or blockSize396if(cbShift != 1)397{398cbShift = blockSize;399}400401memcpy( chain, pbChainingValue, blockSize );402while( cbData >= cbShift )403{404(*pBlockCipher->encryptFunc)( pExpandedKey, chain, tmp );405SymCryptXorBytes( pbSrc, tmp, tmp, cbShift ); // tmp[0..cbShift-1] ^= pbSrc[0..cbShift-1]406memcpy( pbDst, tmp, cbShift );407408memmove( chain, chain + cbShift, blockSize - cbShift );409memcpy( chain + blockSize - cbShift, tmp, cbShift );410411pbDst += cbShift;412pbSrc += cbShift;413cbData -= cbShift;414}415416memcpy( pbChainingValue, chain, blockSize );417}418419420VOID421SYMCRYPT_CALL422SymCryptCfbDecrypt(423_In_ PCSYMCRYPT_BLOCKCIPHER pBlockCipher,424SIZE_T cbShift,425_In_ PCVOID pExpandedKey,426_Inout_updates_( pBlockCipher->blockSize )427PBYTE pbChainingValue,428_In_reads_( cbData ) PCBYTE pbSrc,429_Out_writes_( cbData ) PBYTE pbDst,430SIZE_T cbData )431{432SYMCRYPT_ALIGN BYTE buf[2*SYMCRYPT_MAX_BLOCK_SIZE];433PBYTE chain = &buf[0];434PBYTE tmp = &buf[SYMCRYPT_MAX_BLOCK_SIZE];435SIZE_T blockSize;436437blockSize = pBlockCipher->blockSize;438SYMCRYPT_ASSERT( blockSize <= SYMCRYPT_MAX_BLOCK_SIZE );439440// Force cbShift to either be 1 or blockSize441if(cbShift != 1)442{443cbShift = blockSize;444}445446memcpy( chain, pbChainingValue, blockSize );447while( cbData >= cbShift )448{449(*pBlockCipher->encryptFunc)( pExpandedKey, chain, tmp );450451//452// First we update the chain block453//454455memmove( chain, chain + cbShift, blockSize - cbShift );456memcpy( chain + blockSize - cbShift, pbSrc, cbShift );457458//459// To obey the read-once rule, we take the ciphertext from the updated chain block.460//461SymCryptXorBytes( chain + blockSize - cbShift, tmp, pbDst, cbShift );462463pbDst += cbShift;464pbSrc += cbShift;465cbData -= cbShift;466}467468memcpy( pbChainingValue, chain, blockSize );469}470471472