diff options
Diffstat (limited to 'src/vm/marvin32.cpp')
-rw-r--r-- | src/vm/marvin32.cpp | 266 |
1 files changed, 266 insertions, 0 deletions
diff --git a/src/vm/marvin32.cpp b/src/vm/marvin32.cpp new file mode 100644 index 0000000000..4fadee4580 --- /dev/null +++ b/src/vm/marvin32.cpp @@ -0,0 +1,266 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// +// This module contains the routines to implement the Marvin32 checksum function +// +// + +#include "common.h" +#include "marvin32.h" + +// +// See the symcrypt.h file for documentation on what the various functions do. +// + +// +// Round rotation amounts. This array is optimized away by the compiler +// as we inline all our rotations. +// +static const int rotate[4] = { + 20, 9, 27, 19, +}; + + +#define ROL32( x, n ) _rotl( (x), (n) ) +#define ROR32( x, n ) _rotr( (x), (n) ) + +#define BLOCK( a, b ) \ +{\ + b ^= a; a = ROL32( a, rotate[0] );\ + a += b; b = ROL32( b, rotate[1] );\ + b ^= a; a = ROL32( a, rotate[2] );\ + a += b; b = ROL32( b, rotate[3] );\ +} + + + +HRESULT +SymCryptMarvin32ExpandSeed( + __out PSYMCRYPT_MARVIN32_EXPANDED_SEED pExpandedSeed, + __in_ecount(cbSeed) PCBYTE pbSeed, + SIZE_T cbSeed ) +{ + HRESULT retVal = S_OK; + + if( cbSeed != SYMCRYPT_MARVIN32_SEED_SIZE ) + { + retVal =E_INVALIDARG; + goto cleanup; + } + pExpandedSeed->s[0] = LOAD_LSBFIRST32( pbSeed ); + pExpandedSeed->s[1] = LOAD_LSBFIRST32( pbSeed + 4 ); + +cleanup: + return retVal; +} + + +VOID +SymCryptMarvin32Init( _Out_ PSYMCRYPT_MARVIN32_STATE pState, + _In_ PCSYMCRYPT_MARVIN32_EXPANDED_SEED pExpandedSeed) +{ + pState->chain = *pExpandedSeed; + pState->dataLength = 0; + pState->pSeed = pExpandedSeed; + + *(ULONG *) &pState->buffer[4] = 0; // wipe the last 4 bytes of the buffer. +} + +VOID +SymCryptMarvin32AppendBlocks( + _Inout_ PSYMCRYPT_MARVIN32_CHAINING_STATE pChain, + _In_reads_( cbData ) PCBYTE pbData, + SIZE_T cbData ) +{ + ULONG s0 = pChain->s[0]; + ULONG s1 = pChain->s[1]; + + SIZE_T bytesInFirstBlock = cbData & 0xc; // 0, 4, 8, or 12 + + pbData += bytesInFirstBlock; + cbData -= bytesInFirstBlock; + + switch( bytesInFirstBlock ) + { + case 0: // This handles the cbData == 0 case too + while( cbData > 0 ) + { + pbData += 16; + cbData -= 16; + + s0 += LOAD_LSBFIRST32( pbData - 16 ); + BLOCK( s0, s1 ); + case 12: + s0 += LOAD_LSBFIRST32( pbData - 12 ); + BLOCK( s0, s1 ); + case 8: + s0 += LOAD_LSBFIRST32( pbData - 8 ); + BLOCK( s0, s1 ); + case 4: + s0 += LOAD_LSBFIRST32( pbData - 4 ); + BLOCK( s0, s1 ); + } + } + + pChain->s[0] = s0; + pChain->s[1] = s1; +} + +VOID +SymCryptMarvin32Append(_Inout_ SYMCRYPT_MARVIN32_STATE * state, +_In_reads_bytes_(cbData) PCBYTE pbData, +SIZE_T cbData) +{ + ULONG bytesInBuffer = state->dataLength; + + state->dataLength += (ULONG)cbData; // We only keep track of the last 2 bits... + + // + // Truncate bytesInBuffer so that we never have an integer overflow. + // + bytesInBuffer &= SYMCRYPT_MARVIN32_INPUT_BLOCK_SIZE - 1; + + // + // If previous data in buffer, buffer new input and transform if possible. + // + if (bytesInBuffer > 0) + { + SIZE_T freeInBuffer = SYMCRYPT_MARVIN32_INPUT_BLOCK_SIZE - bytesInBuffer; + if (cbData < freeInBuffer) + { + // + // All the data will fit in the buffer. + // We don't do anything here. + // As cbData < INPUT_BLOCK_SIZE the bulk data processing is skipped, + // and the data will be copied to the buffer at the end + // of this code. + } + else { + // + // Enough data to fill the whole buffer & process it + // + memcpy(&state->buffer[bytesInBuffer], pbData, freeInBuffer); + pbData += freeInBuffer; + cbData -= freeInBuffer; + SymCryptMarvin32AppendBlocks(&state->chain, state->buffer, SYMCRYPT_MARVIN32_INPUT_BLOCK_SIZE); + + // + // Set bytesInBuffer to zero to ensure that the trailing data in the + // buffer will be copied to the right location of the buffer below. + // + bytesInBuffer = 0; + } + } + + // + // Internal buffer is empty; process all remaining whole blocks in the input + // + if (cbData >= SYMCRYPT_MARVIN32_INPUT_BLOCK_SIZE) + { + SIZE_T cbDataRoundedDown = cbData & ~(SIZE_T)(SYMCRYPT_MARVIN32_INPUT_BLOCK_SIZE - 1); + SymCryptMarvin32AppendBlocks(&state->chain, pbData, cbDataRoundedDown); + pbData += cbDataRoundedDown; + cbData -= cbDataRoundedDown; + } + + // + // buffer remaining input if necessary. + // + if (cbData > 0) + { + memcpy(&state->buffer[bytesInBuffer], pbData, cbData); + } + +} + +VOID +SymCryptMarvin32Result( + _Inout_ PSYMCRYPT_MARVIN32_STATE pState, + _Out_writes_( SYMCRYPT_MARVIN32_RESULT_SIZE ) PBYTE pbResult ) +{ + SIZE_T bytesInBuffer = ( pState->dataLength) & 0x3; + + // + // Wipe four bytes in the buffer. + // Doing this first ensures that this write is aligned when the input was of + // length 0 mod 4. + // The buffer is 8 bytes long, so we never overwrite anything else. + // + *(ULONG *) &pState->buffer[bytesInBuffer] = 0; + + // + // The buffer is never completely full, so we can always put the first + // padding byte in. + // + pState->buffer[bytesInBuffer++] = 0x80; + + // + // Process the final block + // + SymCryptMarvin32AppendBlocks( &pState->chain, pState->buffer, 8 ); + + STORE_LSBFIRST32( pbResult , pState->chain.s[0] ); + STORE_LSBFIRST32( pbResult + 4, pState->chain.s[1] ); + + // + // Wipe only those things that we need to wipe. + // + + *(ULONG *) &pState->buffer[0] = 0; + pState->dataLength = 0; + + pState->chain = *pState->pSeed; +} + + +VOID +SymCryptMarvin32( + __in PCSYMCRYPT_MARVIN32_EXPANDED_SEED pExpandedSeed, + __in_ecount(cbData) PCBYTE pbData, + SIZE_T cbData, + __out_ecount(SYMCRYPT_MARVIN32_RESULT_SIZE) PBYTE pbResult) +// +// To reduce the per-computation overhead, we have a dedicated code here instead of the whole Init/Append/Result stuff. +// +{ + ULONG tmp; + + ULONG s0 = pExpandedSeed->s[0]; + ULONG s1 = pExpandedSeed->s[1]; + + while( cbData > 7 ) + { + s0 += LOAD_LSBFIRST32( pbData ); + BLOCK( s0, s1 ); + s0 += LOAD_LSBFIRST32( pbData + 4 ); + BLOCK( s0, s1 ); + pbData += 8; + cbData -= 8; + } + + switch( cbData ) + { + default: + case 4: s0 += LOAD_LSBFIRST32( pbData ); BLOCK( s0, s1 ); pbData += 4; + case 0: tmp = 0x80; break; + + case 5: s0 += LOAD_LSBFIRST32( pbData ); BLOCK( s0, s1 ); pbData += 4; + case 1: tmp = 0x8000 | pbData[0]; break; + + case 6: s0 += LOAD_LSBFIRST32( pbData ); BLOCK( s0, s1 ); pbData += 4; + case 2: tmp = 0x800000 | LOAD_LSBFIRST16( pbData ); break; + + case 7: s0 += LOAD_LSBFIRST32( pbData ); BLOCK( s0, s1 ); pbData += 4; + case 3: tmp = LOAD_LSBFIRST16( pbData ) | (pbData[2] << 16) | 0x80000000; break; + } + s0 += tmp; + + + BLOCK( s0, s1 ); + BLOCK( s0, s1 ); + + STORE_LSBFIRST32( pbResult , s0 ); + STORE_LSBFIRST32( pbResult + 4, s1 ); +} |