File: HMAC_iterative.cry

package info (click to toggle)
aws-crt-python 0.16.8%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 78,328 kB
  • sloc: ansic: 330,743; python: 18,949; makefile: 6,271; sh: 3,712; asm: 754; cpp: 699; ruby: 208; java: 77; perl: 73; javascript: 46; xml: 11
file content (399 lines) | stat: -rw-r--r-- 13,490 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
////////////////////////////////////////////////////////////////
// Copyright 2016 Galois, Inc. All Rights Reserved
//
// Authors:
//      Aaron Tomb : atomb@galois.com
//	Nathan Collins : conathan@galois.com
//      Joey Dodds : jdodds@galois.com
//
// Licensed under the Apache License, Version 2.0 (the "License").
// You may not use this file except in compliance with the License.
// A copy of the License is located at
//
//  http://aws.amazon.com/apache2.0
//
// or in the "license" file accompanying this file. This file is distributed
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
// express or implied. See the License for the specific language governing
// permissions and limitations under the License.
//
////////////////////////////////////////////////////////////////

module HMAC_iterative where

import Array
import SHA256
import Hashing

////////////////////////////////////////////////////////////////
// HMAC (specialized to SHA256).
////////////////////////////////////////////////////////////////

// s2n_hmac.h
/*
typedef enum { S2N_HMAC_NONE, S2N_HMAC_MD5, S2N_HMAC_SHA1, S2N_HMAC_SHA224, S2N_HMAC_SHA256, S2N_HMAC_SHA384,
    S2N_HMAC_SHA512, S2N_HMAC_SSLv3_MD5, S2N_HMAC_SSLv3_SHA1
} s2n_hmac_algorithm;
*/

S2N_HMAC_NONE       = 0:[32]
S2N_HMAC_MD5        = 1:[32]
S2N_HMAC_SHA1       = 2:[32]
S2N_HMAC_SHA224     = 3:[32]
S2N_HMAC_SHA256     = 4:[32]
S2N_HMAC_SHA384     = 5:[32]
S2N_HMAC_SHA512     = 6:[32]
S2N_HMAC_SSLv3_MD5  = 7:[32]
S2N_HMAC_SSLv3_SHA1 = 8:[32]

// s2n_hmac.h
/*

struct s2n_hmac_state {
    s2n_hmac_algorithm alg;

    uint16_t hash_block_size;
    uint32_t currently_in_hash_block;
    uint16_t block_size;
    uint8_t digest_size;

    struct s2n_hash_state inner;
    struct s2n_hash_state inner_just_key;
    struct s2n_hash_state outer;

    /* key needs to be as large as the biggest block size */
    uint8_t xor_pad[128];

    /* For storing the inner digest */
    uint8_t digest_pad[SHA512_DIGEST_LENGTH];
};
*/
// No surprises in the LLVM (:/src/hmac.ll)
/*
%struct.s2n_hmac_state = type { i32, i16, i32, i16, i8, %struct.s2n_hash_state, %struct.s2n_hash_state, %struct.s2n_hash_state, [128 x i8], [64 x i8] }
*/

type HMAC_c_state =
  { alg                     : [32]
  , hash_block_size         : [16]
  , currently_in_hash_block : [32]
  , block_size              : [16]
  , digest_size             : [8]
  , inner                   : SHA512_c_state
  , inner_just_key          : SHA512_c_state
  , outer                   : SHA512_c_state
  , outer_just_key          : SHA512_c_state
  , xor_pad                 : [128][8]
  , digest_pad              : [SHA512_DIGEST_LENGTH][8]
  }

////////////////////////////////////////////////////////////////
// Deep HMAC specs in terms of C HMAC state.
//
// Here "deep" because we reimplement the HMAC functions to call the
// corresponding hash functions on the raw C hash states, instead of
// converting them to Cryptol SHA256 hash states.

type ByteArray = Array[64][8]

type hash_init_ty =
  SHA512_c_state -> SHA512_c_state
type hash_update_ty msg_size =
  SHA512_c_state -> [msg_size][8] -> SHA512_c_state
type hash_digest_ty digest_size =
  SHA512_c_state -> [digest_size][8]

hash_init_c_state : hash_init_ty
hash_init_c_state = sha256_init_sha512_c_state
//hash_init_c_state = undefined

// Cryptol does not support polymorphic arguments (rank 2
// polymorphism), so making the hash update function a parameter to
// the hmac functions is annoying, since we must pass a separate
// monomorphic copy for each use at a different type. Instead, we just
// define it at the top level, but can leave it uninterpreted in the
// "generic" verification.
hash_update_c_state :
  {msg_size} (fin msg_size) => hash_update_ty msg_size
hash_update_c_state = sha256_update_sha512_c_state
//hash_update_c_state = undefined
hash_update_c_state_unbounded :
  SHA512_c_state -> ByteArray -> [32] -> SHA512_c_state
hash_update_c_state_unbounded = undefined


hash_digest_c_state :
  {digest_size} (fin digest_size) => hash_digest_ty digest_size
