Skip to content

Commit

Permalink
add sponge construction
Browse files Browse the repository at this point in the history
  • Loading branch information
bkomuves committed Nov 7, 2023
1 parent 5e980dd commit 2ace304
Show file tree
Hide file tree
Showing 4 changed files with 166 additions and 46 deletions.
101 changes: 57 additions & 44 deletions poseidon2.nim
Original file line number Diff line number Diff line change
@@ -1,53 +1,13 @@
import
constantine/math/arithmetic,
constantine/math/config/curves
import constantine/math/arithmetic

import poseidon2/types
import poseidon2/roundconst
import poseidon2/roundfun
import poseidon2/io

#-------------------------------------------------------------------------------

const zero : F = getZero()

const externalRoundConst : array[24, F] = arrayFromHex( externalRoundConstStr )
const internalRoundConst : array[56, F] = arrayFromHex( internalRoundConstStr )

#-------------------------------------------------------------------------------

# inplace sbox, x => x^5
func sbox(x: var F) : void =
var y = x
square(y)
square(y)
x *= y

func linearLayer(x, y, z : var F) =
var s = x ; s += y ; s += z
x += s
y += s
z += s

func internalRound(j: int; x, y, z: var F) =
x += internalRoundConst[j]
sbox(x)
var s = x ; s += y ; s += z
double(z)
x += s
y += s
z += s

func externalRound(j: int; x, y, z : var F) =
x += externalRoundConst[3*j+0]
y += externalRoundConst[3*j+1]
z += externalRoundConst[3*j+2]
sbox(x) ; sbox(y) ; sbox(z)
var s = x ; s += y ; s += z
x += s
y += s
z += s

func permInplace*(x, y, z : var F) =
# the Poseidon2 permutation (mutable, in-place version)
proc permInplace*(x, y, z : var F) =
linearLayer(x, y, z);
for j in 0..3:
externalRound(j, x, y, z)
Expand All @@ -56,13 +16,64 @@ func permInplace*(x, y, z : var F) =
for j in 4..7:
externalRound(j, x, y, z)

# the Poseidon2 permutation
func perm*(xyz: S) : S =
var (x,y,z) = xyz
permInplace(x, y, z)
return (x,y,z)

#-------------------------------------------------------------------------------

# sponge with rate=1 (capacity=2)
func spongeWithRate1*(xs: openArray[F]) : F =
let a = low(xs)
let b = high(xs)
let n = b-a+1

var s0 : F = zero
var s1 : F = zero
var s2 : F = zero

for i in 0..<n:
s0 += xs[a+i]
permInplace(s0,s1,s2)

# padding
s0 += one;
permInplace(s0,s1,s2)
return s0

# sponge with rate=2 (capacity=1)
func spongeWithRate2*(xs: openArray[F]) : F =
let a = low(xs)
let b = high(xs)
let n = b-a+1
let halfn : int = n div 2

var s0 : F = zero
var s1 : F = zero
var s2 : F = zero

for i in 0..<halfn:
s0 += xs[a+2*i ]
s1 += xs[a+2*i+1]
permInplace(s0,s1,s2)

if (2*halfn == n):
# padding even input
s0 += one
s1 += zero
else:
# padding odd input
s0 += xs[b]
s1 += one

permInplace(s0,s1,s2)
return s0

#-------------------------------------------------------------------------------

# 2-to-1 compression
func compress*(a, b : F) : F =
var x = a
var y = b
Expand Down Expand Up @@ -99,3 +110,5 @@ func merkleRoot*(xs: openArray[F]) : F =

func merkleRoot*(bytes: openArray[byte]): F =
merkleRoot(seq[F].unmarshal(bytes))

#-------------------------------------------------------------------------------
51 changes: 51 additions & 0 deletions poseidon2/roundfun.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import
constantine/math/arithmetic,
constantine/math/config/curves

import ./types
import ./roundconst

#-------------------------------------------------------------------------------

const zero* : F = getZero()
const one* : F = getOne()

const externalRoundConst : array[24, F] = arrayFromHex( externalRoundConstStr )
const internalRoundConst : array[56, F] = arrayFromHex( internalRoundConstStr )

#-------------------------------------------------------------------------------

# inplace sbox, x => x^5
func sbox*(x: var F) : void =
var y = x
square(y)
square(y)
x *= y

