mirror of
https://github.com/logos-storage/constantine.git
synced 2026-01-02 21:23:11 +00:00
106 lines
3.5 KiB
Nim
106 lines
3.5 KiB
Nim
# Constantine
|
|
# Copyright (c) 2018-2019 Status Research & Development GmbH
|
|
# Copyright (c) 2020-Present Mamy André-Ratsimbazafy
|
|
# Licensed and distributed under either of
|
|
# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT).
|
|
# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0).
|
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
|
|
|
import
|
|
../hashes,
|
|
../platforms/primitives
|
|
|
|
# HMAC: Keyed-Hashing for Message Authentication
|
|
# ----------------------------------------------
|
|
#
|
|
# https://datatracker.ietf.org/doc/html/rfc2104
|
|
#
|
|
# Test vectors:
|
|
# - https://datatracker.ietf.org/doc/html/rfc4231
|
|
# - https://csrc.nist.gov/projects/cryptographic-algorithm-validation-program
|
|
# - http://csrc.nist.gov/groups/STM/cavp/documents/mac/hmactestvectors.zip
|
|
|
|
{.push raises: [].} # No exceptions
|
|
|
|
type HMAC*[H: CryptoHash] = object
|
|
inner: H
|
|
outer: H
|
|
|
|
func init*[H: CryptoHash, T: char|byte](ctx: var HMAC[H], secretKey: openArray[T]) =
|
|
## Initialize a HMAC-based Message Authentication Code
|
|
## with a pre-shared secret key
|
|
## between the parties that want to authenticate messages between each other.
|
|
##
|
|
## Keys should be at least the same size as the hash function output size.
|
|
##
|
|
## Keys need to be chosen at random (or using a cryptographically strong
|
|
## pseudo-random generator seeded with a random seed), and periodically
|
|
## refreshed.
|
|
var key{.noInit.}: array[H.internalBlockSize(), byte]
|
|
if secretKey.len <= key.len:
|
|
copy(key, 0, secretKey, 0, secretKey.len)
|
|
for i in secretKey.len ..< key.len:
|
|
key[i] = byte 0
|
|
else:
|
|
ctx.inner.init()
|
|
ctx.inner.update(secretKey)
|
|
ctx.inner.finish(cast[ptr array[32, byte]](key.addr)[])
|
|
for i in H.digestSize() ..< key.len:
|
|
key[i] = byte 0
|
|
|
|
# Spec: inner hash
|
|
for i in 0 ..< H.internalBlockSize():
|
|
key[i] = key[i] xor byte 0x36
|
|
|
|
ctx.inner.init()
|
|
ctx.inner.update(key)
|
|
|
|
# Spec: outer hash (by cancelling previous xor)
|
|
for i in 0 ..< H.internalBlockSize():
|
|
key[i] = key[i] xor (byte 0x36 xor byte 0x5C)
|
|
|
|
ctx.outer.init()
|
|
ctx.outer.update(key)
|
|
|
|
func update*[H: CryptoHash, T: char|byte](ctx: var HMAC[H], message: openArray[T]) =
|
|
## Append a message to a HMAC authentication context.
|
|
## for incremental HMAC computation.
|
|
ctx.inner.update(message)
|
|
|
|
func finish*[H: CryptoHash, T: char|byte, N: static int](ctx: var HMAC[H], tag: var array[N, T]) =
|
|
## Finalize a HMAC authentication
|
|
## and output an authentication tag to the `tag` buffer
|
|
##
|
|
## Output may be used truncated, with the leftmost bits are kept.
|
|
## It is recommended that the tag length is at least half the length of the hash output
|
|
## and at least 80-bits.
|
|
static: doAssert N == H.digestSize()
|
|
ctx.inner.finish(tag)
|
|
ctx.outer.update(tag)
|
|
ctx.outer.finish(tag)
|
|
|
|
func clear*[H: CryptoHash](ctx: var HMAC[H]) =
|
|
## Clear the context internal buffers
|
|
# TODO: ensure compiler cannot optimize the code away
|
|
ctx.inner.clear()
|
|
ctx.outer.clear()
|
|
|
|
func mac*[T: char|byte, H: CryptoHash, N: static int](
|
|
Hash: type HMAC[H],
|
|
tag: var array[N, byte],
|
|
message: openArray[T],
|
|
secretKey: openarray[T],
|
|
clearMem = false) =
|
|
## Produce an authentication tag from a message
|
|
## and a preshared unique non-reused secret key
|
|
|
|
static: doAssert N == H.digestSize()
|
|
|
|
var ctx {.noInit.}: HMAC[H]
|
|
ctx.init(secretKey)
|
|
ctx.update(message)
|
|
ctx.finish(tag)
|
|
|
|
if clearMem:
|
|
ctx.clear()
|