add Griffin permutation

This commit is contained in:
Balazs Komuves 2026-01-21 14:36:11 +01:00
parent d381c00874
commit 47df39467c
No known key found for this signature in database
GPG Key ID: F63B7AEF18435562
9 changed files with 272 additions and 22 deletions

3
bench/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
.DS_Store
bench_perm
bench_griffin

51
bench/bench_griffin.nim Normal file
View File

@ -0,0 +1,51 @@
#
# nimble build -d:release
#
import strformat
# import strutils
import constantine/math/arithmetic
import constantine/math/io/io_fields
import constantine/math/io/io_bigints
import poseidon2/types
import poseidon2/io
import griffin/permutation
import ./shared
#-------------------------------------------------------------------------------
proc iteratePerm(n: int) =
var x: F = toF(0)
var y: F = toF(1)
var z: F = toF(2)
for i in 0..<n:
permInPlace(x, y, z)
echo "x = ", toDecimal(x)
echo "y = ", toDecimal(y)
echo "z = ", toDecimal(z)
#-------------------------------------------------------------------------------
when isMainModule:
testGriffin()
echo "----------------------------------"
echo "quick & dirty Griffin benchmark"
let n: int = 100000
let text = fmt"{n} Grffin permutations"
withMeasureTime(true,text):
iteratePerm(n)
let mb = float64(n)*62.0/1024/1024
echo fmt"that corresponds to about {mb:.2f} megabytes of linear hashing"

View File

@ -4,7 +4,7 @@
#
import strformat
import times, os, strutils
#import strutils
import constantine/math/arithmetic
import constantine/math/io/io_fields
@ -14,22 +14,7 @@ import poseidon2/types
import poseidon2/io
import poseidon2/permutation
#-------------------------------------------------------------------------------
func seconds*(x: float): string = fmt"{x:.4f} seconds"
func quoted*(s: string): string = fmt"`{s:s}`"
template withMeasureTime*(doPrint: bool, text: string, code: untyped) =
block:
if doPrint:
let t0 = epochTime()
code
let elapsed = epochTime() - t0
let elapsedStr = elapsed.formatFloat(format = ffDecimal, precision = 4)
echo ( text & " took " & elapsedStr & " seconds" )
else:
code
import ./shared
#-------------------------------------------------------------------------------

23
bench/shared.nim Normal file
View File

@ -0,0 +1,23 @@
import strformat
import times, strutils
#-------------------------------------------------------------------------------
func seconds*(x: float): string = fmt"{x:.4f} seconds"
func quoted*(s: string): string = fmt"`{s:s}`"
template withMeasureTime*(doPrint: bool, text: string, code: untyped) =
block:
if doPrint:
let t0 = epochTime()
code
let elapsed = epochTime() - t0
let elapsedStr = elapsed.formatFloat(format = ffDecimal, precision = 4)
echo ( text & " took " & elapsedStr & " seconds" )
else:
code
#-------------------------------------------------------------------------------

10
griffin/compress.nim Normal file
View File

@ -0,0 +1,10 @@
import ./types
import ./permutation
# 2-to-1 compression
func compress*(a, b : F, key = zero) : F =
var x = a
var y = b
var z = key
permInPlace(x, y, z)
return x

106
griffin/permutation.nim Normal file
View File

