Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimized SHA2 implementation. #78

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 120 additions & 0 deletions nimcrypto/cpufeatures.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
#
#
# NimCrypto
# (c) Copyright 2024 Eugene Kabanov
#
# See the file "LICENSE", included in this
# distribution, for details about the copyright.
#

## This module provides CPU cryptographic features detection.

{.push raises: [].}

type
CpuFeature* {.pure.} = enum
AMD64, # AMD64 processor
AARCH64, # AARCH64 (ARM64) processor
AVX, # Intel AVX extension support
AVX2, # Intel AVX2 extension support
AVX512, # Intel AVX512 extension support
SHA1EXT, # SHA1 extension support
SHA2EXT, # SHA2-256 extension support
SHA2BEXT, # SHA2-512 extension support
CRC32, # CRC-32 extension support
AES # AES extension support

when defined(amd64):
when defined(vcc):
proc getCpuid(cpuInfo: ptr uint32, funcId: uint32, subId: uint32) {.
importc: "__cpuidex", header: "<intrin.h>".}
else:
proc getCpuid(leaf, subleaf: uint32,
eax, ebx, ecx, edx: var uint32): uint32 {.
importc: "__get_cpuid_count", header: "<cpuid.h>".}

proc cpuId(leaf, subleaf: uint32): array[4, uint32] =
var res: array[4, uint32]
when defined(vcc):
getCpuid(addr res[0], leaf, subleaf)
else:
discard getCpuid(leaf, subleaf, res[0], res[1], res[2], res[3])
res

const
BIT_SHA = 1'u32 shl 29
REG_SHA = 1
BIT_AVX = 1'u32 shl 28
REG_AVX = 2
BIT_AES = 1'u32 shl 25
REG_AES = 2
BIT_AVX2 = 1'u32 shl 5
REG_AVX2 = 1
BIT_AVX512F = 1'u32 shl 16
REG_AVX512F = 1
BIT_AVX512BW = 1'u32 shl 30
REG_AVX512BW = 1
BIT_CRC32 = 1'u32 shl 20
REG_CRC32 = 2

proc getCpuFeatures*(): set[CpuFeature] =
var res: set[CpuFeature]
res.incl(CpuFeature.AMD64)
let
array1 = cpuId(1'u32, 0'u32)
array7 = cpuId(7'u32, 0'u32)

