Coverage Report

Created: 2024-11-21 07:03

/src/SymCrypt/lib/marvin32.c
Line
Count
Source (jump to first uncovered line)
1
//
2
// Marvin32.c
3
//
4
// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
5
//
6
7
//
8
// This module contains the routines to implement the Marvin32 checksum function
9
//
10
//
11
12
#include "precomp.h"
13
14
//
15
// See the symcrypt.h file for documentation on what the various functions do.
16
//
17
18
19
//
20
// Default initial seed, first 8 bytes of SHA256( "Marvin32" );
21
//
22
static const SYMCRYPT_MARVIN32_EXPANDED_SEED SymCryptMarvin32DefaultSeedStruct = {
23
    {0xcd0893b7, 0xd53cd9ce},
24
#if defined( SYMCRYPT_MAGIC_ENABLED )
25
    SYMCRYPT_MAGIC_VALUE( &SymCryptMarvin32DefaultSeedStruct ),
26
#endif
27
    };
28
29
PCSYMCRYPT_MARVIN32_EXPANDED_SEED const SymCryptMarvin32DefaultSeed = &SymCryptMarvin32DefaultSeedStruct;
30
31
//
32
// Round rotation amounts. This array is optimized away by the compiler
33
// as we inline all our rotations.
34
//
35
static const int rotate[4] = {
36
    20, 9, 27, 19,
37
};
38
39
40
SYMCRYPT_ERROR
41
SYMCRYPT_CALL
42
SymCryptMarvin32ExpandSeed(
43
    _Out_               PSYMCRYPT_MARVIN32_EXPANDED_SEED    pExpandedSeed,
44
    _In_reads_(cbSeed)  PCBYTE                              pbSeed,
45
                        SIZE_T                              cbSeed )
46
0
{
47
0
    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
48
49
0
    if( cbSeed != SYMCRYPT_MARVIN32_SEED_SIZE )
50
0
    {
51
0
        scError = SYMCRYPT_WRONG_KEY_SIZE;
52
0
        goto cleanup;
53
0
    }
54
0
    pExpandedSeed->s[0] = SYMCRYPT_LOAD_LSBFIRST32( pbSeed );
55
0
    pExpandedSeed->s[1] = SYMCRYPT_LOAD_LSBFIRST32( pbSeed + 4 );
56
57
0
    SYMCRYPT_SET_MAGIC( pExpandedSeed );
58
59
0
cleanup:
60
0
    return scError;
61
0
}
62
63
VOID
64
SYMCRYPT_CALL
65
SymCryptMarvin32SeedCopy(   _In_    PCSYMCRYPT_MARVIN32_EXPANDED_SEED   pSrc,
66
                            _Out_   PSYMCRYPT_MARVIN32_EXPANDED_SEED    pDst )
67
0
{
68
0
    SYMCRYPT_CHECK_MAGIC( pSrc );
69
0
    *pDst = *pSrc;
70
0
    SYMCRYPT_SET_MAGIC( pDst );
71
0
}
72
73
74
VOID
75
SYMCRYPT_CALL
76
SymCryptMarvin32StateCopy(
77
    _In_        PCSYMCRYPT_MARVIN32_STATE           pSrc,
78
    _In_opt_    PCSYMCRYPT_MARVIN32_EXPANDED_SEED   pExpandedSeed,
79
    _Out_       PSYMCRYPT_MARVIN32_STATE            pDst )
80
0
{
81
0
    SYMCRYPT_CHECK_MAGIC( pSrc );
82
0
    *pDst = *pSrc;
83
84
0
    if( pExpandedSeed == NULL )
85
0
    {
86
0
        SYMCRYPT_CHECK_MAGIC( pSrc->pSeed );
87
0
        pDst->pSeed = pSrc->pSeed;
88
0
    }
89
0
    else
90
0
    {
91
0
        SYMCRYPT_CHECK_MAGIC( pExpandedSeed );
92
0
        pDst->pSeed = pExpandedSeed;
93
0
    }
94
95
0
    SYMCRYPT_SET_MAGIC( pDst );
96
0
}
97
98
99
VOID
100
SYMCRYPT_CALL
101
SymCryptMarvin32Init(   _Out_   PSYMCRYPT_MARVIN32_STATE            pState,
102
                        _In_    PCSYMCRYPT_MARVIN32_EXPANDED_SEED   pExpandedSeed)
