99 lines
3.3 KiB
Nim

# sponge construction for linear hashing.
#
# we recommend to use rate=8
# (note that we have the state width fixed to t=12)
#
# we use the 10* padding strategy (that is, always append an 1, and append
# as many zeros as required so that the final length is divisible by the rate)
# both when hashing bytes and when hashing field elements
import ../types
import ../goldilocks
import ./permutation
#import ./io
#-------------------------------------------------------------------------------
type
Sponge*[T: type, rate: static int] = object
state: F12
lenModRate: uint
func initialize(sponge: var Sponge) =
when not Sponge.rate >= 1 and Sponge.rate <= 8:
{.error: "with t=12, rate must be at most 8 (and positive)".}
const nbits = numberOfBits(Sponge.T)
const IV = toF( 0x10000*uint64(nbits) + 0x100*12 + uint64(Sponge.rate) ) # domain separation IV := (65536*nbits + 256*t + r)
sponge.state[8] = IV;
#---------------------------------------
func extractDigestF4(sponge: var Sponge) : F4 =
var digest : F4
for i in 0..<4: digest[i] = sponge.state[i]
return digest
func extractDigest(sponge: var Sponge) : Digest =
return toDigest(sponge.extractDigestF4())
#---------------------------------------
func update*[rate](sponge: var Sponge[F,rate], x: F) =
sponge.state[sponge.lenModRate] += x
sponge.lenModRate = (sponge.lenModRate + 1) mod rate
if (sponge.lenModRate == 0):
permInPlaceF12( sponge.state );
func finish*(sponge: var Sponge): Digest =
# padding
sponge.update(one)
while( sponge.lenModRate != 0):
sponge.update(zero)
return sponge.extractDigest()
#-------------------------------------------------------------------------------
func init*(_: type Sponge, T: type, rate: static int): Sponge[T,rate] =
when (rate < 1 or rate > 8):
{.error: "only rates between 1 and 8 are supported".}
var sponge: Sponge[T, rate]
initialize(sponge)
return sponge
#---------------------------------------
# digest a sequence of field elements
func digestNim*(rate: static int = 8, elements: openArray[F]): Digest =
var sponge : Sponge[F,rate] = Sponge.init(F,rate)
for element in elements:
sponge.update(element)
return sponge.finish()
# # digest a sequence of bytes
#func digestNim*(rate: static int = 8, bytes: openArray[byte],): F =
# var sponge = Sponge.init(nbits=8, rate)
# for element in bytes.elements(F):
# sponge.update(element)
# return sponge.finish()
#---------------------------------------
proc digestFeltsRawC(rate: int, len: int, input: ptr F , hash: var F4) {. header: "goldilocks.h", importc: "goldilocks_poseidon2_felts_digest", cdecl .}
proc digestBytesRawC(rate: int, len: int, input: ptr byte, hash: var F4) {. header: "goldilocks.h", importc: "goldilocks_poseidon2_bytes_digest", cdecl .}
func digestFeltsC*(rate: static int = 8, felts: openArray[F]): Digest =
var digest : F4
let input = if felts.len > 0: unsafeAddr felts[0] else: nil
digestFeltsRawC(rate, felts.len, input, digest)
return toDigest(digest)
func digestBytesC*(rate: static int = 8, bytes: openArray[byte]): Digest =
var digest : F4
let input = if bytes.len > 0: unsafeAddr bytes[0] else: nil
digestBytesRawC(rate, bytes.len, input, digest)
return toDigest(digest)
#-------------------------------------------------------------------------------