summaryrefslogtreecommitdiff
path: root/src/vm/marvin32.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/vm/marvin32.cpp')
-rw-r--r--src/vm/marvin32.cpp266
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 );
+}