103
0
{
104
0
    pState->chain = *pExpandedSeed;
105
0
    pState->dataLength = 0;
106
0
    pState->pSeed = pExpandedSeed;
107
108
0
    *(UINT32 *) &pState->buffer[4] = 0; // wipe the last 4 bytes of the buffer.
109
110
0
    SYMCRYPT_SET_MAGIC( pState );
111
0
}
112
113
114
//
115
// SymCryptMarvin32Append
116
//
117
118
VOID
119
SYMCRYPT_CALL
120
SymCryptMarvin32Append(     _Inout_                 PSYMCRYPT_MARVIN32_STATE    state,
121
                            _In_reads_( cbData )    PCBYTE                      pbData,
122
                                                    SIZE_T                      cbData )
123
0
{
124
0
    UINT32 bytesInBuffer = state->dataLength;
125
126
0
    SYMCRYPT_CHECK_MAGIC( state );
127
128
0
    state->dataLength += (UINT32) cbData;    // We only keep track of the last 2 bits...
129
130
0
#define ALG MARVIN32
131
0
#define Alg Marvin32
132
0
#include "hash_buffer_pattern.c"
133
0
#undef ALG
134
0
#undef Alg
135
136
0
}
137
138
139
//
140
// SymCryptMarvin32Result
141
//
142
VOID
143
SYMCRYPT_CALL
144
SymCryptMarvin32Result(
145
     _Inout_                                        PSYMCRYPT_MARVIN32_STATE    pState,
146
     _Out_writes_( SYMCRYPT_MARVIN32_RESULT_SIZE )  PBYTE                       pbResult )
147
0
{
148
0
    SIZE_T bytesInBuffer = ( pState->dataLength) & 0x3;
149
150
0
    SYMCRYPT_CHECK_MAGIC( pState );
151
152
    //
153
    // Wipe four bytes in the buffer.
154
    // Doing this first ensures that this write is aligned when the input was of
155
    // length 0 mod 4.
156
    // The buffer is 8 bytes long, so we never overwrite anything else.
157
    //
158
0
    *(UINT32 *) &pState->buffer[bytesInBuffer] = 0;
159
160
    //
161
    // The buffer is never completely full, so we can always put the first
162
    // padding byte in.
163
    //
164
0
    pState->buffer[bytesInBuffer++] = 0x80;
165
166
    //
167
    // Process the final block
168
    //
169
0
    SymCryptMarvin32AppendBlocks( &pState->chain, pState->buffer, 8 );
170
171
0
    SYMCRYPT_STORE_LSBFIRST32( pbResult    , pState->chain.s[0] );
172
0
    SYMCRYPT_STORE_LSBFIRST32( pbResult + 4, pState->chain.s[1] );
173
174
    //
175
    // Wipe only those things that we need to wipe.
176
    //
177
178
0
    *(UINT32 *) &pState->buffer[0] = 0;
179
0
    pState->dataLength = 0;
180
181
0
    pState->chain = *pState->pSeed;
182
0
}
183
184
0
#define BLOCK( a, b ) \
185
0
{\
186
0
    b ^= a; a = ROL32( a, rotate[0] );\
187
0
    a += b; b = ROL32( b, rotate[1] );\
188
0
    b ^= a; a = ROL32( a, rotate[2] );\
189
0
    a += b; b = ROL32( b, rotate[3] );\
190
0
}
191
192
VOID
193
SYMCRYPT_CALL
194
SymCryptMarvin32AppendBlocks(
195
    _Inout_                 PSYMCRYPT_MARVIN32_CHAINING_STATE   pChain,
196
    _In_reads_( cbData )    PCBYTE                              pbData,
197
                            SIZE_T                              cbData )
198
0
{
199
0
    UINT32 s0 = pChain->s[0];
200
0
    UINT32 s1 = pChain->s[1];
201
202
0
    SIZE_T bytesInFirstBlock = cbData & 0xc;        // 0, 4, 8, or 12
203
204
0
    SYMCRYPT_ASSERT( (cbData & 3) == 0 );
205
206
207
0
    pbData += bytesInFirstBlock;
208
0
    cbData -= bytesInFirstBlock;
209
210
0
    switch( bytesInFirstBlock )
211
0
    {
212
0
    case 0: // This handles the cbData == 0 case too
213
0
        while( cbData > 0 )
214
0
        {
215
0
            pbData += 16;
216
0
            cbData -= 16;
217
218
0
            s0 += SYMCRYPT_LOAD_LSBFIRST32( pbData - 16 );
219
0
            BLOCK( s0, s1 );
220
0
    case 12:
221
0
            s0 += SYMCRYPT_LOAD_LSBFIRST32( pbData - 12 );
222
0
            BLOCK( s0, s1 );
223
0
    case 8:
224
0
            s0 += SYMCRYPT_LOAD_LSBFIRST32( pbData -  8 );
225
0
            BLOCK( s0, s1 );
226
0
    case 4:
227
0
            s0 += SYMCRYPT_LOAD_LSBFIRST32( pbData -  4 );
228
0
            BLOCK( s0, s1 );
229
0
        }
230
0
    }
231
232
0
    pChain->s[0] = s0;
233
0
    pChain->s[1] = s1;
234
0
}
235
236
237
VOID
238
SYMCRYPT_CALL
239
SymCryptMarvin32(
240
    _In_                                            PCSYMCRYPT_MARVIN32_EXPANDED_SEED   pExpandedSeed,
241
    _In_reads_( cbData )                            PCBYTE                              pbData,
242
                                                    SIZE_T                              cbData,
243
    _Out_writes_( SYMCRYPT_MARVIN32_RESULT_SIZE )   PBYTE                               pbResult )
