151 lines
4.8 KiB
Nim
151 lines
4.8 KiB
Nim
#
|
|
#
|
|
# NimCrypto
|
|
# (c) Copyright 2016 Eugene Kabanov
|
|
#
|
|
# See the file "LICENSE", included in this
|
|
# distribution, for details about the copyright.
|
|
#
|
|
|
|
## This module provides helper procedures for calculating secure digests
|
|
## supported by `nimcrypto` library.
|
|
import utils
|
|
|
|
{.deadCodeElim:on.}
|
|
|
|
const
|
|
MaxMDigestLength* = 64
|
|
## Maximum size of generated digests by `nimcrypto` library is 64 octets.
|
|
|
|
type
|
|
MDigest*[bits: static[int]] = object
|
|
## Message digest type
|
|
data*: array[bits div 8, byte]
|
|
|
|
bchar* = byte | char
|
|
|
|
proc `$`*(digest: MDigest): string =
|
|
## Return hexadecimal string representation of ``digest``.
|
|
##
|
|
## .. code-block::nim
|
|
## import nimcrypto
|
|
##
|
|
## var digestHexString = $sha256.digest("Hello World!")
|
|
## echo digestHexString
|
|
result = ""
|
|
var i = 0'u
|
|
while i < uint(len(digest.data)):
|
|
result &= hexChar(cast[byte](digest.data[i]))
|
|
inc(i)
|
|
|
|
proc digest*(HashType: typedesc, data: ptr byte,
|
|
ulen: uint): MDigest[HashType.bits] =
|
|
## Calculate and return digest using algorithm ``HashType`` of data ``data``
|
|
## with length ``ulen``.
|
|
##
|
|
## .. code-block::nim
|
|
## import nimcrypto
|
|
##
|
|
## var stringToHash = "Hello World!"
|
|
## let data = cast[ptr byte](addr stringToHash[0])
|
|
## let datalen = uint(len(stringToHash))
|
|
## echo sha256.digest(data, datalen)
|
|
mixin init, update, finish, clear
|
|
var ctx: HashType
|
|
ctx.init()
|
|
ctx.update(data, ulen)
|
|
result = ctx.finish()
|
|
ctx.clear()
|
|
|
|
proc digest*[T](HashType: typedesc, data: openarray[T],
|
|
ostart: int = 0, ofinish: int = -1): MDigest[HashType.bits] =
|
|
## Calculate and return digest using algorithm ``HashType`` of data ``data``
|
|
## in slice ``[ostart, ofinish]``, both ``ostart`` and ``ofinish`` are
|
|
## inclusive.
|
|
##
|
|
## .. code-block::nim
|
|
## import nimcrypto
|
|
##
|
|
## var stringToHash = "Hello World!"
|
|
## ## Calculate digest of whole string `Hello World!`.
|
|
## echo sha256.digest(stringToHash)
|
|
## ## Calcualte digest of `Hello`.
|
|
## echo sha256.digest(stringToHash, ofinish = 4)
|
|
## ## Calculate digest of `World!`.
|
|
## echo sha256.digest(stringToHash, ostart = 6)
|
|
## ## Calculate digest of constant `Hello`.
|
|
## echo sha256.digest("Hello")
|
|
## ## Calculate digest of constant `World!`.
|
|
## echo sha256.digest("World!")
|
|
mixin init, update, finish, clear
|
|
var ctx: 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(T)
|
|
ctx.init()
|
|
if length <= 0:
|
|
result = ctx.finish()
|
|
else:
|
|
ctx.update(cast[ptr byte](unsafeAddr data[so]), uint(length))
|
|
result = ctx.finish()
|
|
ctx.clear()
|
|
|
|
proc fromHex*(T: typedesc[MDigest], s: string): T =
|
|
## Create ``MDigest`` object from hexadecimal string representation.
|
|
##
|
|
## .. code-block::nim
|
|
## import nimcrypto
|
|
##
|
|
## var a = MDigest[256].fromHex("7F83B1657FF1FC53B92DC18148A1D65DFC2D4B1FA3D677284ADDD200126D9069")
|
|
## echo $a
|
|
## ## Get number of bits used by ``a``.
|
|
## echo a.bits
|
|
hexToBytes(s, result.data)
|
|
|
|
proc `==`*[A, B](d1: MDigest[A], d2: MDigest[B]): bool =
|
|
## Check for equality between two ``MDigest`` objects ``d1`` and ``d2``.
|
|
## If size in bits of ``d1`` is not equal to size in bits of ``d2`` then
|
|
## digests considered as not equal.
|
|
if d1.bits != d2.bits:
|
|
return false
|
|
var n = len(d1.data)
|
|
var res, diff: int
|
|
while n > 0:
|
|
dec(n)
|
|
diff = int(d1.data[n]) - int(d2.data[n])
|
|
res = (res and -not(diff)) or diff
|
|
result = (res == 0)
|
|
|
|
when true:
|
|
proc toDigestAux(n: static int, s: static string): MDigest[n] =
|
|
static:
|
|
assert n > 0 and n mod 8 == 0,
|
|
"The provided hex string should have an even non-zero length"
|
|
hexToBytes(s, result.data)
|
|
|
|
template toDigest*(s: static string): auto =
|
|
## Convert hexadecimal string representation to ``MDigest`` object.
|
|
## This template can be used to create ``MDigest`` constants.
|
|
##
|
|
## .. code-block::nim
|
|
## import nimcrypto
|
|
##
|
|
## const SomeDigest = "7F83B1657FF1FC53B92DC18148A1D65DFC2D4B1FA3D677284ADDD200126D9069".toDigest
|
|
## echo $SomeDigest
|
|
## ## Get number of bits used by ``SomeDigest``.
|
|
## echo SomeDigest.bits
|
|
const digest = toDigestAux(len(s) * 4, s)
|
|
digest
|
|
|
|
else:
|
|
# This definition is shorter, but it turns out that it
|
|
# triggers a Nim bug. Calls to `toDigest` will compile,
|
|
# but the result values won't be considered the same
|
|
# type as MDigest[N] even when s.len * 4 == N
|
|
proc toDigest*(s: static string): MDigest[s.len * 4] =
|
|
static:
|
|
assert s.len > 0 and s.len mod 2 == 0,
|
|
"The provided hex string should have an even non-zero length"
|
|
const digest = hexToBytes(s, result.data)
|
|
return digest
|