@ -0,0 +1,106 @@
import
constantine/math/arithmetic,
constantine/math/io/io_fields,
constantine/math/io/io_bigints
import ../poseidon2/types
import ./roundconst
#-------------------------------------------------------------------------------
const expo_inv : B = fromHex(B,"0x26b6a528b427b35493736af8679aad17535cb9d394945a0dcfe7f7a98ccccccd")
const alpha : F = fromHex(F,"0x146ecffb34a66316fae66609f78d1310bc14ad7208082ca7943afebb1da4aa4a")
const beta : F = fromHex(F,"0x2b568115d544c7e941eff6ccc935384619b0fb7d2c5ba6c078c34cf81697ee1c")
const roundConstArray : array[33, F] = arrayFromHex( roundConstStr )
#-------------------------------------------------------------------------------
# inplace sbox, x => x^5
proc pow5(x: var F) : void =
var y = x
square(y)
square(y)
x *= y
# inplace sbox, x => x^(1/5)
proc powInv5(x: var F) : void =
x = fastPow(x, expo_inv)
#-------------------------------------------------------------------------------
proc sbox(x, y, z: var F) =
x.powInv5()
y.pow5()
let u : F = x + y
var m = beta
m += sqr(u)
m += alpha * u # m = u^2 + alpha*u + beta
z *= m
proc addRC(round: int, x, y, z: var F) =
if (round > 0):
let j = (round-1) * 3
x += roundConstArray[ j ]
y += roundConstArray[ j+1 ]
z += roundConstArray[ j+2 ]
proc linear(x, y, z : var F) =
var s = x ; s += y ; s += z
x += s
y += s
z += s
proc roundFun(round: int, x, y, z: var F) =
addRC(round,x,y,z)
sbox(x,y,z)
linear(x,y,z)
#-------------------------------------------------------------------------------
# the Griffin permutation (mutable, in-place version)
proc permInPlace*(x, y, z : var F) =
linear(x, y, z)
for j in 0..<12:
roundFun(j, x, y, z)
# the Griffin permutation (pure version)
func perm*(xyz: S) : S =
var (x,y,z) = xyz
permInPlace(x, y, z)
return (x,y,z)
#-------------------------------------------------------------------------------
# known-answer test: the expected permutation of (0,1,2)
const kat: S =
( F.fromHex("0x2311cdb3076c3a7ee37fd5a271e0f3a8a3cc38057d0cea37b78951f43b1b6ff6")
, F.fromHex("0x1d3aaed9ea361e899e667abd18e5328555b97b5c3890d52b261f940d6ab4df58")
, F.fromHex("0x22614a0ac719cb623a636adac3bac1b85b5a7a418fcf8ab3a3ae0787fb4bed9d")
)
proc sanityCheckExpoInv*() =
let x0 : F = F.fromHex("0x666")
var x : F = x0
x.pow5()
x.powInv5()
echo "sanity check /a = " & $(x == x0)
let y0 : F = F.fromHex("0x777017")
var y : F = y0
y.powInv5()
y.pow5()
echo "sanity check /b = " & $(y == y0)
proc testGriffin*() =
sanityCheckExpoInv()
let inp: S = (zero,one,two)
let candidate = perm(inp)
let (x,y,z) = candidate
echo "x = " & $x.toDecimal()
echo "y = " & $y.toDecimal()
echo "z = " & $z.toDecimal()
echo "KAT ok = " & $(candidate == kat)
#-------------------------------------------------------------------------------

41
griffin/roundconst.nim Normal file
View File