244
//
245
// To reduce the per-computation overhead, we have a dedicated code here instead of the whole Init/Append/Result stuff.
246
//
247
0
{
248
0
    UINT32 tmp;
249
250
0
    UINT32 s0 = pExpandedSeed->s[0];
251
0
    UINT32 s1 = pExpandedSeed->s[1];
252
253
0
    while( cbData > 7 )
254
0
    {
255
0
        s0 += SYMCRYPT_LOAD_LSBFIRST32( pbData );
256
0
        BLOCK( s0, s1 );
257
0
        s0 += SYMCRYPT_LOAD_LSBFIRST32( pbData + 4 );
258
0
        BLOCK( s0, s1 );
259
0
        pbData += 8;
260
0
        cbData -= 8;
261
0
    }
262
263
    /*
264
    switch( cbData )
265
    {
266
    case 3:
267
        buf[2] = pbData[2];
268
    case 2:
269
        *(UINT16 *) &buf[0] = *(UINT16 *) pbData;
270
        break;
271
    case 1:
272
        buf[0] = pbData[0];
273
    case 0:
274
        ;
275
    }
276
277
    buf[ cbData ] = 0x80;
278
279
    s0 += LOAD_LSBFIRST32( buf );
280
    */
281
282
283
0
    switch( cbData )
284
0
    {
285
0
    default:
286
0
    case 4: s0 += SYMCRYPT_LOAD_LSBFIRST32( pbData ); BLOCK( s0, s1 ); pbData += 4;
287
0
    case 0: tmp = 0x80; break;
288
289
0
    case 5: s0 += SYMCRYPT_LOAD_LSBFIRST32( pbData ); BLOCK( s0, s1 ); pbData += 4;
290
0
    case 1: tmp = 0x8000 | pbData[0]; break;
291
292
0
    case 6: s0 += SYMCRYPT_LOAD_LSBFIRST32( pbData ); BLOCK( s0, s1 ); pbData += 4;
293
0
    case 2: tmp = 0x800000 | SYMCRYPT_LOAD_LSBFIRST16( pbData ); break;
294
295
0
    case 7: s0 += SYMCRYPT_LOAD_LSBFIRST32( pbData ); BLOCK( s0, s1 ); pbData += 4;
296
0
    case 3: tmp = SYMCRYPT_LOAD_LSBFIRST16( pbData ) | (pbData[2] << 16) | 0x80000000; break;
297
0
    }
298
0
    s0 += tmp;
299
300
301
0
    BLOCK( s0, s1 );
302
0
    BLOCK( s0, s1 );
303
304
0
    SYMCRYPT_STORE_LSBFIRST32( pbResult    , s0 );
305
0
    SYMCRYPT_STORE_LSBFIRST32( pbResult + 4, s1 );
306
0
}
307
308
309
310
//
311
// Simple test vector
312
//
313
314
static const BYTE   marvin32KATAnswer[ 8 ] = {
315
    0xbf, 0x69, 0x27, 0x49, 0x39, 0x43, 0xc7, 0x22,
316
} ;
317
318
VOID
319
SYMCRYPT_CALL
320
SymCryptMarvin32Selftest(void)
321
0
{
322
0
    BYTE res[SYMCRYPT_MARVIN32_RESULT_SIZE];
323
324
0
    SymCryptMarvin32( SymCryptMarvin32DefaultSeed, SymCryptTestMsg3, sizeof( SymCryptTestMsg3 ), res );
325
326
0
    SymCryptInjectError( res, sizeof( res ) );
327
0
    if( memcmp( res, marvin32KATAnswer, sizeof( res ) ) != 0 )
328
0
    {
329
0
        SymCryptFatal( 'marv' );
330
0
    }
331
0
}
332