blobsets/src/blobsets/priv/blake2.nim

191 lines
6.4 KiB
Nim

import std/bitops, std/endians
type
Blake2b* = object
hash: array[8, uint64]
offset: array[2, uint64]
buffer: array[128, uint8]
buffer_idx: uint8
hash_size: uint8
Blake2bParams* = object
b: array[64, byte]
Blake2sParams* = object
b: array[32, byte]
Blake2Params* = Blake2bParams | Blake2sParams
proc params(c: var Blake2b): ptr Blake2bParams =
cast[ptr Blake2bParams](c.hash.addr)
proc `digestLen=`*(p: ptr Blake2bParams; x: range[1..64]) =
p.b[0] = (uint8)x
proc `digestLen=`*(p: ptr Blake2sParams; x: range[1..32]) =
p.b[0] = (uint8)x
proc `keyLen=`*(p: ptr Blake2bParams; x: range[0..64]) =
p.b[1] = (uint8)x
proc `keyLen=`*(p: ptr Blake2sParams; x: range[0..32]) =
p.b[1] = (uint8)x
proc `fanout=`*(p: ptr Blake2Params; x: Natural) =
p.b[2] = (uint8)x
proc `depth=`*(p: ptr Blake2Params; x: Natural) =
p.b[3] = (uint8)x
proc `leafLength=`*(p: ptr Blake2Params; x: Natural) =
var x = x; littleEndian32(p.b[4].addr, x.addr)
proc `nodeOffset=`*(p: ptr Blake2bParams; x: Natural) =
var x = x; littleEndian64(p.b[8].addr, x.addr)
proc `nodeOffset=`*(p: ptr Blake2sParams; x: Natural) =
var tmp: int64
littleEndian64(tmp.addr, p.b[8].addr)
tmp = (tmp and 0xffffffff) or (x shl 32)
littleEndian64(p.b[8].addr, tmp.addr)
proc `nodeDepth=`*(p: ptr Blake2bParams; x: Natural) =
p.b[16] = (uint8)x
proc `nodeDepth=`*(p: ptr Blake2sParams; x: Natural) =
p.b[14] = (uint8)x
proc `innerLength=`*(p: ptr Blake2bParams; x: Natural) =
p.b[17] = (uint8)x
proc `innerLength=`*(p: ptr Blake2sParams; x: Natural) =
p.b[15] = (uint8)x
proc `salt=`*(p: ptr Blake2bParams; salt: pointer; len: Natural) =
copyMem(p.b[32].addr, salt, min(len, 16))
proc `salt=`*(p: ptr Blake2sParams; salt: pointer; len: Natural) =
copyMem(p.b[16].addr, salt, min(len, 8))
proc `personal=`*(p: ptr Blake2bParams; salt: pointer; len: Natural) =
copyMem(p.b[48].addr, salt, min(len, 16))
proc `personal=`*(p: ptr Blake2sParams; salt: pointer; len: Natural) =
copyMem(p.b[24].addr, salt, min(len, 8))
proc init(p: ptr Blake2Params) =
when p is Blake2bParams:
p.digestLen = 64
else:
p.digestLen = 32
p.fanout = 1
p.depth = 1
const Blake2bIV =
[ 0x6a09e667f3bcc908'u64, 0xbb67ae8584caa73b'u64,
0x3c6ef372fe94f82b'u64, 0xa54ff53a5f1d36f1'u64,
0x510e527fade682d1'u64, 0x9b05688c2b3e6c1f'u64,
0x1f83d9abfb41bd6b'u64, 0x5be0cd19137e2179'u64 ]
const Sigma = [
[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 ],
[ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 ],
[ 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 ],
[ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 ],
[ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 ],
[ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 ],
[ 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 ],
[ 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 ],
[ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 ],
[ 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 ],
[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 ],
[ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 ] ]
proc inc(a: var array[2, uint64], b: uint8) =
a[0] = a[0] + b
if (a[0] < b): inc(a[1])
proc padding(a: var array[128, uint8], b: uint8) =
for i in b..127: a[i] = 0
proc G (v: var array[16, uint64],
a,b,c,d: int, x,y: uint64)
{.inline.} =
v[a] = v[a] + v[b] + x
v[d] = rotateRightBits(v[d] xor v[a], 32)
v[c] = v[c] + v[d]
v[b] = rotateRightBits(v[b] xor v[c], 24)
v[a] = v[a] + v[b] + y
v[d] = rotateRightBits(v[d] xor v[a], 16)
v[c] = v[c] + v[d]
v[b] = rotateRightBits(v[b] xor v[c], 63)
proc compress(c: var Blake2b, last: int = 0) =
var input, v: array[16, uint64]
for i in 0..15:
input[i] = cast[ptr uint64](addr(c.buffer[i*8]))[]
for i in 0..7:
v[i] = c.hash[i]
v[i+8] = Blake2bIV[i]
v[12] = v[12] xor c.offset[0]
v[13] = v[13] xor c.offset[1]
if (last == 1): v[14] = not(v[14])
for i in 0..11:
G(v, 0, 4, 8, 12, input[Sigma[i][0]], input[Sigma[i][1]])
G(v, 1, 5, 9, 13, input[Sigma[i][2]], input[Sigma[i][3]])
G(v, 2, 6, 10, 14, input[Sigma[i][4]], input[Sigma[i][5]])
G(v, 3, 7, 11, 15, input[Sigma[i][6]], input[Sigma[i][7]])
G(v, 0, 5, 10, 15, input[Sigma[i][8]], input[Sigma[i][9]])
G(v, 1, 6, 11, 12, input[Sigma[i][10]], input[Sigma[i][11]])
G(v, 2, 7, 8, 13, input[Sigma[i][12]], input[Sigma[i][13]])
G(v, 3, 4, 9, 14, input[Sigma[i][14]], input[Sigma[i][15]])
for i in 0..7:
c.hash[i] = c.hash[i] xor v[i] xor v[i+8]
c.buffer_idx = 0
{.push boundChecks: off.}
proc update*(c: var Blake2b, buf: pointer, data_size: int) =
var data = cast[ptr array[0, uint8]](buf)
for i in 0..<data_size:
if c.buffer_idx == 128:
inc(c.offset, c.buffer_idx)
compress(c)
c.buffer[c.buffer_idx] = data[i]
inc(c.buffer_idx)
{.pop.}
proc update*(c: var Blake2b, data: cstring|string|seq|uint8, data_size: int) =
for i in 0..<data_size:
if c.buffer_idx == 128:
inc(c.offset, c.buffer_idx)
compress(c)
when data is cstring or data is string:
c.buffer[c.buffer_idx] = data[i].uint8
elif data is seq:
c.buffer[c.buffer_idx] = data[i]
else:
c.buffer[c.buffer_idx] = data
inc(c.buffer_idx)
proc initBlake2b*(key: pointer = nil, key_size: range[0..64] = 0): Blake2b =
init(result.params)
result.hash_size = result.params.b[0]
result.params.keyLen = keySize
for i in 0..7:
result.hash[i] = Blake2bIV[i]
if key_size > 0:
update(result, key, key_size)
padding(result.buffer, result.buffer_idx)
result.buffer_idx = 128
proc initBlake2b*(customize: proc(params: ptr Blake2bParams)): Blake2b =
let p = result.params
init(p)
customize(p)
result.hash_size = p.b[0]
for i in 0..7:
result.hash[0] = result.hash[0] xor Blake2bIV[i]
proc finish*(c: var Blake2b): seq[uint8] =
result = newSeq[uint8](c.hash_size)
inc(c.offset, c.buffer_idx)
padding(c.buffer, c.buffer_idx)
compress(c, 1)
for i in 0'u8..<c.hash_size:
result[i.int] = cast[uint8]((c.hash[i div 8] shr (8'u8 * (i and 7)) and 0xFF))
proc `$`(d: seq[uint8]): string =
const digits = "0123456789abcdef"
result = ""
for i in 0..high(d):
add(result, digits[(d[i].int shr 4) and 0xF])
add(result, digits[d[i].int and 0xF])