@ -0,0 +1,41 @@
#-------------------------------------------------------------------------------
const roundConstStr* : array[33, string] =
[ "0x2fb30cafdb1f76156dfabf0cd0af4b895e764ac2a84386c9d0d7aed6a7f4eac9"
, "0x282927892ce324572f19abb14871d2b539a80d8a5800cdb87a81e1697a94b6c9"
, "0x03d0f3f2711dd59e3d97fc797261300cd3fee33b95cf710a32edf42aa2bc0905"
, "0x036a8b3eb9ef35c74ea5a367ed279ee6d043d4ff69817f192c7251b91dcbb03d"
, "0x2a626d396e7fa8ce8d6339bb37bd48491d56db0c7ac0afb5008a7464d5776a26"
, "0x0cc9dfabbeaef7982543453ea3ac37ef2bfefd35a7e7070aa39b021035852d5b"
, "0x2a1951149e2568ab28e972a2ceddc49eff0cae8e1cddcf4b0684a73a1b4ef61b"
, "0x2d0ff8e9158b2fd7ae3afe01cf09d4ce9ff81e6127e441eb6cbc79d21f22be9e"
, "0x1cc315b7ea0c1efb538f0c3248a7da062309a9e41af5a555c9ea9e8a10930cb5"
, "0x03cb10093ea62fb3f6e5680a128d07112ee566f1b424558f2ec9d86892e13a80"
, "0x12e7bb50ae7e9e90f1765c073eb61c4be4956c424930233ce497d2722a458868"
, "0x006b1367547937ae71e2e9b55d2f90c90131f9e6784ce3de0eb314ec748871e7"
, "0x1ffff572c53442c58809aeca02287839b11df1420deb0e99fde2baad8b86fa9c"
, "0x13aefd685e7739f9a8b4ccdbfc5ef9e566149af4d54d6b746058ea44cb422840"
, "0x1ea6c3ea93fe6f4ed0186941650de76ff94ab0e6e8a583996b67ba026dd2b7a5"
, "0x288f120288f9225643de833c5c15e22aadd358132bbdc12c75109048a158c9f4"
, "0x0f638114cd7c781ab299e5233338b00cf2996df962347a00146a22103d9ad91a"
, "0x14eeca5fa2c18999ea25ddf44237d6ac3cb8757ea452f67e2590a46f7d5b1e4f"
, "0x102d1a099e8cd107dc056e72370e340b0316d237b72d99ef6261761f7eb2d61c"
, "0x0ef741fc2fcda50f207c759dbd844a4d630cc0e4062ca80f3ffba2cce2d3f51d"
, "0x0989b9f642485692a1f91a4b207db64f38ae545bf3e0622f3862967d27f563db"
, "0x1eb4d812c80ce04784a80c89fbcc5aab89db274c62602bdd30f3223655e6cf8a"
, "0x0124a9400253731facd46e21f41016aed69a79087f81665bc5d29a34e4e924dd"
, "0x2520bfa6b70e6ba7ad380aaf9015b71983868a9c53e66e685ed6e48692c185a8"
, "0x1bd62b5bfa02667ac08d51d9e77bb3ab8dbd19e7a701442a20e23f7d3d6b28b4"
, "0x1ae2f0d09fffc6bb869ebc639484a7c2084cfa3c1f88a7440713b1b154e5f952"
, "0x0cd06e16a0d570c3799d800d92a25efbd44a795ed5b9114a28f5f869a57d9ba1"
, "0x00691740e313922521fe8c4843355eff8de0f93d4f62df0fe48755b897881c39"
, "0x19903aa449fe9c27ee9c8320e6915b50c2822e61ce894be72b47a449c5705762"
, "0x126e801aae44016a35deceaa3eba6ccc341fa3c2a65ab3d021fcd39abd170e1b"
, "0x1b0a98be27b54ac9d5d72b94187c991c1872cb2c7777c0e880f439c133971e8d"
, "0x1e10a35afda2e5a173d4f3edecf29dacf51d8fac33d6bfb4088cc787ec647605"
, "0x1793cda85abe2782ea8e911ce92bab59a8c68e0dd561a57b064bb233f109cc57"
]
#-------------------------------------------------------------------------------

View File

@ -1,8 +1,11 @@
version = "0.1.0"
author = "nim-poseidon2 authors"
version = "0.1.0"
author = "nim-poseidon2 authors"
description = "Poseidon2 hash function"
license = "MIT"
license = "MIT"
requires "https://github.com/mratsim/constantine#bc3845aa492b52f7fef047503b1592e830d1a774"
installExt = @["nim"]
bin = @["bench/bench_perm"]
#requires "https://github.com/mratsim/constantine#bc3845aa492b52f7fef047503b1592e830d1a774"
requires "https://github.com/mratsim/constantine#782d838e7a073262750eff593af6dfff3ff832dd"
bin = @["bench/bench_perm", "bench/bench_griffin"]

View File

@ -22,6 +22,7 @@ func getZero*() : F =
const zero* : F = getZero()
const one* : F = fromHex(F,"0x01") # note: `fromUint()` does not work at compile time
const two* : F = fromHex(F,"0x02")
const twoToThe64* : F = fromHex(F,"0x10000000000000000")
@ -39,5 +40,32 @@ func arrayFromHex*[N](
tmp[i] = hexToF(inp[i], endian)
return tmp
#-------------------------------------------------------------------------------
func `+`*(x, y: F): F = ( var z: F = x ; z += y ; return z )
func `-`*(x, y: F): F = ( var z: F = x ; z -= y ; return z )
func `*`*(x, y: F): F = ( var z: F = x ; z *= y ; return z )
func `==`*(a, b: F): bool =
bool(arithmetic.`==`(a, b))
func sqr*(x : F): F =
var y = x
y.square()
return y
#-------------------------------------------------------------------------------
func fastPow*(base: F, expo: B): F =
var s : F = base
var a : F = one
var e : B = expo
for i in 0..<254:
if bool(isOdd(e)):
a *= s
s.square()
e.div2()
return a
#-------------------------------------------------------------------------------