// To support any digest length, we pad and truncate as necessary.
// This implementation only makes sense for SHA256 size params, but
// that is not a concern since we leave these functions uninterpreted
// in the verification against the S2N C code (we want a concrete
// implementation for debugging and to compare with our functional spec).
hash_digest_c_state st =
  take `{digest_size}
    (sha256_digest_sha512_c_state st # (zero : [inf][8]))
//hash_digest_c_state = undefined


// Cases depending on key size:
//
// * small key (key size <= block size)
//
//   copy key into 'xor_pad', up to key size
//
// * large key (key size > block size)
//
//   update outer state with key to key size
//   digest outer state into digest pad at digest size
//
// The C code that follows the key init sets all remaining bytes,
// up to block size, to 0x36, and xors the other bytes with 0x36.
// This is equivalent to setting upper bytes (i.e. up to block size)
// to zero in key init, and then xoring everything with 0x36.
//
// We don't care about 'xor_pad' and 'outer', so we should be able to
// just ignore them. But for now we just compute them anyway.
key_init_c_state : { key_size, block_size, digest_size }
        ( fin key_size, fin block_size,
          SHA512_DIGEST_LENGTH >= digest_size )
     => SHA512_c_state
     -> [SHA512_DIGEST_LENGTH][8]
     -> [key_size][8]
     -> (SHA512_c_state, [SHA512_DIGEST_LENGTH][8], [block_size][8])
key_init_c_state outer0 digest_pad0 key =
  if `key_size > (`block_size : [max (width key_size) (width block_size)])
  then (outer2, digest_pad1, hash')
  else (outer1, digest_pad0, key')
  where
  outer1 = hash_init_c_state outer0

  // Long key.
  outer2 = hash_update_c_state outer1 key
  hash : [digest_size][8]
  hash = hash_digest_c_state outer2
  digest_pad1 = hash # drop `{digest_size} digest_pad0
  hash' = take `{block_size} (hash # (zero : [block_size][8]))

  // Short key.
  key' = take `{block_size} (key # (zero : [block_size][8]))

key_init_c_state_unbounded : { block_size, digest_size }
        ( 16 >= width block_size, SHA512_DIGEST_LENGTH >= digest_size )
     => SHA512_c_state
     -> [SHA512_DIGEST_LENGTH][8]
     -> ByteArray
     -> [32]
     -> (SHA512_c_state, [SHA512_DIGEST_LENGTH][8], [block_size][8])
key_init_c_state_unbounded outer0 digest_pad0 key klen =
  if klen > (`block_size : [32])
  then (outer2, digest_pad1, hash')
  else (outer1, digest_pad0, key')
  where
  outer1 = hash_init_c_state outer0

  // Long key.
  outer2 = hash_update_c_state_unbounded outer1 key klen
  hash : [digest_size][8]
  hash = hash_digest_c_state outer2
  digest_pad1 = hash # drop `{digest_size} digest_pad0
  hash' = take `{block_size} (hash # (zero : [block_size][8]))

  // Short key.
  key' = map
    (\i -> if (i <$ (0 # klen)) then (arrayLookup key i) else 0)
    (take`{block_size} [0 .. block_size])

hmac_init_c_state :
     { key_size, block_size, hash_block_size, digest_size }
     ( fin key_size
     , 64 >= width (8*key_size)
     , 16 >= width hash_block_size
     , 16 >= width block_size
     , 8 >= width digest_size
     , 128 >= block_size
     , 64 >= digest_size )
  => HMAC_c_state
  -> [32]
  -> [key_size][8]
  -> HMAC_c_state
hmac_init_c_state st0 alg key =
  { alg                     = alg
  , hash_block_size         = `hash_block_size
  , currently_in_hash_block = currently_in_hash_block
  , block_size              = `block_size
  , digest_size             = `digest_size

  , inner                   = inner
  , inner_just_key          = inner_just_key
  , outer                   = outer
  , outer_just_key          = outer_just_key
  , xor_pad                 = xor_pad
  , digest_pad              = digest_pad
  }
  where
    currently_in_hash_block = 0

    k0 : [block_size][8]
    (outer, digest_pad, k0) =
      key_init_c_state `{digest_size=digest_size} st0.outer st0.digest_pad key
    ikey = [ k ^ 0x36 | k <- k0 ]
    okey = [ k ^ 0x6a | k <- ikey ]

    inner_just_key = hash_update_c_state
      (hash_init_c_state st0.inner_just_key) ikey
    inner          = inner_just_key
    outer_just_key = hash_update_c_state
      (hash_init_c_state st0.outer_just_key) okey
    xor_pad = zero //okey # drop st0.xor_pad

hmac_init_c_state_unbounded :
     { block_size, hash_block_size, digest_size }
     ( 16 >= width hash_block_size
     , 16 >= width block_size
     , 8 >= width digest_size
     , 128 >= block_size
     , 64 >= digest_size )
  => HMAC_c_state
  -> [32]
  -> ByteArray
  -> [32]
  -> HMAC_c_state
hmac_init_c_state_unbounded st0 alg key klen =
  { alg                     = alg
  , hash_block_size         = `hash_block_size
  , currently_in_hash_block = currently_in_hash_block
  , block_size              = `block_size
  , digest_size             = `digest_size

  , inner                   = inner
  , inner_just_key          = inner_just_key
  , outer                   = outer
  , outer_just_key          = outer_just_key
  , xor_pad                 = xor_pad
  , digest_pad              = digest_pad
  }
  where
    currently_in_hash_block = 0

    k0 : [block_size][8]
    (outer, digest_pad, k0) =
      key_init_c_state_unbounded `{digest_size=digest_size}
        st0.outer st0.digest_pad key klen
    ikey = [ k ^ 0x36 | k <- k0 ]
    okey = [ k ^ 0x6a | k <- ikey ]

    inner_just_key = hash_update_c_state
      (hash_init_c_state st0.inner_just_key) ikey
    inner          = inner_just_key
    outer_just_key = hash_update_c_state
      (hash_init_c_state st0.outer_just_key) okey
    xor_pad = zero //okey # drop st0.xor_pad


hmac_update_c_state : {msg_size} (32 >= width msg_size) =>
  HMAC_c_state -> [msg_size][8] -> HMAC_c_state
hmac_update_c_state s m =
  { inner = hash_update_c_state s.inner m
  , currently_in_hash_block =
      (s.currently_in_hash_block + (`msg_size % (zero # s.hash_block_size))) %
      (zero # s.block_size)

  // Rest unchanged.
  , alg             = s.alg
  , hash_block_size = s.hash_block_size
  , block_size      = s.block_size
  , digest_size     = s.digest_size
  , inner_just_key  = s.inner_just_key
  , outer           = s.outer
  , outer_just_key  = s.outer_just_key
  , xor_pad         = s.xor_pad
  , digest_pad      = s.digest_pad
  }

hmac_update_c_state_unbounded :
  HMAC_c_state -> ByteArray -> [32] -> HMAC_c_state
hmac_update_c_state_unbounded s m sz =
  { inner = hash_update_c_state_unbounded s.inner m sz
  , currently_in_hash_block =
      (s.currently_in_hash_block + (sz % (zero # s.hash_block_size))) %
      (zero # s.block_size)

  // Rest unchanged.
  , alg             = s.alg
  , hash_block_size = s.hash_block_size
  , block_size      = s.block_size
  , digest_size     = s.digest_size
  , inner_just_key  = s.inner_just_key
  , outer           = s.outer
  , outer_just_key  = s.outer_just_key
  , xor_pad         = s.xor_pad
  , digest_pad      = s.digest_pad
  }


// TODO: What about `size` argument to `s2n_hmac_digest`? The `size`
// argument is supposed to be the "digest length" of the underlying
// hash. Here we are specializing to SHA256, so `size` would be 32.
hmac_digest_c_state :
     { block_size, digest_size }
     ( 64 >= digest_size
     , 128 >= block_size )
  => HMAC_c_state -> (HMAC_c_state, [8 * digest_size])
hmac_digest_c_state s = (sout, out)
  where
    hin : [digest_size][8]
    hin = hash_digest_c_state inner
    digest_pad : [SHA512_DIGEST_LENGTH][8]
    digest_pad = hin # zero

    okey : [block_size][8]
    okey = take s.xor_pad

    // The `inner` and `outer` here are probably not accurate:
    // in s2n, the `s2n_hash_digest` has been called on them, which
    // presumably can change them (calls `SHA256_Final` from
    // underlying C crypto lib behind the scenes). However, our
    // Cryptol `SHA256Final` does not change the hash state. Even
    // if we leave the hash function uninterpreted later, we will
    // still need to change the interface of our Cryptol `<hash>Final`
    // to additionally return an updated `<hash>State`.
    //
    // However, we probably don't need to say anything about the
    // *hash* state after calls to "final" functions, since
    // 's2n_hash_reset' just calls 's2n_hash_init'. So, the above
    // concern is not much of a concern after all.

    // Our goal is to leave the hash functions (init, update,
    // digest/final) uninterpreted, and so we need the structure of
    // our calls to these functions to match the structure of calls in
    // the s2n code, since we don't have lemmas giving algebraic
    // properties of these functions (e.g. here we'd need
    //
    //   update (update st x) y == update st (x # y)
    //
    // ). So, we replace
    //
    //outer = SHA256Update SHA256Init (okey # hin)
    //
    // with:
    outer = hash_update_c_state s.outer_just_key hin
    inner = s.inner

    out = join (hash_digest_c_state outer)

    sout : HMAC_c_state
    sout =
      { inner      = inner
      , outer      = s.outer_just_key
      , digest_pad = digest_pad

      // Rest unchanged.
      , alg                     = s.alg
      , hash_block_size         = s.hash_block_size
      , currently_in_hash_block = s.currently_in_hash_block
      , block_size              = s.block_size
      , digest_size             = s.digest_size
      , inner_just_key          = s.inner_just_key
      , outer_just_key          = s.outer_just_key
      , xor_pad                 = s.xor_pad
      }