diff --git a/benchmarks/bench_fp_double_precision.nim b/benchmarks/bench_fp_double_precision.nim index f03e109..49d1167 100644 --- a/benchmarks/bench_fp_double_precision.nim +++ b/benchmarks/bench_fp_double_precision.nim @@ -217,6 +217,15 @@ proc reduce2x*(T: typedesc, iters: int) = bench("Redc 2x", $T & " <- " & $doublePrec(T), iters): r.redc2x(t) +proc reduce2xViaDivision*(T: typedesc, iters: int) = + + const bits2x = 2 * T.C.getCurveBitWidth() + var r: matchingBigInt(T.C) + let t = rng.random_unsafe(BigInt[bits2x]) + + bench("Reduction via division", $T & " <- " & $doublePrec(T), iters): + r.reduce(t, T.fieldMod()) + proc main() = separator() sum(Fp[BLS12_381], iters = 10_000_000) @@ -237,6 +246,8 @@ proc main() = square2xBench(768, 384, iters = 10_000_000) reduce2x(Fp[BN254_Snarks], iters = 10_000_000) reduce2x(Fp[BLS12_381], iters = 10_000_000) + reduce2xViaDivision(Fp[BN254_Snarks], iters = 10_000) + reduce2xViaDivision(Fp[BLS12_381], iters = 10_000) separator() main() diff --git a/benchmarks/bench_poly1305.nim b/benchmarks/bench_poly1305.nim index dbe3e60..dbde5e0 100644 --- a/benchmarks/bench_poly1305.nim +++ b/benchmarks/bench_poly1305.nim @@ -34,7 +34,7 @@ proc benchPoly1305_constantine[T](msg: openarray[T], msgComment: string, iters: 0x4a, 0xbf, 0xf6, 0xaf, 0x41, 0x49, 0xf5, 0x1b ] bench("Poly1305 - Constantine - " & msgComment, msg.len, iters): - poly1305.auth(tag, msg, ikm) + poly1305.mac(tag, msg, ikm) when isMainModule: proc main() = diff --git a/constantine.nimble b/constantine.nimble index 7b363c3..cc56572 100644 --- a/constantine.nimble +++ b/constantine.nimble @@ -205,11 +205,17 @@ const testDesc: seq[tuple[path: string, useGMP: bool]] = @[ # Message Authentication Code # ---------------------------------------------------------- ("tests/t_mac_poly1305.nim", false), + ("tests/t_mac_hmac_sha256.nim", false), + + # KDF + # ---------------------------------------------------------- + ("tests/t_kdf_hkdf.nim", false), # Protocols # ---------------------------------------------------------- ("tests/t_ethereum_evm_precompiles.nim", false), ("tests/t_blssig_pop_on_bls12381_g2.nim", false), + ("tests/t_ethereum_eip2333_bls12381_key_derivation.nim", false), ] # For temporary (hopefully) investigation that can only be reproduced in CI @@ -228,7 +234,11 @@ const skipSanitizers = [ "tests/math/t_ec_sage_bls12_381.nim", "tests/t_hash_to_field.nim", "tests/t_hash_to_curve.nim", - "tests/t_hash_to_curve_random.nim" + "tests/t_hash_to_curve_random.nim", + "tests/t_mac_poly1305.nim", + "tests/t_mac_hmac.nim", + "tests/t_kdf_hkdf.nim", + "tests/t_ethereum_eip2333_bls12381_key_derivation" ] when defined(windows): diff --git a/constantine/ethereum_eip2333_bls12381_key_derivation.nim b/constantine/ethereum_eip2333_bls12381_key_derivation.nim new file mode 100644 index 0000000..32105b0 --- /dev/null +++ b/constantine/ethereum_eip2333_bls12381_key_derivation.nim @@ -0,0 +1,177 @@ +# 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, + ./kdf/kdf_hkdf, + ./math/config/[curves, type_ff], + ./math/arithmetic/[bigints, limbs_montgomery], + ./math/io/io_bigints, + ./platforms/endians + +# EIP2333: BLS12-381 Key Generation +# ------------------------------------------------------------ +# +# https://eips.ethereum.org/EIPS/eip-2333 + +{.push raises: [].} # No exceptions + +type SecretKey = matchingOrderBigInt(BLS12_381) + +func hkdf_mod_r[T: char|byte](secretKey: var SecretKey, ikm: openArray[byte], key_info: openArray[T]) = + ## Ethereum 2 EIP-2333, extracts this from the BLS signature schemes + # 1. salt = "BLS-SIG-KEYGEN-SALT-" + # 2. SK = 0 + # 3. while SK == 0: + # 4. salt = H(salt) + # 5. PRK = HKDF-Extract(salt, IKM || I2OSP(0, 1)) + # 6. OKM = HKDF-Expand(PRK, key_info || I2OSP(L, 2), L) + # 7. SK = OS2IP(OKM) mod r + # 8. return SK + const salt0 = "BLS-SIG-KEYGEN-SALT-" + var ctx{.noInit.}: HKDF[sha256] + var prk{.noInit.}: array[sha256.digestSize(), byte] + + var salt {.noInit.}: array[sha256.digestSize(), byte] + sha256.hash(salt, salt0) + + while true: + # 5. PRK = HKDF-Extract("BLS-SIG-KEYGEN-SALT-", IKM || I2OSP(0, 1)) + ctx.hkdf_extract_init(salt, ikm) + ctx.hkdf_extract_append_to_IKM([byte 0]) + ctx.hkdf_extract_finish(prk) + # curve order r = 52435875175126190479447740508185965837690552500527637822603658699938581184513 + # const L = ceil((1.5 * ceil(log2(r))) / 8) = 48 + # https://www.wolframalpha.com/input/?i=ceil%28%281.5+*+ceil%28log2%2852435875175126190479447740508185965837690552500527637822603658699938581184513%29%29%29+%2F+8%29 + # 6. OKM = HKDF-Expand(PRK, key_info || I2OSP(L, 2), L) + const L = 48 + var okm{.noInit.}: array[L, byte] + const L_octetstring = L.uint16.toBytesBE() + ctx.hkdfExpand(okm, prk, key_info, append = L_octetstring) + # 7. x = OS2IP(OKM) mod r + # We reduce mod r via Montgomery reduction, instead of bigint division + # as constant-time division works bits by bits (384 bits) while + # Montgomery reduction works word by word, quadratically so 6*6 = 36 on 64-bit CPUs. + # With R ≡ (2^WordBitWidth)^numWords (mod M) + # redc2xMont(a) computes a/R + # mulMont(a, b) computes a.b.R⁻¹ + var seckeyDbl{.noInit.}: BigInt[2 * BLS12_381.getCurveOrderBitWidth()] + seckeyDbl.unmarshal(okm, bigEndian) + # secretKey.reduce(seckeyDbl, BLS12_381.getCurveOrder()) + secretKey.limbs.redc2xMont(seckeyDbl.limbs, # seckey/R + BLS12_381.getCurveOrder().limbs, Fr[BLS12_381].getNegInvModWord(), + Fr[BLS12_381].getSpareBits()) + secretKey.limbs.mulMont(secretKey.limbs, Fr[BLS12_381].getR2modP().limbs, # (seckey/R) * R² * R⁻¹ = seckey + BLS12_381.getCurveOrder().limbs, Fr[BLS12_381].getNegInvModWord(), + Fr[BLS12_381].getSpareBits()) + + if bool secretKey.isZero(): + # Chance of 2⁻²⁵⁶ to happen + sha256.hash(salt, salt) + else: + return + +iterator ikm_to_lamport_SK( + lamportSecretKeyChunk: var array[32, byte], + ikm: array[32, byte], salt: array[4, byte]): int = + ## Generate a Lamport secret key + ## This uses an iterator to stream HKDF + ## instead of allocating 255*32 bytes ~= 8KB + var ctx{.noInit.}: HKDF[sha256] + var prk{.noInit.}: array[32, byte] + + # 0. PRK = HKDF-Extract(salt, IKM) + ctx.hkdfExtract(prk, salt, ikm) + + # 1. OKM = HKDF-Expand(PRK, "" , L) + # with L = K * 255 and K = 32 (sha256 output) + {.push checks: off.} # No OverflowError or IndexError allowed + for i in ctx.hkdfExpandChunk( + lamportSecretKeyChunk, + prk, "",""): + yield i + +func parent_SK_to_lamport_PK( + lamportPublicKey: var array[32, byte], + parentSecretKey: SecretKey, + index: uint32) = + ## Derives the index'th child's lamport PublicKey + ## from the parent SecretKey + + # 0. salt = I2OSP(index, 4) + let salt{.noInit.} = index.toBytesBE() + + # 1. IKM = I2OSP(parent_SK, 32) + var ikm {.noinit.}: array[32, byte] + ikm.marshal(parentSecretKey, bigEndian) + + # Reorganized the spec to save on stack allocations + # by reusing buffers and using streaming HKDF + + # 5. lamport_PK = "" + var ctx{.noInit.}: sha256 + ctx.init() + + var tmp{.noInit.}, chunk{.noInit.}: array[32, byte] + + {.push checks: off.} # No OverflowError or IndexError allowed + + # 2. lamport_0 = IKM_to_lamport_SK(IKM, salt) + # 6. for i = 1, .., 255 (inclusive) + # lamport_PK = lamport_PK | SHA256(lamport_0[i]) + for i in ikm_to_lamport_SK(chunk, ikm, salt): + sha256.hash(tmp, chunk) + ctx.update(tmp) + if i == 254: + # We iterate from 0 + break + + # 3. not_IKM = flip_bits(parent_SK) + for i in 0 ..< 32: + ikm[i] = not ikm[i] + + # 4. lamport_1 = IKM_to_lamport_SK(not_IKM, salt) + # 7. for i = 1, .., 255 (inclusive) + # lamport_PK = lamport_PK | SHA256(lamport_1[i]) + for i in ikm_to_lamport_SK(chunk, ikm, salt): + sha256.hash(tmp, chunk) + ctx.update(tmp) + if i == 254: + # We iterate from 0 + break + + # 8. compressed_lamport_PK = SHA256(lamport_PK) + # 9. return compressed_lamport_PK + ctx.finish(lamportPublicKey) + +func derive_child_secretKey*( + childSecretKey: var SecretKey, + parentSecretKey: SecretKey, + index: uint32 + ): bool = + ## EIP2333 Child Key derivation function + var compressed_lamport_PK{.noInit.}: array[32, byte] + # 0. compressed_lamport_PK = parent_SK_to_lamport_PK(parent_SK, index) + parent_SK_to_lamport_PK( + compressed_lamport_PK, + parentSecretKey, + index, + ) + childSecretKey.hkdf_mod_r(compressed_lamport_PK, key_info = "") + return true + +func derive_master_secretKey*( + masterSecretKey: var SecretKey, + ikm: openArray[byte] + ): bool = + ## EIP2333 Master key derivation + if ikm.len < 32: + return false + + masterSecretKey.hkdf_mod_r(ikm, key_info = "") + return true \ No newline at end of file diff --git a/constantine/hash_to_curve/h2c_hash_to_field.nim b/constantine/hash_to_curve/h2c_hash_to_field.nim index 492bea4..841552a 100644 --- a/constantine/hash_to_curve/h2c_hash_to_field.nim +++ b/constantine/hash_to_curve/h2c_hash_to_field.nim @@ -116,7 +116,7 @@ func expandMessageXMD*[B1, B2, B3: byte|char, len_in_bytes: static int]( let ell = ceilDiv(output.len.uint, DigestSize.uint) const zPad = default(array[BlockSize, byte]) var l_i_b_str0 {.noInit.}: array[3, byte] - l_i_b_str0.asBytesBE(output.len.uint16, pos = 0) + l_i_b_str0.dumpRawInt(output.len.uint16, cursor = 0, bigEndian) l_i_b_str0[2] = 0 var b0 {.noinit, align: DigestSize.}: array[DigestSize, byte] diff --git a/constantine/hashes/h_sha256.nim b/constantine/hashes/h_sha256.nim index 8c39e39..231776b 100644 --- a/constantine/hashes/h_sha256.nim +++ b/constantine/hashes/h_sha256.nim @@ -276,11 +276,11 @@ func hashBuffer(ctx: var Sha256Context) = # Public API # ---------------------------------------------------------------- -func digestSize*(H: type sha256): int = +template digestSize*(H: type sha256): int = ## Returns the output size in bytes 32 -func internalBlockSize*(H: type sha256): int = +template internalBlockSize*(H: type sha256): int = ## Returns the byte size of the hash function ingested blocks 64 diff --git a/constantine/kdf/README.txt b/constantine/kdf/README.txt new file mode 100644 index 0000000..f7408bb --- /dev/null +++ b/constantine/kdf/README.txt @@ -0,0 +1,11 @@ +# Key Derivation Functions + +Note: + +We prefix the filename with "kdf" to prevents name collision between a modulename and the function +which leads to confusing error messages + +```Nim +# in kdf_hkdf +func hkdf*(...) +``` \ No newline at end of file diff --git a/constantine/kdf/kdf_hkdf.nim b/constantine/kdf/kdf_hkdf.nim new file mode 100644 index 0000000..c7012c5 --- /dev/null +++ b/constantine/kdf/kdf_hkdf.nim @@ -0,0 +1,200 @@ +# 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, + ../mac/mac_hmac, + ../platforms/primitives + +# HMAC-based Extract-and-Expand Key Derivation Function (HKDF) +# ------------------------------------------------------------ +# +# https://datatracker.ietf.org/doc/html/rfc5869 + +{.push raises: [].} # No exceptions + +type HKDF*[H: CryptoHash] = object + hmac: HMAC[H] + +func hkdf_extract_init*[H: CryptoHash, S, I: char|byte]( + ctx: var HKDF[H], + salt: openArray[S], + ikm: openArray[I]) {.inline.}= + ctx.hmac.init(salt) + ctx.hmac.update(ikm) + +func hkdf_extract_append_to_IKM*[H: CryptoHash, T: char|byte]( + ctx: var HKDF[H], append: openArray[T]) {.inline.} = + ctx.hmac.update(append) + +func hkdf_extract_finish*[H: CryptoHash, N: static int]( + ctx: var HKDF[H], prk: var array[N, byte]) {.inline.} = + ## Allows appending to IKM without allocating it on the heap + static: doAssert H.digestSize == N + ctx.hmac.finish(prk) + +func hkdfExtract*[H: CryptoHash;S,I: char|byte, N: static int]( + ctx: var HKDF[H], + prk: var array[N, byte], + salt: openArray[S], + ikm: openArray[I]) {.inline.} = + ## "Extract" step of HKDF. + ## Extract a fixed size pseudom-random key + ## from an optional salt value + ## and a secret input keying material. + ## + ## Inputs: + ## - salt: a buffer to an optional salt value (set to nil if unused) + ## - ikm: "input keying material", the secret value to hash. + ## If a protocol needs to append to the IKM, it is recommended + ## to use the: + ## hkdf_extract_init, + ## hkdf_extract_append_to_IKM + ## hkdf_extract_finish + ## to avoid allocating and exposing secrets to the heap. + ## + ## Output: + ## - prk: a pseudo random key of fixed size. The size is the same as the cryptographic hash chosen. + ## + ## Temporary: + ## - ctx: a HMAC["cryptographic-hash"] context, for example HMAC[sha256]. + + static: doAssert N == H.digestSize() + + ctx.hkdf_extract_init(salt, ikm) + ctx.hkdf_extract_finish(prk) + +iterator hkdfExpandChunk*[H: CryptoHash; N: static int; I, A: char|byte]( + ctx: var HKDF[H], + chunk: var array[N, byte], + prk: array[N, byte], + info: openArray[I], + append: openArray[A]): int = + ## "Expand" step of HKDF, with an iterator with up to 255 iterations. + ## + ## Note: The output MUST be at most 255 iterations as per RFC5869 + ## https://datatracker.ietf.org/doc/html/rfc5869 + ## + ## Expand a fixed size pseudo random-key + ## into several pseudo-random keys + ## + ## Inputs: + ## - prk: a pseudo random key (PRK) of fixed size. The size is the same as the cryptographic hash chosen. + ## - info: optional context and application specific information (set to nil if unused) + ## - append: + ## Compared to the spec we add a specific append procedure to do + ## OKM = HKDF-Expand(PRK, key_info || I2OSP(L, 2), L) + ## without having additional allocation on the heap + ## Input/Output: + ## - chunk: + ## In: OKMᵢ₋₁ (output keying material chunk i-1) + ## Out: OKMᵢ (output keying material chunk i). + ## + ## Output: + ## - returns the current chunk number i + ## + ## Temporary: + ## - ctx: a HMAC["cryptographic-hash"] context, for example HMAC[sha256]. + + const HashLen = H.digestSize() + static: doAssert N == HashLen + + {.push checks: off.} # No OverflowError or IndexError allowed + for i in 0 ..< 255: + ctx.hmac.init(prk) + # T(0) = empty string + if i != 0: + ctx.hmac.update(chunk) + ctx.hmac.update(info) + ctx.hmac.update(append) + ctx.hmac.update([uint8(i)+1]) # For byte 255, this append "0" and not "256" + ctx.hmac.finish(chunk) + + yield i + +func hkdfExpand*[H: CryptoHash; K: static int; I, A: char|byte]( + ctx: var HKDF[H], + output: var openArray[byte], + prk: array[K, byte], + info: openArray[I], + append: openArray[A]) = + ## "Expand" step of HKDF + ## Expand a fixed size pseudo random-key + ## into several pseudo-random keys + ## + ## Inputs: + ## - prk: a pseudo random key (PRK) of fixed size. The size is the same as the cryptographic hash chosen. + ## - info: optional context and application specific information (set to nil if unused) + ## - append: + ## Compared to the spec we add a specific append procedure to do + ## OKM = HKDF-Expand(PRK, key_info || I2OSP(L, 2), L) + ## without having additional allocation on the heap + ## Output: + ## - output: OKM (output keying material). The PRK is expanded to match + ## the output length, the result is stored in output. + ## + ## Temporary: + ## - ctx: a HMAC["cryptographic-hash"] context, for example HMAC[sha256]. + + const HashLen = H.digestSize() + static: doAssert K == HashLen + + debug: + doAssert output.len <= 255*HashLen + + var t{.noInit.}: array[HashLen, byte] + + {.push checks: off.} # No OverflowError or IndexError allowed + for i in ctx.hkdfExpandChunk(t, prk, info, append): + let iStart = i * HashLen + let size = min(HashLen, output.len - iStart) + copy(output, iStart, t, 0, size) + + if iStart+HashLen >= output.len: + break + + # ctx.clear() - TODO: very expensive + +func hkdfExpand*[H: CryptoHash; K: static int; I: char|byte]( + ctx: var HKDF[H], + output: var openArray[byte], + prk: array[K, byte], + info: openArray[I]) {.inline.} = + ## "Expand" step of HKDF + ## Expand a fixed size pseudo random-key + ## into several pseudo-random keys + ## + ## Inputs: + ## - prk: a pseudo random key (PRK) of fixed size. The size is the same as the cryptographic hash chosen. + ## - info: optional context and application specific information (set to nil if unused) + ## Output: + ## - output: OKM (output keying material). The PRK is expanded to match + ## the output length, the result is stored in output. + ## + ## Temporary: + ## - ctx: a HMAC["cryptographic-hash"] context, for example HMAC[sha256]. + hkdfExpand(ctx, output, prk, info, default(array[0, byte])) + +func hkdf*[H: CryptoHash, N: static int, O, S, K, I: char|byte]( + Hash: typedesc[H], + output: var openArray[O], + salt: openArray[S], + ikm: openArray[K], + info: openArray[I]) {.inline.} = + ## HKDF + ## Inputs: + ## - A hash function, with an output digest length HashLen + ## - An opttional salt value (non-secret random value), if not provided, + ## it is set to an array of HashLen zero bytes + ## - A secret Input Keying Material + ## - info: an optional context and application specific information for domain separation + ## it can be an empty string + var ctx{.noInit.}: HMAC[H] + var prk{.noInit.}: array[H.digestSize(), byte] + ctx.hkdfExtract(prk, salt, ikm) + ctx.hkdfExpand(output, prk, info) diff --git a/constantine/mac/README.md b/constantine/mac/README.md new file mode 100644 index 0000000..d6b9192 --- /dev/null +++ b/constantine/mac/README.md @@ -0,0 +1,11 @@ +# Message Authentication Codes + +Note: + +We prefix the filename with "mac" to prevents name collision between a modulename and the types +which leads to confusing error messages + +```Nim +# in mac_poly1305 +type poly1305* = Poly1305_CTX +``` \ No newline at end of file diff --git a/constantine/mac/mac_hmac.nim b/constantine/mac/mac_hmac.nim new file mode 100644 index 0000000..d7eacf6 --- /dev/null +++ b/constantine/mac/mac_hmac.nim @@ -0,0 +1,105 @@ +# 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() diff --git a/constantine/mac/mac_poly1305.nim b/constantine/mac/mac_poly1305.nim index 45811be..31ab5ee 100644 --- a/constantine/mac/mac_poly1305.nim +++ b/constantine/mac/mac_poly1305.nim @@ -328,7 +328,7 @@ func clear*(ctx: var Poly1305_CTX) = ctx.msgLen = 0 ctx.bufIdx = 0 -func auth*[T: char|byte]( +func mac*[T: char|byte]( _: type poly1305, tag: var array[16, byte], message: openArray[T], @@ -345,11 +345,11 @@ func auth*[T: char|byte]( if clearMem: ctx.clear() -func auth*[T: char|byte]( +func mac*[T: char|byte]( _: type poly1305, message: openArray[T], nonReusedKey: array[32, byte], clearMem = false): array[16, byte]{.noInit.}= ## Produce an authentication tag from a message ## and a preshared unique non-reused secret key - poly1305.auth(result, message, nonReusedKey, clearMem) + poly1305.mac(result, message, nonReusedKey, clearMem) diff --git a/constantine/platforms/endians.nim b/constantine/platforms/endians.nim index 4640af4..b9e4d8d 100644 --- a/constantine/platforms/endians.nim +++ b/constantine/platforms/endians.nim @@ -76,13 +76,9 @@ func dumpRawInt*[T: byte|char]( for i in 0'u ..< L: dst[cursor+i] = toByte(src shr ((L-i-1) * 8)) -func asBytesBE*[N: static int]( - r: var array[N, byte], - num: SomeUnsignedInt, - pos: static int) {.inline.}= +func toBytesBE*(num: SomeUnsignedInt): array[sizeof(num), byte] {.noInit, inline.}= ## Store an integer into an array of bytes ## in big endian representation const L = sizeof(num) - static: doAssert N - (pos + L) >= 0 for i in 0 ..< L: - r[i] = toByte(num shr ((L-1-i) * 8)) + result[i] = toByte(num shr ((L-1-i) * 8)) \ No newline at end of file diff --git a/constantine/platforms/primitives.nim b/constantine/platforms/primitives.nim index 06cef87..b6ee9b3 100644 --- a/constantine/platforms/primitives.nim +++ b/constantine/platforms/primitives.nim @@ -54,8 +54,8 @@ func setZero*[N](a: var array[N, SomeNumber]){.inline.} = for i in 0 ..< a.len: a[i] = 0 -func copy*[N: static int, T: byte|char]( - dst: var array[N, byte], +func copy*[T: byte|char]( + dst: var openArray[byte], dStart: SomeInteger, src: openArray[T], sStart: SomeInteger, @@ -69,5 +69,6 @@ func copy*[N: static int, T: byte|char]( doAssert 0 <= dStart and dStart+len <= dst.len.uint, "dStart: " & $dStart & ", dStart+len: " & $(dStart+len) & ", dst.len: " & $dst.len doAssert 0 <= sStart and sStart+len <= src.len.uint, "sStart: " & $sStart & ", sStart+len: " & $(sStart+len) & ", src.len: " & $src.len + {.push checks: off.} # No OverflowError or IndexError allowed for i in 0 ..< len: dst[dStart + i] = byte src[sStart + i] \ No newline at end of file diff --git a/helpers/prng_unsafe.nim b/helpers/prng_unsafe.nim index ef8af1b..be95c33 100644 --- a/helpers/prng_unsafe.nim +++ b/helpers/prng_unsafe.nim @@ -9,6 +9,7 @@ import ../constantine/platforms/abstractions, ../constantine/math/arithmetic, + ../constantine/math/arithmetic/limbs_montgomery, ../constantine/math/config/curves, ../constantine/math/elliptic/[ ec_shortweierstrass_affine, @@ -135,20 +136,38 @@ func sample_unsafe*[T](rng: var RngState, src: openarray[T]): T = # - A bias is a result that is consistently off from the true value i.e. # a deviation of an estimate from the quantity under observation +func reduceViaMont[N: static int, F](reduced: var array[N, SecretWord], unreduced: array[2*N, SecretWord], _: typedesc[F]) = + # reduced.reduce(unreduced, FF.fieldMod()) + # ---------------------------------------- + # With R ≡ (2^WordBitWidth)^numWords (mod p) + # redc2xMont(a) computes a/R (mod p) + # mulMont(a, b) computes a.b.R⁻¹ (mod p) + # Hence with b = R², this computes a (mod p). + # Montgomery reduction works word by word, quadratically + # so for 384-bit prime (6-words on 64-bit CPUs), so 6*6 = 36, twice for multiplication and reduction + # significantly faster than division which works bit-by-bit due to constant-time requirement + reduced.redc2xMont(unreduced, # a/R + F.fieldMod().limbs, F.getNegInvModWord(), + F.getSpareBits()) + reduced.mulMont(reduced, F.getR2modP().limbs, # (a/R) * R² * R⁻¹ ≡ a (mod p) + F.fieldMod().limbs, F.getNegInvModWord(), + F.getSpareBits()) + +func random_unsafe(rng: var RngState, a: var Limbs) = + ## Initialize standalone limbs + for i in 0 ..< a.len: + a[i] = SecretWord(rng.next()) + func random_unsafe(rng: var RngState, a: var BigInt) = ## Initialize a standalone BigInt - for i in 0 ..< a.limbs.len: - a.limbs[i] = SecretWord(rng.next()) + rng.random_unsafe(a.limbs) func random_unsafe(rng: var RngState, a: var FF) = ## Initialize a Field element ## Unsafe: for testing and benchmarking purposes only - var reduced, unreduced{.noInit.}: typeof(a.mres) + var unreduced{.noinit.}: Limbs[2*a.mres.limbs.len] rng.random_unsafe(unreduced) - - # Note: a simple modulo will be biaised but it's simple and "fast" - reduced.reduce(unreduced, FF.fieldMod()) - a.mres.getMont(reduced, FF.fieldMod(), FF.getR2modP(), FF.getNegInvModWord(), FF.getSpareBits()) + a.mres.limbs.reduceViaMont(unreduced, FF) func random_unsafe(rng: var RngState, a: var ExtensionField) = ## Recursively initialize an extension Field element @@ -162,24 +181,27 @@ func random_word_highHammingWeight(rng: var RngState): BaseType = for _ in 0 ..< numZeros: result = result.clearBit rng.random_unsafe(WordBitWidth) +func random_highHammingWeight(rng: var RngState, a: var Limbs) = + ## Initialize standalone limbs + ## with high Hamming weight + ## to have a higher probability of triggering carries + for i in 0 ..< a.len: + a[i] = SecretWord rng.random_word_highHammingWeight() + func random_highHammingWeight(rng: var RngState, a: var BigInt) = ## Initialize a standalone BigInt ## with high Hamming weight ## to have a higher probability of triggering carries - for i in 0 ..< a.limbs.len: - a.limbs[i] = SecretWord rng.random_word_highHammingWeight() + rng.random_highHammingWeight(a.limbs) func random_highHammingWeight(rng: var RngState, a: var FF) = ## Recursively initialize a BigInt (part of a field) or Field element ## Unsafe: for testing and benchmarking purposes only ## The result will have a high Hamming Weight ## to have a higher probability of triggering carries - var reduced, unreduced{.noInit.}: typeof(a.mres) + var unreduced{.noinit.}: Limbs[2*a.mres.limbs.len] rng.random_highHammingWeight(unreduced) - - # Note: a simple modulo will be biaised but it's simple and "fast" - reduced.reduce(unreduced, FF.fieldMod()) - a.mres.getMont(reduced, FF.fieldMod(), FF.getR2modP(), FF.getNegInvModWord(), FF.getSpareBits()) + a.mres.limbs.reduceViaMont(unreduced, FF) func random_highHammingWeight(rng: var RngState, a: var ExtensionField) = ## Recursively initialize an extension Field element @@ -215,16 +237,23 @@ func random_long01Seq(rng: var RngState, a: var BigInt) = else: a.unmarshal(buf, littleEndian) +func random_long01Seq(rng: var RngState, a: var Limbs) = + ## Initialize standalone limbs + ## It is skewed towards producing strings of 1111... and 0000 + ## to trigger edge cases + const bits = a.len*WordBitWidth + + var t{.noInit.}: BigInt[bits] + rng.random_long01Seq(t) + a = t.limbs + func random_long01Seq(rng: var RngState, a: var FF) = ## Recursively initialize a BigInt (part of a field) or Field element ## It is skewed towards producing strings of 1111... and 0000 ## to trigger edge cases - var reduced, unreduced{.noInit.}: typeof(a.mres) + var unreduced{.noinit.}: Limbs[2*a.mres.limbs.len] rng.random_long01Seq(unreduced) - - # Note: a simple modulo will be biaised but it's simple and "fast" - reduced.reduce(unreduced, FF.fieldMod()) - a.mres.getMont(reduced, FF.fieldMod(), FF.getR2modP(), FF.getNegInvModWord(), FF.getSpareBits()) + a.mres.limbs.reduceViaMont(unreduced, FF) func random_long01Seq(rng: var RngState, a: var ExtensionField) = ## Recursively initialize an extension Field element diff --git a/tests/t_ethereum_eip2333_bls12381_key_derivation.nim b/tests/t_ethereum_eip2333_bls12381_key_derivation.nim new file mode 100644 index 0000000..a228b1f --- /dev/null +++ b/tests/t_ethereum_eip2333_bls12381_key_derivation.nim @@ -0,0 +1,123 @@ +# Nim-BLSCurve +# Copyright (c) 2018-Present Status Research & Development GmbH +# Licensed under either of +# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE)) +# * MIT license ([LICENSE-MIT](LICENSE-MIT)) +# at your option. +# This file may not be copied, modified, or distributed except according to +# those terms. + +import + std/unittest, + ../constantine/ethereum_eip2333_bls12381_key_derivation, + ../constantine/math/io/io_bigints, + ../constantine/math/config/curves, + ../constantine/math/arithmetic/bigints, + ../constantine/platforms/abstractions + +type SecretKey = matchingOrderBigInt(BLS12_381) + +proc toBytes(hex: string): seq[byte] = + doAssert (hex.len and 1) == 0, "Input hex must have an even number of characters" + let length = hex.len shr 1 - int(hex[0] == '0' and (hex[1] in {'x', 'X'})) + + result.newSeq(length) + hex.hexToPaddedByteArray(result, bigEndian) + +proc test0 = + let seed = toBytes"0xc55257c360c07c72029aebc1b53c05ed0362ada38ead3e3e9efa3708e53495531f09a6987599d18264c1e1c92f2cf141630c7a3c4ab7c81b2f001698e7463b04" + let expectedMaster = "6083874454709270928345386274498605044986640685124978867557563392430687146096" + let child_index = 0'u32 + let expectedChild = "20397789859736650942317412262472558107875392172444076792671091975210932703118" + + var master: SecretKey + let ok0 = master.derive_master_secretKey(seed) + doAssert ok0 + + var eMaster: SecretKey + doAssert bool eMaster.fromDecimal(expectedMaster) + doAssert bool(master == eMaster) + + var child: SecretKey + let ok1 = child.derive_child_secretKey(master, child_index) + doAssert ok1 + + var eChild: SecretKey + doAssert bool eChild.fromDecimal(expectedChild) + doAssert bool(child == eChild) + +proc test1 = + let seed = toBytes"0x3141592653589793238462643383279502884197169399375105820974944592" + let expectedMaster = "29757020647961307431480504535336562678282505419141012933316116377660817309383" + let child_index = 3141592653'u32 + let expectedChild = "25457201688850691947727629385191704516744796114925897962676248250929345014287" + + var master: SecretKey + let ok0 = master.derive_master_secretKey(seed) + doAssert ok0 + + var eMaster: SecretKey + doAssert bool eMaster.fromDecimal(expectedMaster) + doAssert bool(master == eMaster) + + var child: SecretKey + let ok1 = child.derive_child_secretKey(master, child_index) + doAssert ok1 + + var eChild: SecretKey + doAssert bool eChild.fromDecimal(expectedChild) + doAssert bool(child == eChild) + +proc test2 = + let seed = toBytes"0x0099FF991111002299DD7744EE3355BBDD8844115566CC55663355668888CC00" + let expectedMaster = "27580842291869792442942448775674722299803720648445448686099262467207037398656" + let child_index = 4294967295'u32 + let expectedChild = "29358610794459428860402234341874281240803786294062035874021252734817515685787" + + var master: SecretKey + let ok0 = master.derive_master_secretKey(seed) + doAssert ok0 + + var eMaster: SecretKey + doAssert bool eMaster.fromDecimal(expectedMaster) + doAssert bool(master == eMaster) + + var child: SecretKey + let ok1 = child.derive_child_secretKey(master, child_index) + doAssert ok1 + + var eChild: SecretKey + doAssert bool eChild.fromDecimal(expectedChild) + doAssert bool(child == eChild) + +proc test3 = + let seed = toBytes"0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3" + let expectedMaster = "19022158461524446591288038168518313374041767046816487870552872741050760015818" + let child_index = 42'u32 + let expectedChild = "31372231650479070279774297061823572166496564838472787488249775572789064611981" + + var master: SecretKey + let ok0 = master.derive_master_secretKey(seed) + doAssert ok0 + + var eMaster: SecretKey + doAssert bool eMaster.fromDecimal(expectedMaster) + doAssert bool(master == eMaster) + + var child: SecretKey + let ok1 = child.derive_child_secretKey(master, child_index) + doAssert ok1 + + var eChild: SecretKey + doAssert bool eChild.fromDecimal(expectedChild) + doAssert bool(child == eChild) + +suite "Key Derivation (EIP-2333)": + test "Test 0": + test0() + test "Test 1": + test1() + test "Test 2": + test2() + test "Test 3": + test3() diff --git a/tests/t_kdf_hkdf.nim b/tests/t_kdf_hkdf.nim new file mode 100644 index 0000000..3691f54 --- /dev/null +++ b/tests/t_kdf_hkdf.nim @@ -0,0 +1,109 @@ +# 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 + stew/byteutils, + ../constantine/[hashes, mac/mac_hmac, kdf/kdf_hkdf] + +proc hexToBytes(s: string): seq[byte] = + if s.len != 0: return hexToSeqByte(s) + +template test(id, constants: untyped) = + proc `test _ id`() = + # We create a proc to avoid allocating too much globals. + constants + + let + bikm = hexToBytes(IKM) + bsalt = hexToBytes(salt) + binfo = hexToBytes(info) + bprk = hexToBytes(PRK) + bokm = hexToBytes(OKM) + + var output = newSeq[byte](L) + var ctx: HKDF[HashType] + var prk: array[HashType.digestSize, byte] + + # let salt = if bsalt.len == 0: nil + # else: bsalt[0].unsafeAddr + # let ikm = if bikm.len == 0: nil + # else: bikm[0].unsafeAddr + # let info = if binfo.len == 0: nil + # else: binfo[0].unsafeAddr + let + salt = bsalt + ikm = bikm + info = binfo + + hkdfExtract(ctx, prk, salt, ikm) + hkdfExpand(ctx, output, prk, info) + + doAssert @(prk) == bprk, "\nComputed 0x" & toHex(prk) & + "\nbut expected " & PRK & '\n' + doAssert output == bokm, "\nComputed 0x" & toHex(output) & + "\nbut expected " & OKM & '\n' + echo "HKDF Test ", astToStr(id), " - SUCCESS" + + `test _ id`() + +test 1: # Basic test case with SHA-256 + type HashType = sha256 + const + IKM = "0x0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b" + salt = "0x000102030405060708090a0b0c" + info = "0xf0f1f2f3f4f5f6f7f8f9" + L = 42 + + PRK = "0x077709362c2e32df0ddc3f0dc47bba63" & + "90b6c73bb50f9c3122ec844ad7c2b3e5" + OKM = "0x3cb25f25faacd57a90434f64d0362f2a" & + "2d2d0a90cf1a5a4c5db02d56ecc4c5bf" & + "34007208d5b887185865" + +test 2: # Test with SHA-256 and longer inputs/outputs + type HashType = sha256 + const + IKM = "0x000102030405060708090a0b0c0d0e0f" & + "101112131415161718191a1b1c1d1e1f" & + "202122232425262728292a2b2c2d2e2f" & + "303132333435363738393a3b3c3d3e3f" & + "404142434445464748494a4b4c4d4e4f" + salt = "0x606162636465666768696a6b6c6d6e6f" & + "707172737475767778797a7b7c7d7e7f" & + "808182838485868788898a8b8c8d8e8f" & + "909192939495969798999a9b9c9d9e9f" & + "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf" + info = "0xb0b1b2b3b4b5b6b7b8b9babbbcbdbebf" & + "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf" & + "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf" & + "e0e1e2e3e4e5e6e7e8e9eaebecedeeef" & + "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff" + L = 82 + + PRK = "0x06a6b88c5853361a06104c9ceb35b45c" & + "ef760014904671014a193f40c15fc244" + OKM = "0xb11e398dc80327a1c8e7f78c596a4934" & + "4f012eda2d4efad8a050cc4c19afa97c" & + "59045a99cac7827271cb41c65e590e09" & + "da3275600c2f09b8367793a9aca3db71" & + "cc30c58179ec3e87c14c01d5c1f3434f" & + "1d87" + +test 3: # Test with SHA-256 and zero-length salt/info + type HashType = sha256 + const + IKM = "0x0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b" + salt = "" + info = "" + L = 42 + + PRK = "0x19ef24a32c717b167f33a91d6f648bdf" & + "96596776afdb6377ac434c1c293ccb04" + OKM = "0x8da4e775a563c18f715f802a063c5a31" & + "b8a11f5c5ee1879ec3454e5f3c738d2d" & + "9d201395faa4b61a96c8" \ No newline at end of file diff --git a/tests/t_mac_hmac_sha256.nim b/tests/t_mac_hmac_sha256.nim new file mode 100644 index 0000000..1409027 --- /dev/null +++ b/tests/t_mac_hmac_sha256.nim @@ -0,0 +1,114 @@ +# 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 + std/unittest, + ../constantine/mac/mac_hmac, + ../constantine/hashes, + ../constantine/math/io/io_bigints + +type TestVector = object + key: seq[byte] + data: seq[byte] + digest: array[32, byte] + truncatedLen: int + +proc doTest(key, data, digest: string) = + var tv: TestVector + + doAssert (key.len and 1) == 0, "An hex string must be of even length" + doAssert (data.len and 1) == 0, "An hex string must be of even length" + doAssert (digest.len and 1) == 0, "An hex string must be of even length" + doAssert digest.len <= 64, "HMAC-SHA256 hex string must be at most length 64 (32 bytes)" + + tv.key.newSeq(key.len div 2) + key.hexToPaddedByteArray(tv.key, bigEndian) + + tv.data.newSeq(data.len div 2) + data.hexToPaddedByteArray(tv.data, bigEndian) + + tv.truncatedLen = digest.len div 2 + digest.hexToPaddedByteArray(tv.digest, bigEndian) + + var output{.noInit.}: array[32, byte] + + HMAC[sha256].mac(output, tv.data, tv.key) + doAssert tv.digest.toOpenArray(tv.digest.len-tv.truncatedLen, tv.digest.len-1) == output.toOpenArray(0, tv.truncatedLen-1) + + +suite "[Message Authentication Code] HMAC-SHA256": + test "Test vector 1 - RFC4231": + doTest( + key = "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", + data = "4869205468657265", + digest = "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7" + ) + test "Test vector 2 - RFC4231": + doTest( + key = "4a656665", + data = "7768617420646f2079612077616e7420666f72206e6f7468696e673f", + digest = "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843" + ) + test "Test vector 3 - RFC4231": + doTest( + key = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + data = "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd", + digest = "773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe" + ) + test "Test vector 4 - RFC4231": + doTest( + key = "0102030405060708090a0b0c0d0e0f10111213141516171819", + data = "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd", + digest = "82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b" + ) + test "Test vector 5 - RFC4231": + doTest( + key = "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c", + data = "546573742057697468205472756e636174696f6e", + digest = "a3b6167473100ee06e0c796c2955552b" + ) + test "Test vector 6 - RFC4231": + doTest( + key = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" & + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" & + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" & + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" & + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" & + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" & + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" & + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" & + "aaaaaa", + data = "54657374205573696e67204c61726765" & + "72205468616e20426c6f636b2d53697a" & + "65204b6579202d2048617368204b6579" & + "204669727374", + digest = "60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54" + ) + test "Test vector 7 - RFC4231": + doTest( + key = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" & + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" & + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" & + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" & + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" & + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" & + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" & + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" & + "aaaaaa", + data = "54686973206973206120746573742075" & + "73696e672061206c6172676572207468" & + "616e20626c6f636b2d73697a65206b65" & + "7920616e642061206c61726765722074" & + "68616e20626c6f636b2d73697a652064" & + "6174612e20546865206b6579206e6565" & + "647320746f2062652068617368656420" & + "6265666f7265206265696e6720757365" & + "642062792074686520484d414320616c" & + "676f726974686d2e", + digest = "9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2" + ) \ No newline at end of file diff --git a/tests/t_mac_poly1305.nim b/tests/t_mac_poly1305.nim index d556887..e930b80 100644 --- a/tests/t_mac_poly1305.nim +++ b/tests/t_mac_poly1305.nim @@ -26,6 +26,6 @@ suite "[Message Authentication Code] Poly1305": ] var tag: array[16, byte] - poly1305.auth(tag, message, ikm) + poly1305.mac(tag, message, ikm) doAssert tag == expectedTag \ No newline at end of file