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
}
|