# # # NimCrypto # (c) Copyright 2016 Eugene Kabanov # # See the file "LICENSE", included in this # distribution, for details about the copyright. # ## This module implements HMAC (Keyed-Hashing for Message Authentication) ## [http://www.ietf.org/rfc/rfc2104.txt]. ## ## Module provides two common interfaces for calculating HMAC. ## ``Classic`` method allows to process big chunks of data using limited amount ## of memory, while ``one-line`` method allows you to perform HMAC calculation ## in one line of code. ## ## .. code-block::nim ## import nimcrypto ## ## ## ``Classic`` method of HMAC calculation. ## ## var stringToHmac = "Hello World!" ## var stringHmacKey = "AliceKey" ## let ptrToHmac = cast[ptr byte](addr stringToHmac[0]) ## let ptrHmacKey = cast[ptr byte](addr stringHmacKey[0]) ## let toHmacLen = uint(len(stringToHmac)) ## let hmacKeyLen = uint(len(stringHmacKey)) ## ## # Declare context objects ## var hctx1, hctx2: HMAC[sha256] ## # Initalize HMAC[SHA256] contexts with key `AliceKey`. ## hctx1.init(stringHmacKey) ## hctx2.init(ptrHmacKey, hmacKeyLen) ## # Update HMAC[SHA256] context using data `Hello World!` twice. ## hctx1.update(stringToHmac) ## hctx1.update(stringToHmac) ## # Update HMAC[SHA256] context using data `Hello World!` twice. ## hctx2.update(ptrToHmac, toHmacLen) ## hctx2.update(ptrToHmac, toHmacLen) ## # Print HMAC[SHA256] digest. ## echo $hctx1.finish() ## echo $hctx2.finish() ## # Do not forget to clear contexts. ## hctx1.clear() ## hctx2.clear() ## ## ## ``One-line`` method of HMAC calculation. ## ## # Print HMAC[SHA256] digest of `Hello World!Hello World!` using key ## # `AliceKey`. ## echo $sha256.hmac(stringHmacKey, stringToHmac & stringToHmac) ## ## # Output to stdout must be 3 equal digests: ## # 18AF7C8586141A47EAAD416C2B356431D001FAFF3B8C98C80AA108DC971B230D ## # 18AF7C8586141A47EAAD416C2B356431D001FAFF3B8C98C80AA108DC971B230D ## # 18AF7C8586141A47EAAD416C2B356431D001FAFF3B8C98C80AA108DC971B230D import hash, utils from sha2 import Sha2Context from ripemd import RipemdContext from keccak import KeccakContext from blake2 import Blake2Context {.deadCodeElim:on.} const MaxHmacBlockSize = 256 type HMAC*[HashType] = object ## HMAC context object. mdctx: HashType opadctx: HashType template sizeBlock*(h: HMAC[Sha2Context]): uint = ## Size of processing block in octets (bytes), while perform HMAC ## operation using SHA2 algorithms. cast[uint](h.HashType.sizeBlock) template sizeBlock*(h: HMAC[RipemdContext]): uint = ## Size of processing block in octets (bytes), while perform HMAC ## operation using RIPEMD algorithms. cast[uint](h.HashType.sizeBlock) template sizeBlock*(h: HMAC[KeccakContext]): uint = ## Size of processing block in octets (bytes), while perform HMAC ## operation using KECCAK/SHA3/SHAKE algorithms. when h.HashType.kind == Keccak or h.HashType.kind == Sha3: when h.HashType.bits == 224: 144'u elif h.HashType.bits == 256: 136'u elif h.HashType.bits == 384: 104'u elif h.HashType.bits == 512: 72'u else: {.fatal: "Choosen hash primitive is not yet supported!".} else: {.fatal: "Choosen hash primitive is not yet supported!".} template sizeBlock*(h: HMAC[Blake2Context]): uint = ## Size of processing block in octets (bytes), while perform HMAC ## operation using BLAKE2b/BLAKE2s algorithms. cast[uint](h.HashType.sizeBlock) template sizeDigest*(h: HMAC[Sha2Context]): uint = ## Size of HMAC digest in octets (bytes) using SHA2 algorithms. cast[uint](h.mdctx.sizeDigest) template sizeDigest*(h: HMAC[RipemdContext]): uint = ## Size of HMAC digest in octets (bytes) using RIPEMD algorithms. cast[uint](h.mdctx.sizeDigest) template sizeDigest*(h: HMAC[KeccakContext]): uint = ## Size of HMAC digest in octets (bytes) using KECCAK/SHA3/SHAKE ## algorithms. cast[uint](h.mdctx.sizeDigest) template sizeDigest*(h: HMAC[Blake2Context]): uint = ## Size of HMAC digest in octets (bytes) using BLAKE2b/BLAKE2s algorithms. cast[uint](h.mdctx.sizeDigest) proc init*[T](hmctx: var HMAC[T], key: ptr byte, ulen: uint) = ## Initialize HMAC context ``hmctx`` with key using ``key`` and size ``ulen``. ## ## ``key`` can be ``nil``. mixin init, update, finish var k: array[MaxHmacBlockSize, byte] var ipad: array[MaxHmacBlockSize, byte] var opad: array[MaxHmacBlockSize, byte] const sizeBlock = hmctx.sizeBlock hmctx.mdctx = T() hmctx.opadctx = T() init(hmctx.opadctx) if not isNil(key): if ulen > sizeBlock: init(hmctx.mdctx) update(hmctx.mdctx, key, ulen) discard finish(hmctx.mdctx, addr k[0], sizeBlock) else: if ulen > 0'u: copyMem(addr k[0], key, ulen) for i in 0..= hmctx.sizeDigest) result = finish(hmctx, cast[ptr byte](addr data[0]), ulen) proc finish*(hmctx: var HMAC): MDigest[hmctx.HashType.bits] = ## Finalize HMAC context ``hmctx`` and return calculated digest as ``MDigest`` ## object. discard finish(hmctx, cast[ptr byte](addr result.data[0]), cast[uint](len(result.data))) proc hmac*(HashType: typedesc, key: ptr byte, klen: uint, data: ptr byte, ulen: uint): MDigest[HashType.bits] = ## Perform HMAC computation with hash algorithm ``HashType`` using key ``key`` ## of length ``klen`` on data buffer pointed by ``data`` of length ``ulen``. ## ## .. code-block::nim ## import nimcrypto ## ## var stringToHmac = "Hello World!" ## var stringHmacKey = "AliceKey" ## let data = cast[ptr byte](addr stringToHmac[0]) ## let datalen = uint(len(stringToHmac)) ## let key = cast[ptr byte](addr stringHmacKey[0]) ## let keylen = uint(len(stringHmacKey)) ## # Print HMAC[SHA256](key = "AliceKey", data = "Hello World!") ## echo sha256.hmac(key, keylen, data, datalen) ## # Print HMAC[SHA512](key = "AliceKey", data = "Hello World!") ## echo sha512.hmac(key, keylen, data, datalen) ## # Print HMAC[KECCAK256](key = "AliceKey", data = "Hello World!") ## echo keccak256.hmac(key, keylen, data, datalen) ## # Print HMAC[RIPEMD160](key = "AliceKey", data = "Hello World!") ## echo ripemd160.hmac(key, keylen, data, datalen) var ctx: HMAC[HashType] ctx.init(key, klen) ctx.update(data, ulen) result = ctx.finish() ctx.clear() proc hmac*[A, B](HashType: typedesc, key: openarray[A], data: openarray[B], ostart: int = 0, ofinish: int = -1): MDigest[HashType.bits] = ## Perform HMAC computation with hash algorithm ``HashType`` using key ``key`` ## of data ``data``, in slice ``[ostart, ofinish]``, both ``ostart`` and ## ``ofinish`` are inclusive. ## ## .. code-block::nim ## import nimcrypto ## ## var stringToHmac = "Hello World!" ## var stringHmacKey = "AliceKey" ## # Print HMAC[SHA256] digest of whole string `Hello World!` using ## # key `AliceKey`. ## echo sha256.hmac(stringHmacKey, stringToHmac) ## # Print HMAC[SHA256] digest of `Hello` using key `AliceKey`. ## echo sha256.hmac(stringHmacKey, stringToHmac, ofinish = 4) ## # Print HMAC[SHA256] digest of `World!` using key `AliceKey`. ## echo sha256.hmac(stringHmacKey, stringToHmac, ostart = 6) ## # Print HMAC[SHA256] digest of constant `Hello` using constant key ## # `AliceKey`. ## echo sha256.hmac("AliceKey", "Hello") ## # Print HMAC[SHA256] digest of constant `World!` using constant key ## # `AliceKey` ## echo sha256.hmac("AliceKey", "World!") var ctx: HMAC[HashType] let so = if ostart < 0: (len(data) + ostart) else: ostart let eo = if ofinish < 0: (len(data) + ofinish) else: ofinish let length = (eo - so + 1) * sizeof(B) if len(key) == 0: ctx.init(nil, 0) else: ctx.init(cast[ptr byte](unsafeAddr key[0]), cast[uint](sizeof(A) * len(key))) if length <= 0: result = ctx.finish() else: ctx.update(cast[ptr byte](unsafeAddr data[so]), cast[uint](length)) result = ctx.finish() ctx.clear()