1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
6 // This module contains the routines to implement the Marvin32 checksum function
14 // See the symcrypt.h file for documentation on what the various functions do.
18 // Round rotation amounts. This array is optimized away by the compiler
19 // as we inline all our rotations.
21 static const int rotate[4] = {
26 #define ROL32( x, n ) _rotl( (x), (n) )
27 #define ROR32( x, n ) _rotr( (x), (n) )
29 #define BLOCK( a, b ) \
31 b ^= a; a = ROL32( a, rotate[0] );\
32 a += b; b = ROL32( b, rotate[1] );\
33 b ^= a; a = ROL32( a, rotate[2] );\
34 a += b; b = ROL32( b, rotate[3] );\
40 SymCryptMarvin32ExpandSeed(
41 __out PSYMCRYPT_MARVIN32_EXPANDED_SEED pExpandedSeed,
42 __in_ecount(cbSeed) PCBYTE pbSeed,
45 HRESULT retVal = S_OK;
47 if( cbSeed != SYMCRYPT_MARVIN32_SEED_SIZE )
52 pExpandedSeed->s[0] = LOAD_LSBFIRST32( pbSeed );
53 pExpandedSeed->s[1] = LOAD_LSBFIRST32( pbSeed + 4 );
61 SymCryptMarvin32Init( _Out_ PSYMCRYPT_MARVIN32_STATE pState,
62 _In_ PCSYMCRYPT_MARVIN32_EXPANDED_SEED pExpandedSeed)
64 pState->chain = *pExpandedSeed;
65 pState->dataLength = 0;
66 pState->pSeed = pExpandedSeed;
68 *(ULONG *) &pState->buffer[4] = 0; // wipe the last 4 bytes of the buffer.
72 SymCryptMarvin32AppendBlocks(
73 _Inout_ PSYMCRYPT_MARVIN32_CHAINING_STATE pChain,
74 _In_reads_( cbData ) PCBYTE pbData,
77 ULONG s0 = pChain->s[0];
78 ULONG s1 = pChain->s[1];
80 SIZE_T bytesInFirstBlock = cbData & 0xc; // 0, 4, 8, or 12
82 pbData += bytesInFirstBlock;
83 cbData -= bytesInFirstBlock;
85 switch( bytesInFirstBlock )
87 case 0: // This handles the cbData == 0 case too
93 s0 += LOAD_LSBFIRST32( pbData - 16 );
96 s0 += LOAD_LSBFIRST32( pbData - 12 );
99 s0 += LOAD_LSBFIRST32( pbData - 8 );
102 s0 += LOAD_LSBFIRST32( pbData - 4 );
112 SymCryptMarvin32Append(_Inout_ SYMCRYPT_MARVIN32_STATE * state,
113 _In_reads_bytes_(cbData) PCBYTE pbData,
116 ULONG bytesInBuffer = state->dataLength;
118 state->dataLength += (ULONG)cbData; // We only keep track of the last 2 bits...
121 // Truncate bytesInBuffer so that we never have an integer overflow.
123 bytesInBuffer &= SYMCRYPT_MARVIN32_INPUT_BLOCK_SIZE - 1;
126 // If previous data in buffer, buffer new input and transform if possible.
128 if (bytesInBuffer > 0)
130 SIZE_T freeInBuffer = SYMCRYPT_MARVIN32_INPUT_BLOCK_SIZE - bytesInBuffer;
131 if (cbData < freeInBuffer)
134 // All the data will fit in the buffer.
135 // We don't do anything here.
136 // As cbData < INPUT_BLOCK_SIZE the bulk data processing is skipped,
137 // and the data will be copied to the buffer at the end
142 // Enough data to fill the whole buffer & process it
144 memcpy(&state->buffer[bytesInBuffer], pbData, freeInBuffer);
145 pbData += freeInBuffer;
146 cbData -= freeInBuffer;
147 SymCryptMarvin32AppendBlocks(&state->chain, state->buffer, SYMCRYPT_MARVIN32_INPUT_BLOCK_SIZE);
150 // Set bytesInBuffer to zero to ensure that the trailing data in the
151 // buffer will be copied to the right location of the buffer below.
158 // Internal buffer is empty; process all remaining whole blocks in the input
160 if (cbData >= SYMCRYPT_MARVIN32_INPUT_BLOCK_SIZE)
162 SIZE_T cbDataRoundedDown = cbData & ~(SIZE_T)(SYMCRYPT_MARVIN32_INPUT_BLOCK_SIZE - 1);
163 SymCryptMarvin32AppendBlocks(&state->chain, pbData, cbDataRoundedDown);
164 pbData += cbDataRoundedDown;
165 cbData -= cbDataRoundedDown;
169 // buffer remaining input if necessary.
173 memcpy(&state->buffer[bytesInBuffer], pbData, cbData);
179 SymCryptMarvin32Result(
180 _Inout_ PSYMCRYPT_MARVIN32_STATE pState,
181 _Out_writes_( SYMCRYPT_MARVIN32_RESULT_SIZE ) PBYTE pbResult )
183 SIZE_T bytesInBuffer = ( pState->dataLength) & 0x3;
186 // Wipe four bytes in the buffer.
187 // Doing this first ensures that this write is aligned when the input was of
189 // The buffer is 8 bytes long, so we never overwrite anything else.
191 *(ULONG *) &pState->buffer[bytesInBuffer] = 0;
194 // The buffer is never completely full, so we can always put the first
197 pState->buffer[bytesInBuffer++] = 0x80;
200 // Process the final block
202 SymCryptMarvin32AppendBlocks( &pState->chain, pState->buffer, 8 );
204 STORE_LSBFIRST32( pbResult , pState->chain.s[0] );
205 STORE_LSBFIRST32( pbResult + 4, pState->chain.s[1] );
208 // Wipe only those things that we need to wipe.
211 *(ULONG *) &pState->buffer[0] = 0;
212 pState->dataLength = 0;
214 pState->chain = *pState->pSeed;
220 __in PCSYMCRYPT_MARVIN32_EXPANDED_SEED pExpandedSeed,
221 __in_ecount(cbData) PCBYTE pbData,
223 __out_ecount(SYMCRYPT_MARVIN32_RESULT_SIZE) PBYTE pbResult)
225 // To reduce the per-computation overhead, we have a dedicated code here instead of the whole Init/Append/Result stuff.
230 ULONG s0 = pExpandedSeed->s[0];
231 ULONG s1 = pExpandedSeed->s[1];
235 s0 += LOAD_LSBFIRST32( pbData );
237 s0 += LOAD_LSBFIRST32( pbData + 4 );
246 case 4: s0 += LOAD_LSBFIRST32( pbData ); BLOCK( s0, s1 ); pbData += 4;
247 case 0: tmp = 0x80; break;
249 case 5: s0 += LOAD_LSBFIRST32( pbData ); BLOCK( s0, s1 ); pbData += 4;
250 case 1: tmp = 0x8000 | pbData[0]; break;
252 case 6: s0 += LOAD_LSBFIRST32( pbData ); BLOCK( s0, s1 ); pbData += 4;
253 case 2: tmp = 0x800000 | LOAD_LSBFIRST16( pbData ); break;
255 case 7: s0 += LOAD_LSBFIRST32( pbData ); BLOCK( s0, s1 ); pbData += 4;
256 case 3: tmp = LOAD_LSBFIRST16( pbData ) | (pbData[2] << 16) | 0x80000000; break;
264 STORE_LSBFIRST32( pbResult , s0 );
265 STORE_LSBFIRST32( pbResult + 4, s1 );