func linearLayer*(x, y, z : var F) =
var s = x ; s += y ; s += z
x += s
y += s
z += s

func internalRound*(j: int; x, y, z: var F) =
x += internalRoundConst[j]
sbox(x)
var s = x ; s += y ; s += z
double(z)
x += s
y += s
z += s

func externalRound*(j: int; x, y, z : var F) =
x += externalRoundConst[3*j+0]
y += externalRoundConst[3*j+1]
z += externalRoundConst[3*j+2]
sbox(x) ; sbox(y) ; sbox(z)
var s = x ; s += y ; s += z
x += s
y += s
z += s

#-------------------------------------------------------------------------------

12 changes: 10 additions & 2 deletions poseidon2/types.nim
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@

import
constantine/math/arithmetic,
constantine/math/io/io_fields,
constantine/math/io/io_bigints,
constantine/math/arithmetic,
constantine/math/config/curves

#-------------------------------------------------------------------------------
Expand All @@ -18,9 +18,17 @@ func getZero*() : F =
setZero(z)
return z

func getOne*() : F =
var y : F
# y.fromUint(1'u32) # WTF, why does this not compile ???
y.fromHex("0x01")
return y

# for some reason this one does not compile... ???
# (when actually called)
func toF*(a: int) : F =
var y : F
fromInt(y, a);
y.fromInt(a)
return y

func hexToF*(s : string, endian: static Endianness = bigEndian) : F =
Expand Down
48 changes: 48 additions & 0 deletions tests/poseidon2/testPoseidon2.nim
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,36 @@ import constantine/math/config/curves
import poseidon2/types
import poseidon2

#-------------------------------------------------------------------------------

const expectedSpongeResultsRate1 : array[8, string] =
[ "12363515589665961836680709257448433057869762330741639517836048636244832188495"
, "10755250120808789043370150604836786069442045362641800439807384337872752972068"
, "04842014531366721455661330916203255410159059117951668762867230544004815370337"
, "13502515636936876459766686836354199651004594178376827739246669803080321705927"
, "19312121576697000598919845239663673946550934099828684806027699882665482322097"
, "21509595983900483103260021285060939918324350560398732346653142062765920502059"
, "11892726572958426459775026381831352388154613015696290329810000571844227402585"
, "10284126944232604349630438079200913190801781418325975675236599364113149409058"
]

# TODO: add domain separation between rate=1 and rate=2, so that the empty input
# gives different results. But this has to be done in all the other Poseidon2 libraries
# too (circom, Haskell, C...)

const expectedSpongeResultsRate2 : array[8, string] =
[ "12363515589665961836680709257448433057869762330741639517836048636244832188495"
, "00899009032366875286186953183805404053380636995610127460025486428583509745414"
, "16500906802543951227422597869354004883060519121579073949799015758201044544012"
, "05275430613748165078459451567241807462288293965310307668712900802458919462965"
, "13763559248248167400098483085605230840597893317332127197498651878933380690961"
, "14871143128308815290845020646262475973102494373985615216162863857354721038367"
, "02746725081632011689597680224823496636241961292066939394613880404914874634920"
, "02290144245981244996669076598332792758523446545263085369617640761875376727694"
]

#-------------------------------------------------------------------------------

suite "poseidon2":

test "permutation in place":
Expand All @@ -23,6 +53,24 @@ suite "poseidon2":
check toDecimal(y) == "09030699330013392132529464674294378792132780497765201297316864012141442630280"
check toDecimal(z) == "09137931384593657624554037900714196568304064431583163402259937475584578975855"

test "sponge with rate=1":
for n in 0..7:
var xs: seq[F]
for i in 1..n:
xs.add( toF(i) )
let h = spongeWithRate1(xs)
# echo(toDecimal(h))
check toDecimal(h) == expectedSpongeResultsRate1[n]

test "sponge with rate=2":
for n in 0..7:
var xs: seq[F]
for i in 1..n:
xs.add( toF(i) )
let h = spongeWithRate2(xs)
# echo(toDecimal(h))
check toDecimal(h) == expectedSpongeResultsRate2[n]

test "merkle root of field elements":
let m = 17
let n = 2^m
Expand Down

0 comments on commit 2ace304

Please sign in to comment.