if (array1[REG_AVX] and BIT_AVX) != 0'u32:
res.incl(CpuFeature.AVX)
if (array1[REG_AES] and BIT_AES) != 0'u32:
res.incl(CpuFeature.AES)
if (array1[REG_CRC32] and BIT_CRC32) != 0'u32:
res.incl(CpuFeature.CRC32)
if (array7[REG_SHA] and BIT_SHA) != 0'u32:
res.incl(CpuFeature.SHA1EXT)
res.incl(CpuFeature.SHA2EXT)
if (array7[REG_AVX2] and BIT_AVX2) != 0'u32:
res.incl(CpuFeature.AVX2)
if ((array7[REG_AVX512F] and BIT_AVX512F) != 0'u32) and
((array7[REG_AVX512BW] and BIT_AVX512BW) != 0'u32):
res.incl(CpuFeature.AVX512)
res
elif defined(arm64):
when defined(linux):
proc getauxval(t: uint32): uint32 {.
importc: "getauxval", header: "<sys/auxv.h>".}
const
AT_HWCAP = 16'u32
HWCAP_AES = 0x08'u32
HWCAP_SHA1 = 0x20'u32
HWCAP_SHA2 = 0x40'u32
HWCAP_CRC32 = 0x80'u32
HWCAP_SHA512 = 0x200000'u32

proc getCpuFeatures*(): set[CpuFeature] =
var res: set[CpuFeature]
res.incl(CpuFeature.AARCH64)
let plain = getauxval(AT_HWCAP)
if (plain and HWCAP_AES) != 0'u32:
res.incl(CpuFeature.AES)
if (plain and HWCAP_SHA1) != 0'u32:
res.incl(CpuFeature.SHA1EXT)
if (plain and HWCAP_SHA2) != 0'u32:
res.incl(CpuFeature.SHA2EXT)
if (plain and HWCAP_CRC32) != 0'u32:
res.incl(CpuFeature.CRC32)
if (plain and HWCAP_SHA512) != 0'u32:
res.incl(CpuFeature.SHA2BEXT)
res
elif defined(macos) or defined(macosx):
proc getCpuFeatures*(): set[CpuFeature] =
## TODO: Right now all modern aarch64 macos systems has neon extensions
## available, but we do not have method to detect it yet.
{CpuFeature.AARCH64, CpuFeature.SHA2EXT, CpuFeature.SHA2BEXT,
CpuFeature.CRC32, CpuFeature.AES}
else:
proc getCpuFeatures*(): set[CpuFeature] =
{}
else:
proc getCpuFeatures*(): set[CpuFeature] =
{}
146 changes: 134 additions & 12 deletions nimcrypto/hmac.nim
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#
#
# NimCrypto
# (c) Copyright 2016 Eugene Kabanov
# (c) Copyright 2016-2024 Eugene Kabanov
#
# See the file "LICENSE", included in this
# distribution, for details about the copyright.
Expand Down Expand Up @@ -55,9 +55,13 @@
## # 18AF7C8586141A47EAAD416C2B356431D001FAFF3B8C98C80AA108DC971B230D
## # 18AF7C8586141A47EAAD416C2B356431D001FAFF3B8C98C80AA108DC971B230D
## # 18AF7C8586141A47EAAD416C2B356431D001FAFF3B8C98C80AA108DC971B230D
import utils
import sha, sha2, ripemd, keccak, blake2, hash
export sha, sha2, ripemd, keccak, blake2, hash

{.push raises: [].}

import "."/[utils, cpufeatures]
import "."/[sha, ripemd, keccak, blake2, hash]
import "."/sha2/sha2
export sha, sha2, ripemd, keccak, blake2, hash, cpufeatures

template hmacSizeBlock*(h: typedesc): int =
mixin sizeBlock
Expand Down Expand Up @@ -203,19 +207,23 @@ proc finish*[T: bchar](hmctx: var HMAC,
var buffer: array[hmctx.sizeDigest, byte]
discard finish(hmctx.mdctx, buffer)
hmctx.opadctx.update(buffer)
result = hmctx.opadctx.finish(data)
hmctx.opadctx.finish(data)
else:
0

proc finish*(hmctx: var HMAC, pbytes: ptr byte, nbytes: uint): uint {.inline.} =
## Finalize HMAC context ``hmctx`` and store calculated digest to address
## pointed by ``pbytes`` of length ``nbytes``. ``pbytes`` must be able to
## hold at ``hmctx.sizeDigest`` octets (bytes).
var ptrarr = cast[ptr UncheckedArray[byte]](pbytes)
result = hmctx.finish(ptrarr.toOpenArray(0, int(nbytes) - 1))
hmctx.finish(ptrarr.toOpenArray(0, int(nbytes) - 1))

proc finish*(hmctx: var HMAC): MDigest[hmctx.HashType.bits] =
## Finalize HMAC context ``hmctx`` and return calculated digest as
## ``MDigest`` object.
discard finish(hmctx, result.data)
var res: MDigest[hmctx.HashType.bits]
discard finish(hmctx, res.data)
res

proc hmac*[A: bchar, B: bchar](HashType: typedesc, key: openArray[A],
data: openArray[B]): MDigest[HashType.bits] =
Expand Down Expand Up @@ -243,8 +251,9 @@ proc hmac*[A: bchar, B: bchar](HashType: typedesc, key: openArray[A],
var ctx: HMAC[HashType]
ctx.init(key)
ctx.update(data)
result = ctx.finish()
let res = ctx.finish()
ctx.clear()
res

proc hmac*(HashType: typedesc, key: ptr byte, klen: uint,
data: ptr byte, ulen: uint): MDigest[HashType.bits] {.inline.} =
Expand All @@ -268,7 +277,120 @@ proc hmac*(HashType: typedesc, key: ptr byte, klen: uint,
## echo keccak256.hmac(key, keylen, data, datalen)
## # Print HMAC[RIPEMD160](key = "AliceKey", data = "Hello World!")
## echo ripemd160.hmac(key, keylen, data, datalen)
var keyarr = cast[ptr UncheckedArray[byte]](key)
var dataarr = cast[ptr UncheckedArray[byte]](data)
result = hmac(HashType, keyarr.toOpenArray(0, int(klen) - 1),
dataarr.toOpenArray(0, int(ulen) - 1))
var
keyarr = cast[ptr UncheckedArray[byte]](key)
dataarr = cast[ptr UncheckedArray[byte]](data)
hmac(HashType, keyarr.toOpenArray(0, int(klen) - 1),
dataarr.toOpenArray(0, int(ulen) - 1))

## Optimized SHA2 declarations

type
Sha2Type = sha224 | sha256 | sha384 | sha512 | sha512_224 | sha512_256

proc init*[T: Sha2Type, M](hmctx: var HMAC[T], key: openArray[M],
implementation: Sha2Implementation,
cpufeatures: set[CpuFeature] = {}) =
mixin init, update, finish

when not((M is byte) or (M is char)):
{.fatal: "Choosen key type is not supported!".}

var kpad: array[hmctx.sizeBlock, byte]
init(hmctx.opadctx, implementation, cpufeatures)

if len(key) > 0:
if len(key) > int(hmctx.sizeBlock):
init(hmctx.mdctx, implementation, cpufeatures)
update(hmctx.mdctx, key)
discard finish(hmctx.mdctx, kpad)
else:
copyMem(kpad, 0, key, 0, len(key))

for i in 0 ..< int(hmctx.sizeBlock):
hmctx.opad[i] = 0x5C'u8 xor kpad[i]
hmctx.ipad[i] = 0x36'u8 xor kpad[i]

init(hmctx.mdctx, implementation, cpufeatures)
update(hmctx.mdctx, hmctx.ipad)
update(hmctx.opadctx, hmctx.opad)

proc init*[T: Sha2Type, M](hmctx: var HMAC[T], key: openArray[M],
cpufeatures: set[CpuFeature]) =
init(hmctx, key, Sha2Implementation.Auto, cpufeatures)

proc init*[T: Sha2Type](hmctx: var HMAC[T], key: ptr byte, keylen: uint,
implementation: Sha2Implementation,
cpufeatures: set[CpuFeature] = {}) =
var ptrarr = cast[ptr UncheckedArray[byte]](key)
init(hmctx, ptrarr.toOpenArray(0, int(keylen) - 1), implementation,
cpufeatures)

proc init*[T: Sha2Type](hmctx: var HMAC[T], key: ptr byte, keylen: uint,
cpufeatures: set[CpuFeature]) =
var ptrarr = cast[ptr UncheckedArray[byte]](key)
init(hmctx, ptrarr.toOpenArray(0, int(keylen) - 1), Sha2Implementation.Auto,
cpufeatures)

template declareHmac(DigestType: untyped) =
proc hmac*[A: bchar, B: bchar](
HashType: typedesc[DigestType],
key: openArray[A],
data: openArray[B],
implementation: Sha2Implementation,
cpufeatures: set[CpuFeature] = {}
): MDigest[HashType.bits] =
var ctx: HMAC[HashType]
ctx.init(key, implementation, cpufeatures)
ctx.update(data)
let res = ctx.finish()
ctx.clear()
res

proc hmac*[A: bchar, B: bchar](
HashType: typedesc[DigestType],
key: openArray[A],
data: openArray[B],
cpufeatures: set[CpuFeature]
): MDigest[HashType.bits] =
hmac(HashType, key, data, Sha2Implementation.Auto, cpufeatures)

proc hmac*(
HashType: typedesc[DigestType],
key: ptr byte,
klen: uint,
data: ptr byte,
ulen: uint,
implementation: Sha2Implementation,
cpufeatures: set[CpuFeature] = {}
): MDigest[HashType.bits] =
var
keyarr = cast[ptr UncheckedArray[byte]](key)
dataarr = cast[ptr UncheckedArray[byte]](data)
hmac(HashType,
keyarr.toOpenArray(0, int(klen) - 1),
dataarr.toOpenArray(0, int(ulen) - 1),
implementation, cpufeatures)

proc hmac*(
HashType: typedesc[DigestType],
key: ptr byte,
klen: uint,
data: ptr byte,
ulen: uint,
cpufeatures: set[CpuFeature]
): MDigest[HashType.bits] =
var
keyarr = cast[ptr UncheckedArray[byte]](key)
dataarr = cast[ptr UncheckedArray[byte]](data)
hmac(HashType,
keyarr.toOpenArray(0, int(klen) - 1),
dataarr.toOpenArray(0, int(ulen) - 1),
Sha2Implementation.Auto, cpufeatures)

declareHmac(sha224)
declareHmac(sha256)
declareHmac(sha384)
declareHmac(sha512)
declareHmac(sha512_224)
declareHmac(sha512_256)
Loading
Loading