Poseidon

August 6, 2024 · View on GitHub

\def\Zp{{\mathbb{Z}_p}} \def\Zbin{{\mathbb{Z}_{2^m}}} \def\thin{{\thinspace}} \def\Byte{{\mathbb{B}}} \def\Bit{{\{0, 1\}}} \def\typecolon{\mathbin{\large :}} \def\neg{{\text{-}}} \def\constb{{\textbf{const }}} \def\as{{\textbf{ as }}} \def\msb{{\textsf{msb}}} \def\if{{\text{if }}} \def\ifb{{\textbf{if } \hspace{1pt}}} \def\bi{{\ \ }} \def\FieldBits{{\text{FieldBits}}} \def\SboxBits{{\text{SboxBits}}} \def\RoundConstants{{\text{RoundConstants}}} \def\hso{{\hspace{1pt}}} \def\Function{{\textbf{Function: }}} \def\init{{\textsf{init}}} \def\for{{\textbf{for }}} \def\foreach{{\textbf{for each }}} \def\bcolon{{\hspace{1pt} \textbf{:}}} \def\state{{\textsf{state}}} \def\xor{\oplus_\text{xor}} \def\bit{{\textsf{bit}}} \def\bits{{\textsf{bits}}} \def\line#1{{{\small \rm \rlap{#1.}\hphantom{10.}} \ \ }} \def\while{{\textbf{while }}} \def\State{{\text{State}}} \def\pluseq{\mathrel{+}=} \def\return{{\textbf{return }}} \def\else{{\textbf{else}}} \def\len{{\textbf{len}}} \def\preimage{{\textsf{preimage}}} \def\DomainTag{{\text{DomainTag}}} \def\Arity{{\text{Arity}}} \def\HashType{{\text{HashType}}} \def\MerkleTree{{\text{MerkleTree}}} \def\ConstInputLen{{\text{ConstInputLen}}} \def\Mds{{\mathcal{M}}} \def\la{{\langle}} \def\ra{{\rangle}} \def\xb{{\textbf{x}}} \def\yb{{\textbf{y}}} \def\dotdot{{{\ldotp}{\ldotp}}} \def\do{{\textbf{do }}} \def\timesb{{\textbf{ times}}} \def\RC{{\text{RoundConstants}}} \def\Padding{{\text{Padding}}} \def\push{{\textbf{.push}}} \def\dotprod{{{\boldsymbol\cdot} \hso}} \def\row{{\textsf{row}}} \def\acc{{\textsf{acc}}} \def\push{{\textbf{.push}}} \def\extend{{\textbf{.extend}}} \def\reverse{{\textbf{reverse}}} \def\MdsInv{{\mathcal{M}^{\text{-} 1}}} \def\Padding{{\text{Padding}}} \def\Pre{{\mathcal{P}}} \def\Sparse{{\mathcal{S}}} \def\wb{{\textbf{w}}} \def\AtimesB{{(A \times B)}}

[TOC]

Poseidon

General Notation

x\typecolonTx \typecolon \mathbb{T} A variable xx having type T\mathbb{T}.

v\typecolonT[n]v \typecolon \mathbb{T}^{[n]} An array of nn elements each of type T\mathbb{T}.

A\typecolonT[n×n]A \typecolon \mathbb{T}^{[n \times n]} An n×nn {\times} n matrix having elements of type T\mathbb{T}.

In\typecolonT[n×n]\mathcal{I}_n \typecolon \mathbb{T}^{[n \times n]} The n×nn {\times} n identity matrix.

v\typecolonT[n×n][m]v \typecolon {\mathbb{T}^{[n \times n]}}^{[m]} An array of mm matrices.

b\typecolon\Bitb \typecolon \Bit A bit bb.

\bits\typecolon\Bit[n]\bits \typecolon \Bit^{[n]} An array \bits\bits of nn bits.

x\typecolon\Zpx \typecolon \Zp A prime field element xx.

xZnx \in \mathbb{Z}_{n} An integer in x[0,n)x \in [0, n).

x\typecolonZ0x \typecolon \mathbb{Z}_{\geq 0} A non-negative integer.

x\typecolonZ>0x \typecolon \mathbb{Z}_{>0} A positive integer.

[n][n] The range of integers $0, \dots, n - 1 \hso.Notethat. Note that [n] = [0, n)$.

[a,b)[a, b) The range of integers a,,b1\hsoa, \dots, b - 1 \hso.

Array Operations

Note: all arrays and matrices are indexed starting at zero. An array of nn elements has indices $0, \dots, n - 1$.

v[i]v[i] Returns the ithi^{th} element of array vv. When the notation v[i]v[i] is cumbersome viv_i is used instead.

v[i\dotdotj]v[i \dotdot j] Returns a slice of the array vv: [v[i],,v[j1]][v[i], \dots, v[j {-} 1]].

vwv \parallel w Concatenates two arrays vv and ww, of types T[m]\mathbb{T}^{[m]} and T[n]\mathbb{T}^{[n]} respectively, producing an array of type T[m+n]\hso\mathbb{T}^{[m + n]} \hso. The above is equivalent to writing [v0,,vm1,w0,,wn1][v_0, \dots, v_{m - 1}, w_0, \dots, w_{n - 1}].

[f()]i{1,2,3}[f(\dots)]_{i \in \lbrace 1, 2, 3 \rbrace} Creates an array using list comprehension; each element of the array is the output of the expression f()f(\dots) at each element of the input sequence (e.g. i{1,2,3}i \in \lbrace 1, 2, 3 \rbrace).

vwv \mathbin{\vec\oplus} w Element-wise field addition of two equally lengthed vectors of field elements v,w\typecolon\Zp[n]v, w \typecolon \Zp^{[n]}. The above is equivalent to writing [v0w0,,vn1wn1][v_0 \oplus w_0, \dots, v_{n - 1} \oplus w_{n - 1}].

\reverse(v)\reverse(v) Reverses the elements of a vector vv, i.e. the first element of \reverse(v)\reverse(v) is the last element of vv.

Matrix Operations

Ai,jA_{i, j} Returns the value of matrix AA at row ii column jj.

Ai,A_{i, \ast} Returns the ithi^{th} row of matrix AA.

A,jA_{\ast, j} Returns the jthj^{th} column of matrix AA.

A1\dotdot,1\dotdot=[A1,1A1,c1Ar1,1Ar1,c1]A_{1 \dotdot, 1 \dotdot} = \begin{bmatrix} A_{1, 1} & \dots & A_{1, c - 1} \\ \vdots & \ddots & \vdots \\ A_{r - 1, 1} & \dots & A_{r - 1, c - 1} \end{bmatrix} Returns a submatrix of mm which excludes mm's first row and first column (here mm is an r×cr {\times} c matrix).

A×BA \times B Matrix-matrix multiplication of two matrices of field elements A\typecolon\Zp[m×n]A \typecolon \Zp^{[m \times n]} and B\typecolon\Zp[n×k]B \typecolon \Zp^{[n \times k]} which produces a matrix of type \Zp[m×k]\Zp^{[m \times k]}. Note that \AtimesBi,j=Ai,B,j\AtimesB_{i, j} = A_{i, \ast} \boldsymbol\cdot B_{\ast, j} where the dot product uses field multiplication.

A¬1A^{\neg 1} The inverse of a square n×nn {\times} n matrix, i.e. returns the matrix such that A×A¬1=InA \times A^{\neg 1} = \mathcal{I}_n

v×A=[v0,,vm1][A0,0A0,n1Am1,0Am1,n1]=[vA,i]i[n]\textbf{v} \times A = [\textbf{v}_0, \dots, \textbf{v}_{m - 1}] \begin{bmatrix} A_{0, 0} & \dots & A_{0, n - 1} \\ \vdots & \ddots & \vdots \\ A_{m - 1, 0} & \dots & A_{m - 1, n - 1} \end{bmatrix} = [\textbf{v} \boldsymbol\cdot A_{\ast, i}]_{i \in [n]} Vector-matrix multiplication of a row vector of field elements v\typecolon\Zp[m]\textbf{v} \typecolon \Zp^{[m]} with a matrix of field elements m\typecolon\Zp[m×n]m \typecolon \Zp^{[m \times n]}, note that \len(v)=rows(A)\len(\textbf{v}) = \textbf{rows}(A) and \len(v×A)=columns(A)\len(\textbf{v} \times A) = \textbf{columns}(A). The product is a row vector of length nn (the number of matrix columns). The ithi^{th} element of the product vector is the dot product of v\textbf{v} and the ithi^{th} column of AA. Note that dot products use field multiplication.

A×v=[A0,0A0,n1Am1,0Am1,n1][v0vn1]=[A0,vAm1,v]A \times \textbf{v} = \begin{bmatrix} A_{0, 0} & \dots & A_{0, n - 1} \\ \vdots & \ddots & \vdots \\ A_{m - 1, 0} & \dots & A_{m - 1, n - 1} \end{bmatrix} \begin{bmatrix} \textbf{v}_0 \\ \vdots \\ \textbf{v}_{n - 1} \end{bmatrix} = \begin{bmatrix} A_{0, \ast} \boldsymbol\cdot \textbf{v} \\ \vdots \\ A_{m - 1, \ast} \boldsymbol\cdot \textbf{v} \end{bmatrix} Matrix-vector multiplication of a volume vector v\typecolonT[m×1]v \typecolon \mathbb{T}^{[m \times 1]} and matrix A\typecolonT[m×n]A \typecolon \mathbb{T}^{[m \times n]}, note that rows(v)=columns(A)\textbf{rows}(\textbf{v}) = \textbf{columns}(A) and rows(A×v)=rows(A)\textbf{rows}(A \times \textbf{v}) = \textbf{rows}(A). The product is a column vector whose length is equal to the number of rows of AA. The ithi^{th} element of the product vector is the dot product of the ithi^{th} row of AA with v\textbf{v}. Note that dot products use field multiplication.

Note: v×A=(A×v)T\textbf{v} \times A = (A \times \textbf{v})^T when AA is symmetric A=ATA = A^T (the ithi^{th} row of AA equals the ithi^{th} column of AA), i.e. the row vector-matrix product and matrix-column vector product contain the same elements when AA is symmetric.

Field Arithmetic

aba \oplus b Addition in \Zp\Zp of two field elements aa and bb.

xαx^\alpha Exponentiation in \Zp\Zp of a field element xx to an integer power α\alpha.

Bitwise Operations

xor\oplus_\text{xor} Bitwise XOR.

xor i{1,2,3}\hsoi\bigoplus_{\text{xor} \ i \in \lbrace 1, 2, 3 \rbrace} \hso i XOR's all values of a sequence. The above is equivalent to writing $1 \oplus_\text{xor} 2 \oplus_\text{xor} 3$.

Bitstrings

[1,0,0]=1002[1, 0, 0] = 100_2 A bit array can be written as an array [1,0,0][1, 0, 0] or as a bitstring $100_2$. The leftmost bit of the bitstring corresponds to the first bit in the array.

Binary-Integer Conversions

Note: the leftmost digit of an integer x\typecolon\Zp0x \typecolon \Zp_{\geq 0} is the most significant, thus a right-shift by nn bits is defined: xn=x/2nx \gg n = x / 2^n.

x\as\Bit\msb[n]x \as \Bit_\msb^{[n]} Converts an integer x\typecolonZ0x \typecolon \mathbb{Z}_{\geq 0} into its nn-bit binary representation. The most-significant bit (\msb\msb) is first (leftmost) in the produced bit array \Bit[n]\Bit^{[n]}. The above is equivalent to writing \reverse([(xi)1]i[log2(x)])\reverse([(x \gg i) \mathbin\wedge 1]_{i \in [\lceil \log_2(x) \rceil]}). For example, $6 \as \Bit_\msb^{[3]} = [1, 1, 0]$.

\bits\msb\asZ0\bits_\msb \as \mathbb{Z}_{\geq 0} Converts a bit array \bits\typecolon\Bit[n]\bits \typecolon \Bit^{[n]} into a unsigned (non-negative) integer where the first bit in \bits\bits is the most significant (\msb\msb). The above is equivalent to writing i[n]2i\reverse(\bits)[i]\sum_{i \in [n]} 2^i * \reverse(\bits)[i].

Poseidon-Specific Symbols

p\typecolonZ>0p \typecolon \mathbb{Z}_{> 0} The prime field modulus.

M{80,128,256}M \in \lbrace 80, 128, 256 \rbrace The security level measured in bits. Poseidon allows for 80, 128, and 256-bit security levels.

t\typecolonZ>0=\len(\preimage)+\len(digest)=\len(\preimage)+2Mlog2(p)t \typecolon \mathbb{Z}_{> 0} = \len(\preimage) + \len(\text{digest}) = \len(\preimage) + \left\lceil {2M \over \log_2(p)} \right\rceil The width of a Poseidon instance; the length in field elements of an instance's internal \state\state array. The width tt is equal to the preimage length plus the output length, where output length is equal to the number of field elements 2Mlog2(p)\left\lceil {2M \over \log_2(p)} \right\rceil required to achieve the targeted security level MM in a field of size log2(p)\log_2(p). Stated another way, each field element in a Poseidon digest provides and additional log2(p)2\log_2(p) \over 2 bits of security.

(p,M,t)(p, M, t) A Poseidon instance. Each instance is fully specified using this parameter triple.

α{¬1,3,5}\alpha \in \lbrace \neg 1, 3, 5 \rbrace The S-box function's exponent S(x)=xαS(x) = x^\alpha, where gcd(α,p1)=1\gcd(\alpha, p - 1) = 1. Poseidon allows for exponents -1, 3, and 5.

RF\typecolonZ>0R_F \typecolon \mathbb{Z}_{> 0} The number of full rounds. RFR_F is even.

RP\typecolonZ>0R_P \typecolon \mathbb{Z}_{> 0} The number of partial rounds.

R=RF+RPR = R_F + R_P The total number of rounds

Rf=RF/2R_f = R_F / 2 Half the number of full rounds.

r[R]r \in [R] The index of a round.

r[Rf]r \in [R_f] The round index for a first-half full round.

r[Rf+RP,R)r \in [R_f + R_P, R) The round index for a second-half full round.

r[Rf,Rf+RP)r \in [R_f, R_f + R_P) The round index for a partial round.

\state\typecolon\Zp[t]\state \typecolon \Zp^{[t]} A Poseidon instance's internal state array of tt field elements which are transformed in each round.

\RC\typecolon\Zp[Rt]\RC \typecolon \Zp^{[Rt]} The round constants for an unoptimized Poseidon instance.

\RCr\typecolon\Zp[t]\RC_r \typecolon \Zp^{[t]} The round constants for round r[R]r \in [R] for an unoptimized Poseidon instance, that are added to \state\state before round rr's S-boxes.

\RC\typecolon\Zp[tRF+RP]=\RCpre\RC1 \RCR2\RC' \typecolon \Zp^{[tR_F + R_P]} = \RC_\text{pre}' \parallel \RC_1' \parallel \ldots \parallel \ \RC_{R - 2}' The round constants for an optimized Poseidon instance. There are no constants associated with the last full round r=R1r = R - 1.

\RCpre\typecolon\Zp[t]\RC'_\text{pre} \typecolon \Zp^{[t]} The round constants that are added to Poseidon's \state\state array before the S-boxes in the first round r=0r = 0 of an optimized Poseidon instance.

\RCr\typecolon{\Zp[1]\ifr[Rf,Rf+RP)i.e. r is a partial round \Zp[t]\ifr[Rf] or r[Rf+RP,R1)i.e. r is a full round, excluding the last round\RC'_r \typecolon \begin{cases} \Zp^{[1]} & \if r \in [R_f, R_f + R_P) & \text{i.e. } r \text{ is a partial round } \cr \Zp^{[t]} & \if r \in [R_f] \text{ or } r \in [R_f + R_P, R - 1) & \text{i.e. } r \text{ is a full round, excluding the last round} \end{cases} The round constants that are added to Poseidon's \state\state array after the S-boxes in round rr in an optimized Poseidon instance. Partial rounds have a single round constant, full rounds (excluding the last) have tt constants. The last full round has no round constants.

\Mds\typecolon\Zp[t×t]\Mds \typecolon \Zp^{[t \times t]} The MDS matrix for a Poseidon instance.

\Pre\typecolon\Zp[t×t]\Pre \typecolon \Zp^{[t \times t]} The pre-sparse matrix used in MDS mixing for the last first-half full round (r=Rf1r = R_f - 1) of an optimized Poseidon instance.

\Sparse\typecolon\Zp[t×t][RP]\Sparse \typecolon {\Zp^{[t \times t]}}^{[R_P]} An array of sparse matrices used in MDS mixing for the partial rounds r[Rf,Rf+RP)r \in [R_f, R_f + R_P) of the optimized Poseidon algorithm.

Poseidon Instantiation

The parameter triple (p,M,t)(p, M, t) fully specifies a unique instance of Poseidon (a hash function that uses the same constants and parameters and performs the same operations). All other Poseidon parameters and constants are derived from the instantiation parameters.

The S-box exponent α\alpha is derived from the field modulus pp such that a{3,5}a \in \lbrace 3, 5 \rbrace and gcd(α,p1)=1\gcd(\alpha, p - 1) = 1.

The round numbers RFR_F and RPR_P are derived from the field size and security level (log2(p),M)(\lceil \log_2(p) \rceil, M).

The \RC\RC are derived from (p,M,t)(p, M, t).

The MDS matrix \Mds\Mds is derived from the width tt.

The allowed preimage sizes are \len(\preimage)[1,t)\len(\preimage) \in [1, t).

The total number of operations executed per hash is determined by the width and number of rounds (t,RF,RP)(t, R_F, R_P).

Filecoin's Poseidon Instances

Note: the following are the Poseidon instantiation parameters used within Filecoin and do not represent all possible Poseidon instances.

p=52435875175126190479447740508185965837690552500527637822603658699938581184513p = 52435875175126190479447740508185965837690552500527637822603658699938581184513 p=0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001\hphantom{p} = \text{0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001} The Poseidon prime field modulus in base-10 and base-16. Filecoin uses BLS12-381's scalar field as the Poseidon prime field \Zp\Zp, i.e. pp is the order of BLS12-381's prime order subgroup G1\mathbb{G}_1. The bit-length of pp is log2(p)=255256\lceil \log_2(p) \rceil = 255 \approx 256 bits.

M=128 BitsM = 128 \ \text{Bits} Filecoin targets the 128-bit security level.

t{3,5,9,12}={arity+1arity{2,4,8,11}}t \in \lbrace 3, 5, 9, 12 \rbrace = \lbrace \text{arity} + 1 \mid \text{arity} \in \lbrace 2, 4, 8, 11 \rbrace \rbrace The size in field elements of Poseidon's internal state; equal to the preimage length (a Filecoin Merkle tree arity) plus 1 for the digest length (the number of field elements required to target the M=128M = 128-bit security level via a 256-bit prime pp). Filecoin's Poseidon instances take preimages of varying lengths (2, 4, 8, and 11 field elements) and always return one field element.

  • t=3t = 3 is used to hash 2:1 Merkle trees (BinTrees) and to derive SDR-PoRep's CommR\text{CommR}
  • t=5t = 5 is used to hash 4:1 Merkle trees (QuadTrees)
  • t=9t = 9 is used to hash 8:1 Merkle trees (OctTrees)
  • t=12t = 12 is used to hash SDR-PoRep columns of 11 field elements.

α=5\alpha = 5 The S-box function's S(x)=xαS(x) = x^\alpha exponent. It is required that α\alpha is relatively prime to pp, which is true for Filecoin's choice of pp.

Round Numbers

The Poseidon round numbers are the number of full and partial rounds (RF,RP)(R_F, R_P) for a Poseidon instance (p,M,t)(p, M, t). The round numbers are chosen such that they minimize the total number of S-boxes:

Number of S-boxes=tRF+RP\text{Number of S-boxes} = tR_F + R_P

while providing security against known attacks (statistical, interpolation, and Gröbner basis).

\constb R_F, R_P = \texttt{calc_round_numbers}(p, M, t, \alpha) The number of full and partial rounds, both are positive integers RF,RP\typecolonZ>0R_F, R_P \typecolon \mathbb{Z}_{>0} and RFR_F is even.

RFR_F and RPR_P are calculated using either the Python script calc_round_numbers.py or the neptune Rust library, denoted \texttt{calc_round_numbers}. Both methods calculate the round numbers via brute-force; by iterating over all reasonable values for RFR_F and RPR_P and choosing the pair that satisfies the security inequalities (provided below) while minimizing the number of S-boxes.

Security Inequalities

The round numbers RFR_F and RPR_P are chosen such that they satisfy the following inequalities. The symbol \Longrightarrow is used to indicate that an inequality simplifies when Filecoin's Poseidon parameters M=128M = 128 and α=5\alpha = 5 are plugged in.

(1)2Mpt(1) \quad 2^M \leq p^t Equivalent to writing Mtlog2(p)M \leq t\log_2(p) (Appendix C.1.1 in the Poseidon paper). This is always satisfied for Filecoin's choice of pp and MM.

(2)Rf6(2) \quad R_f \geq 6 The minimum RFR_F necessary to prevent statistical attacks (Eq. 2 Section 5.5.1 in the Poseidon paper where log2(p)2=252\lfloor \log_2(p) \rfloor - 2 = 252 and C=2\mathcal{C} = 2 for α=5\alpha = 5).

(3)R>Mlogα(2)+logα(t)    R>{57\ift[2,5]58\ift[6,25](3) \quad R > \lceil M \log_\alpha(2) \rceil + \lceil \log_\alpha(t) \rceil \ \ \Longrightarrow \ \ R > \begin{cases} 57 & \if t \in [2, 5] \cr 58 & \if t \in [6, 25] \end{cases} The minimum number of total rounds necessary to prevent interpolation attacks (Eq. 3 Section 5.5.2 of the Poseidon paper).

(4a)R>Mlogα(2)3    R>18.3(4 \text{a}) \quad R > {M \log_\alpha(2) \over 3} \ \ \Longrightarrow \ \ R > 18.3 (4b)R>t1+Mlogα(2)t+1(4 \text{b}) \quad R > t - 1 + {M \log_\alpha(2) \over t + 1} The minimum number of total rounds required to prevent against Gaussian elimination attacks (both equations must be satisfied, Eq. 5 from Section 5.5.2 of the Poseidon paper).

Round Constants

Note: this section gives the round constants for only the unoptimized Poseidon algorithm.

Round Constants

\constb\RC\typecolon\Zp[Rt]\constb \RC \typecolon \Zp^{[Rt]} For each Poseidon instance (p,M,t)(p, M, t) an array \RC\RC containing RtRt field elements is generated (tt field elements per round r[R]r \in [R]) using the Grain-LFSR stream cipher whose 80-bit state is initialized to GrainState\init\text{GrainState}_\init, an encoding of the Poseidon instance.

\constb\RCr\typecolon\Zp[t]=\RC[rt\dotdot(r+1)t]\constb \RC_r \typecolon \Zp^{[t]} = \RC[rt \dotdot (r + 1)t] The round constants for round r[R]r \in [R] for an unoptimized Poseidon instance.

\constb\FieldBits\typecolon\Bit\msb[2]={0if using a binary field \Zbin1if using a prime field \Zp=012\constb \FieldBits \typecolon \Bit^{[2]}_\msb = \begin{cases} 0 & \text{if using a binary field } \Zbin \\ 1 & \text{if using a prime field } \Zp \end{cases} = 01_2 Specifies the field type as prime or binary. Filecoin always uses a prime field \Zp\Zp, however Poseidon also can be instantiated using a binary field \Zbin\Zbin.

\constb\SboxBits\typecolon\Bit\msb[4]={0\ifα=31\ifα=52\ifα=¬1=00012\constb \SboxBits \typecolon \Bit^{[4]}_\msb = \begin{cases} 0 & \if \alpha = 3 \cr 1 & \if \alpha = 5 \cr 2 & \if \alpha = \neg 1 \end{cases} = 0001_2 Specifies the S-box exponent α\alpha. Filecoin uses α=5\alpha = 5.

\constbFieldSizeBits\typecolon\Bit\msb[12]=log2(p)=255=0000111111112\constb \text{FieldSizeBits} \typecolon \Bit^{[12]}_\msb = \lceil \log_2(p) \rceil = 255 = 000011111111_2 The bit-length of the field modulus.

\constbGrainStateinit\typecolon\Bit[80]=FieldBits \SboxBits FieldSizeBits t\as\Bit\msb[12] RF\as\Bit\msb[10] RP\as\Bit\msb[10] 1[30]\constb \text{GrainState}_\text{init} \typecolon \Bit^{[80]} = \\ \quad \text{FieldBits} \\ \quad \Vert \ \SboxBits \\ \quad \Vert \ \text{FieldSizeBits} \\ \quad \Vert \ t \as \Bit^{[12]}_\msb \\ \quad \Vert \ R_F \as \Bit^{[10]}_\msb \\ \quad \Vert \ R_P \as \Bit^{[10]}_\msb \\ \quad \Vert \ 1^{[30]} Initializes the Grain-LFSR stream cipher which is used to derive \RC\RC for a Poseidon instance (p,M,t)(p, M, t).

Algorithm: \RC\line1\state\typecolon\Bit[80]=GrainState\init\line2do 160\timesb\bcolon\line3\bit\typecolon\Bit=xor i\hso{0,13,23,38,51,62}\thin\state[i]\line4\state=\state[1\dotdot]\bit\line5\RC\typecolon\Zp[Rt]=[ ]\line6\while\len(\RC)<Rt\bcolon\line7\bits\typecolon\Bit[255]=[ ]\line8\while\len(\bits)<255\bcolon\line9\bit1=xor i\hso{0,13,23,38,51,62}\thin\state[i]\line10\state=\state[1\dotdot]\bit1\line11\bit2=xor i\hso{0,13,23,38,51,62}\thin\state[i]\line12\state=\state[1\dotdot]\bit2\line13\ifb\bit1=1\bcolon\line14\bits\push(\bit2)\line15c=\bits\msb\asZ2255\line16\ifc\Zp\bcolon\line17\RC\push(c)\line18\return\RC\overline{\underline{\textbf{Algorithm: } \RC}} \\ \line{1} \state \typecolon \Bit^{[80]} = \text{GrainState}_\init \\ \line{2} \textbf{do } 160 \timesb \bcolon \\ \line{3} \quad \bit \typecolon \Bit = \bigoplus_{\text{xor} \ i \hso \in \lbrace 0, 13, 23, 38, 51, 62 \rbrace} \thin \state[i] \\ \line{4} \quad \state = \state[1 \dotdot] \parallel \bit \\ \line{5} \RC \typecolon \Zp^{[Rt]} = [\ ] \\ \line{6} \while \len(\RC) < Rt \bcolon \\ \line{7} \quad \bits \typecolon \Bit^{[255]} = [\ ] \\ \line{8} \quad \while \len(\bits) < 255 \bcolon \\ \line{9} \quad\quad \bit_1 = \bigoplus_{\text{xor} \ i \hso \in \lbrace 0, 13, 23, 38, 51, 62 \rbrace} \thin \state[i] \\ \line{10} \quad\quad \state = \state[1 \dotdot] \parallel \bit_1 \\ \line{11} \quad\quad \bit_2 = \bigoplus_{\text{xor} \ i \hso \in \lbrace 0, 13, 23, 38, 51, 62 \rbrace} \thin \state[i] \\ \line{12} \quad\quad \state = \state[1 \dotdot] \parallel \bit_2 \\ \line{13} \quad\quad \ifb \bit_1 = 1 \bcolon \\ \line{14} \quad\quad\quad \bits\push(\bit_2) \\ \line{15} \quad c = \bits_\msb \as \mathbb{Z}_{2^{255}} \\ \line{16} \quad \if c \in \Zp \bcolon \\ \line{17} \quad\quad \RC\push(c) \\ \line{18} \return \RC

MDS Matrix

\constb\xb\typecolon\Zp[t]=[0,,t1]\constb\yb\typecolon\Zp[t]=[t,,2t1] \constb\Mds\typecolon\Zp[t×t]=[(\xb0+\yb0)¬1(\xb0+\ybt1)¬1(\xbt1+\yb0)¬1(\xbt1+\ybt1)¬1]\constb \xb \typecolon \Zp^{[t]} = [0, \dots, t - 1] \\ \constb \yb \typecolon \Zp^{[t]} = [t, \dots, 2t - 1] \\~ \\ \constb \Mds \typecolon \Zp^{[t \times t]} = \begin{bmatrix} (\xb_0 + \yb_0)^{\neg 1} & \dots & (\xb_0 + \yb_{t - 1})^{\neg 1} \\ \vdots & \ddots & \vdots \\ (\xb_{t - 1} + \yb_0)^{\neg 1} & \dots & (\xb_{t - 1} + \yb_{t - 1})^{\neg 1} \end{bmatrix} The MDS matrix \Mds\Mds for a Poseidon instance of width tt. The superscript ¬1^{\neg 1} denotes a multiplicative inverse mod p\text{mod } p. The MDS matrix is invertible and symmetric.

Domain Separation

Every preimage hashed is associated with a hash type \HashType\HashType to encode the Poseidon application, note that \HashType\HashType is specified per preimage and does not specify a Poseidon instance.

Filecoin uses two hash types \MerkleTree\MerkleTree and \ConstInputLen\ConstInputLen to designate a preimage as being for a Merkle tree of arity t1t - 1 or being for no specific application, but having a length \len(\preimage)<t\len(\preimage) < t.

The \HashType\HashType determines the \DomainTag\DomainTag and \Padding\Padding used for a preimage, which give the first element of Poseidon's initial state:

\state=\DomainTag\preimage\Padding.\state = \DomainTag \parallel \preimage \parallel \Padding \quad.

\constb\HashType{\MerkleTree,\ConstInputLen}\constb \HashType \in \lbrace \MerkleTree, \ConstInputLen \rbrace The allowed hash types in which to hash a preimage for a Poseidon instance (p,M,t)(p, M, t). It is required that $1 \leq \len(\preimage) < t \hso$.

  • A \HashType\HashType of \MerkleTree\MerkleTree designates a preimage as being the preimage of a Merkle tree hash function, where the tree is (t1)\hso:\hso1(t {-} 1) \hso {:} \hso 1 (i.e. arity=\len(\preimage)\text{arity} = \len(\preimage) number of nodes are hashed into $1$ node).

  • A \HashType\HashType of \ConstInputLen\ConstInputLen designates Poseidon as being used to hash preimages of length exactly \len(\preimage)\len(\preimage) into a single output element (where $1 \leq \len(\preimage) < t$).

\constb\DomainTag\typecolon\Zp={2arity1\if\HashType=\MerkleTree264\len(\preimage)\if\HashType=\ConstInputLen\constb \DomainTag \typecolon \Zp = \begin{cases} 2^\text{arity} - 1 & \if \HashType = \MerkleTree \cr 2^{64} * \len(\preimage) & \if \HashType = \ConstInputLen \end{cases}

Encodes the Poseidon application within the first Poseidon initial state element \state[0]\state[0] for a preimage.

\constb\Padding\typecolon\Zp[]={[ ]\if\HashType=\MerkleTree0[t1\len(\preimage)]\if\HashType=\ConstInputLen\constb \Padding \typecolon \Zp^{[*]} = \begin{cases} [\ ] & \if \HashType = \MerkleTree \cr 0^{[t - 1 - \len(\preimage)]} & \if \HashType = \ConstInputLen \end{cases} The padding that is applied to Poseidon's initial state. A \HashType\HashType of \MerkleTree\MerkleTree results in no applied padding; a \HashType\HashType of \ConstInputLen\ConstInputLen pads the last t1\len(\preimage)t - 1 - \len(\preimage) elements of Poseidon's initial \state\state to zero.

Poseidon Hash Function

The Posiedon hash function takes a preimage of t1t - 1 prime field \Zp\Zp elements to a single field element. Poseidon operates on an internal state \state\state of tt field elements which, in the unoptimized algorithm, are transformed over RR number of rounds of: round constant addition, S-boxes, and MDS matrix mixing. Once all rounds have been performed, Poseidon outputs the second element of the state.

A Posiedon hash function is instantiated by a parameter triple (p,M,t)(p, M, t) which sets the prime field, the security level, and the size of Poseidon's internal state buffer \state\state. From (p,M,t)(p, M, t) the remaining Poseidon parameters are computed (α,RF,RP,\RC,\Mds)(\alpha, R_F, R_P, \RC, \Mds), i.e. the S-box exponent, the round numbers, the round constants, and the MDS matrix.

The S-box function is defined as:

S:\Zp\ZpS(x)=xαS: \Zp \rightarrow \Zp \\ S(x) = x^\alpha

The \state\state is initialized to the concatenation of the \DomainTag\DomainTag, \preimage\preimage, and \Padding\Padding:

\state=\DomainTag\preimage\Padding.\state = \DomainTag \parallel \preimage \parallel \Padding \quad.

Every round r[R]r \in [R] begins with tt field additions of the \state\state with that round's constants \RCr\hso\RC_r \hso:

\state=\state\RoundConstantsr.\state = \state \mathbin{\vec\oplus} \RoundConstants_r \quad.

If rr is a full round, i.e. r<Rfr < R_f or rRf+RPr \geq R_f + R_P, the S-box function is applied to each element of \state\state:

\state=[\state[i]α]i[t]\state = [\state[i]^\alpha]_{i \in [t]}

otherwise, if rr is a partial round r[Rf,Rf+RP)r \in [R_f, R_f + R_P), the S-box function is applied to the first \state\state element exclusively:

\state[0]=\state[0]α.\state[0] = \state[0]^\alpha \quad.

Once the S-boxes have been applied for a round, the \state\state is transformed via vector-matrix multiplication with the MDS matrix:

\state=\state×\Mds.\state = \state \times \Mds \quad.

After RR rounds of the above procedure, Poseidon outputs the digest \state[1]\state[1].

\Functionposeidon(\preimage\typecolon\Zp[t1])\Zp\line1\state\typecolon\Zp[t]=\DomainTag\preimage\Padding\line2\forr[R]\bcolon\line3\state=\state\RCr\line4\ifbr[Rf] or r[Rf+RP,R)\bcolon\line5\state=[\state[i]α]i[t]\line6\else\line7\state[0]=\state[0]α\line8\state=\state×\Mds\line9\return\state[1]\overline{\underline{\Function \textsf{poseidon}(\preimage \typecolon \Zp^{[t - 1]}) \rightarrow \Zp}} \\ \line{1} \state \typecolon \Zp^{[t]} = \DomainTag \parallel \preimage \parallel \Padding \\ \line{2} \for r \in [R] \bcolon \\ \line{3} \quad \state = \state \mathbin{\vec\oplus} \RC_r \\ \line{4} \quad \ifb r \in [R_f] \textbf{ or } r \in [R_f + R_P, R) \bcolon \\ \line{5} \quad\quad \state = [\state[i]^\alpha]_{i \in [t]} \\ \line{6} \quad \else \\ \line{7} \quad\quad \state[0] = \state[0]^\alpha \\ \line{8} \quad \state = \state \times \Mds \\ \line{9} \return \state[1]

Poseidon Algorithm

Optimizations

Filecoin's rust library neptune implements the Poseidon hash function. The library differentiates between unoptimized and optimized Poseidon using the terms correct and static respectively.

The primary differences between the two versions are:

  • the unoptimized algorithm uses the round constants \RC\RC, performs round constant addition before S-boxes, and uses the MDS matrix \Mds\Mds for mixing
  • the optimized algorithm uses the transformed rounds constants \RC\RC' (containing fewer constants than \RC\RC), performs a round constant addition before the first round's S-box, performs round constant addition after every S-box other than the last round's, and uses multiple matrices for MDS mixing \Mds\Mds, \Pre\Pre, and \Sparse\Sparse. This change in MDS mixing from a non-sparse matrix \Mds\Mds to sparse matrices \Sparse\Sparse greatly reduces the number of multiplications in each round.

For a given Poseidon instance (p,M,t)(p, M, t) the optimized and unoptimized algorithms will produce the same output when provided with the same input.

Optimized Round Constants

Given the round constants \RC\RC and MDS matrix \Mds\Mds for a Poseidon instance, we are able to derive round constants \RC\RC' for the corresponding optimized Poseidon algorithm.

Optimized Round Constants

\constb\RC\typecolon\Zp[tRF+RP]\constb \RC' \typecolon \Zp^{[tR_F + R_P]} The round constants for a Poseidon instance's (p,M,t)(p, M, t) optimized hashing algorithm. Each full round is associated with tt round constants, while each partial round is associated with one constant.

\overline{\underline{\textbf{Algorithm: } \RC'}} \\ \line{1} \RC' \typecolon \Zp^{[tR_F + R_P]} = [\ ] \\ \line{2} \RC'\extend(\RC_0) \\ \line{3} \for r \in [1, R_f) \bcolon \\ \line{4} \quad \RC'\extend(\RC_r \hso {\times} \hso \MdsInv) \\ \line{5} \textsf{partial_consts} \typecolon \Zp^{[R_P]} = [\ ] \\ \line{6} \acc \typecolon \Zp^{[t]} = \RC_{R_f + R_P} \\ \line{7} \for r \in \reverse([R_f, R_f + R_P)) \bcolon \\ \line{8} \quad \acc' = \acc \hso {\times} \hso \MdsInv \\ \line{9} \quad \textsf{partial_consts}\push(\acc'[0]) \\ \line{10} \quad \acc'[0] = 0 \\ \line{11} \quad \acc = \acc' \mathbin{\vec\oplus} \RC_r \\ \line{12} \RC'\extend(\acc \hso {\times} \hso \MdsInv) \\ \line{13} \RC'\extend(\reverse(\textsf{partial_consts})) \\ \line{14} \for r \in [R_f + R_P + 1, R) \\ \line{15} \quad \RC'\extend(\RC_r \hso {\times} \hso \MdsInv) \\ \line{16} \return \RC'

Algorithm Comments: Note: ×\times denotes a row vector-matrix multiplication which outputs a row vector. Line 2. The first tt round constants are unchanged. Note that both \RC0\RC_0' and \RC1\RC_1' are used in the first optimized round r=0r = 0. Lines 3-4. For each first-half full round, transform the round constants into \RCr\hso×\hso\MdsInv\RC_r \hso {\times} \hso \MdsInv. Line 5. Create a variable to store the round constants for the partial rounds \textsf{partial_consts} (in reverse order). Line 6. Create and initialize a variable \acc\acc that is transformed and added to \RCr\RC_r in each do\textbf{do} loop iteration. Lines 7-11. For each partial round rr (starting from the greatest partial round index Rf+RP1R_f + R_P - 1 and proceeding to the least RfR_f) transform \acc\acc into \acc\hso×\hso\MdsInv\acc \hso {\times} \hso \MdsInv, take its first element as a partial round constant, then perform element-wise addition with \RCr\RC_r. The value of \acc\acc at the end of the ithi^{th} loop iteration is:

\acci=\RCr[0]((\acci1\hso×\hso\MdsInv)[1\dotdot]\RCr[1\dotdot])\acc_i = \RC_r[0] \parallel ((\acc_{i - 1} \hso {\times} \hso \MdsInv)[1 \dotdot] \mathbin{\vec\oplus} \RC_{r}[1 \dotdot])

Line 12. Set the last first-half full round's constants using the final value of \acc\acc. Line 13. Set the partial round constants. Lines 14-15. Set the remaining full round constants.

\constb\RCpre\typecolon\Zp[t]=\RC[\dotdott]\constb \RC'_\text{pre} \typecolon \Zp^{[t]} = \RC'[\dotdot t] The first tt constants in \RC\RC' are added to \state\state prior to applying the S-box in the first round round r=0r = 0.

\constb\RCr\typecolon\Zp[]={\RC[(r+1)t\dotdot(r+2)t]\if\hsor[Rf]\RC[(Rf+1)t+rP]\ifr[Rf,Rf+RP),where rP=rRf is the partial round index rP[RP]\RC[(rF+1)t+RP\hso\dotdot\hso(rF+2)t+RP]\ifr[Rf+RP,R1),where rF=rRP is the full round index rF[RF1] (excludes the last full round)\constb \RC_r' \typecolon \Zp^{[\ast]} = \begin{cases} \RC'[(r + 1)t \dotdot (r + 2)t] & \if \hso r \in [R_f] \cr \RC'[(R_f + 1)t + r_P] & \if r \in [R_f, R_f + R_P), \text{where } r_P = r - R_f \text{ is the partial round index } r_P \in [R_P] \cr \RC'[(r_F + 1)t + R_P \hso \dotdot \hso (r_F + 2)t + R_P] & \if r \in [R_f + R_P, R - 1), \text{where } r_F = r - R_P \text{ is the full round index } r_F \in [R_F - 1] \text{ (excludes the last full round)} \end{cases}

For each round excluding the last r[R1]r \in [R - 1], \RCr\RC_r' is added to the Poseidon \state\state after that round's S-box has been applied.

Sparse MDS Matrices

A sparse matrix mm is a square n×nn {\times} n matrix whose first row and first column are utilized and where all other entries are the n1×n1n {-} 1 {\times} n {-} 1 identity matrix In1\mathcal{I}_{n - 1}:

A=[A0,0A0,1\dotdotA1\dotdot,0In1]=[A0,0A0,n110An1,001]A = \left[ \begin{array}{c|c} A_{0, 0} & A_{0, 1 \dotdot} \cr \hline A_{1 \dotdot, 0} & \mathcal{I}_{n - 1} \cr \end{array} \right] = \begin{bmatrix} A_{0, 0} & \dots & \dots & A_{0, n - 1} \cr \vdots & 1 & \dots & 0 \cr \vdots & \vdots & \ddots & \vdots \cr A_{n - 1, 0} & 0 & \dots & 1 \cr \end{bmatrix}

The MDS matrix \Mds\Mds is factored into a non-sparse matrix \Pre\Pre and an array of sparse matrices \Sparse=[\Sparse0,,\SparseRP1]\Sparse = [\Sparse_0, \dots, \Sparse_{R_P - 1}] (one matrix per partial round). \Pre\Pre is used in MDS mixing for the last first-half full round r=Rf1r = R_f - 1. Each matrix of \Sparse\Sparse is used in MDS mixing for a partial round. The first sparse matrix \Sparse0\Sparse_0 is used in the first partial round (r=Rfr = R_f) and the last sparse matrix \SparseRP1\Sparse_{R_P - 1} is used in the last partial round (r=Rf+ir = R_f + i).

\constb\Pre\typecolon\Zp[t×t]\constb \Pre \typecolon \Zp^{[t \times t]} The pre-sparse matrix (a non-sparse matrix) used in MDS mixing for the last full round of the first-half r=Rf1r = R_f - 1. Like the MDS matrix \Mds\Mds, the pre-sparse matrix \Pre\Pre is symmetric.

\constb\Sparse\typecolon\Zp[t×t][RP]\constb \Sparse \typecolon {\Zp^{[t \times t]}}^{[R_P]} The array of sparse matrices that \Mds\Mds is factored into, which are used for MDS mixing in the optimized partial rounds.

Algorithm: \Pre,\Sparse\line1sparse\typecolon\Zp[t×t][RP]=[ ]\line2m\typecolon\Zp[t×t]=\Mds\line3\doRP\timesb\bcolon\line4(m,m)\typecolon(\Zp[t×t],\Zp[t×t])=sparse_factorize(m)\line5sparse\push(m)\line6m=\Mds×m\line7\Pre=m\line8\Sparse=\reverse(sparse)\line9\return\Pre,\Sparse\overline{\underline{\textbf{Algorithm: } \Pre, \Sparse}} \\ \line{1} \textsf{sparse} \typecolon {\Zp^{[t \times t]}}^{[R_P]} = [\ ] \\ \line{2} m \typecolon \Zp^{[t \times t]} = \Mds \\ \line{3} \do R_P \timesb \bcolon \\ \line{4} \quad (m', m'') \typecolon (\Zp^{[t \times t]}, \Zp^{[t \times t]}) = \textsf{sparse\_factorize}(m) \\ \line{5} \quad \textsf{sparse}\push(m'') \\ \line{6} \quad m = \Mds \times m' \\ \line{7} \Pre = m \\ \line{8} \Sparse = \reverse(\textsf{sparse}) \\ \line{9} \return \Pre, \Sparse

Algorithm Comments: Line 1. An array containing the sparse matrices that \Mds\Mds is factored into. Line 2. An array mm that is repeatedly factored into a non-sparse matrix mm' and a sparse matrix mm'', i.e. m=m×mm = m' \times m''. Lines 3-6. In each loop iteration we factor mm into mm' and mm''. The first do\textbf{do} loop iteration calculates the sparse matrix mm'' used in MDS mixing for last partial round r=Rf+RP1r = R_f + R_P - 1. The last do\textbf{do} loop iteration calculates the sparse matrix mm'' used in MDS mixing for the first partial round r=Rfr = R_f (i.e. \Sparse0=m\Sparse_0 = m''). Line 6. \Mds×m\Mds \times m' is a matrix-matrix multiplication which produces a t×tt {\times} t matrix.

The function \textsf{sparse_factorize} factors a non-sparse matrix mm into a non-sparse matrix mm' and sparse matrix mm'' such that m=m×mm = m' \times m''.

\overline{\underline{\Function \textsf{sparse_factorize}(m \typecolon \Zp^{[t {\times} t]}) \rightarrow (m' \typecolon \Zp^{[t {\times} t]} \hso, m'' \typecolon \Zp^{[t {\times} t]})}} \\ \line{1} \hat{m} \typecolon \Zp^{[t - 1 {\times} t - 1]} = m_{1 \dotdot, 1 \dotdot} = \begin{bmatrix} m_{1, 1} & \dots & m_{1, t - 1} \\ \vdots & \ddots & \vdots \\ m_{t - 1, 1} & \dots & m_{t - 1, t - 1} \end{bmatrix} \\~ \\ \line{2} m' \typecolon \Zp^{[t {\times} t]} = \left[ \begin{array}{c|c} 1 & 0 \\ \hline 0 & \hat{m} \end{array} \right] = \begin{bmatrix} 1 & 0 & \dots & 0 \\ 0 & m_{1, 1} & \dots & m_{1, t - 1} \\ \vdots & \vdots & \ddots & \vdots \\ 0 & m_{t - 1, 1} & \dots & m_{t - 1, t - 1} \end{bmatrix} \\~ \\ \line{3} \wb \typecolon \Zp^{[t - 1 {\times} 1]} = m_{1 \dotdot, 0} = \begin{bmatrix} m_{1, 0} \\ \vdots \\ m_{t - 1, 0} \end{bmatrix} \\~ \\ \line{4} \hat\wb \typecolon \Zp^{[t - 1 {\times} 1]} = \hat{m}^{\neg 1} {\times} \hso \wb = \begin{bmatrix} {\hat{m}^{\neg 1}}_{0, \ast} \mathbin{\boldsymbol\cdot} \wb \\ \vdots \\ {\hat{m}^{\neg 1}}_{t - 2, \ast} \mathbin{\boldsymbol\cdot} \wb \end{bmatrix} \\~ \\ \line{5} m'' \typecolon \Zp^{[t {\times} t]} = \left[ \begin{array}{c|c} m_{0, 0} & m_{0, 1 \dotdot} \\ \hline \hat{\textbf{w}} & \mathcal{I}_{t - 1} \end{array} \right] = \begin{bmatrix} m_{0, 0} & \dots & \dots & m_{0, t - 1} \\ \hat{\textbf{w}}_0 & 1 & \dots & 0 \\ \vdots & \vdots & \ddots & \vdots \\ \hat{\textbf{w}}_{t - 2} & 0 & \dots & 1 \end{bmatrix} \\ \line{6} \return m', m''

Algorithm Comments: Line 1. m^\hat{m} is a submatrix of mm which excludes mm's first row and first column. Line 2. mm' is a copy of mm where mm's first row and first column have been replaced with [1,0,,0][1, 0, \dots, 0]. Line 3. \wb\wb is a column vector whose values are the first column of mm excluding mm's first row. Line 4. \wb^\hat\wb is the matrix-column vector product of m^¬1\hat{m}^{\neg 1} and \wb\wb. Line 5. mm'' is a sparse matrix whose first row is the first row of mm, remaining first column is \wb^\hat\wb, and remaining entries are the identity matrix.

Optimized Poseidon

The optimized Poseidon hash function is instantiated in the same way as the unoptimized algorithm, however the optimized Poseidon algorithm requires the additional precomputation of round constants \RC\RC', a pre-sparse matrix \Pre\Pre, and sparse matrices \Sparse\Sparse.

Prior to the first round r=0r = 0, the \state\state is initialized and added to the pre-first round constants \RCpre\RC_\text{pre}':

\state=\DomainTag\preimage\Padding\state=\state\RCpre\state = \DomainTag \parallel \preimage \parallel \Padding \\ \state = \state \mathbin{\vec\oplus} \RC_\text{pre}'

For each full round of the first-half r[Rf]r \in [R_f]: the S-box function is applied to each element of \state\state, the output of each S-box is added to the associated round constant in \RCr\RC_r', and MDS mixing occurs between \state\state and the MDS matrix \Mds\Mds (when r<Rf1r < R_f - 1) or the pre-sparse matrix \Pre\Pre (when r=Rf1r = R_f - 1).

\state={[\state[i]α\RCr[i]]i[t]×\Mdsif r[Rf1][\state[i]α\RCr[i]]i[t]×\Preif r=Rf1\state = \begin{cases} \lbrack \state[i]^\alpha \oplus \RC_r' \lbrack i \rbrack \rbrack_{i \in [t]} \times \Mds & \text{if } r \in [R_f - 1] \cr \lbrack \state[i]^\alpha \oplus \RC_r' \lbrack i \rbrack \rbrack_{i \in [t]} \times \Pre & \text{if } r = R_f - 1 \end{cases}

For each partial round r[Rf,Rf+RP)r \in [R_f, R_f + R_P) the S-box function is applied to the first \state\state element, the round constant is added to the first \state\state element, and MDS mixing occurs between the \state\state and the ithi^{th} sparse matrix \Sparsei\Sparse_i (the ithi^{th} partial round i[RP]i \in [R_P] is associated with sparse matrix \Sparsei\Sparse_i where i=rRfi = r - R_f):

\state[0]=\state[0]α\RCr\state=\state×\SparserRf\state[0] = \state[0]^\alpha \oplus \RC_r' \\ \state = \state \times \Sparse_{r - R_f}

The second half of full rounds r[Rf+RP,R)r \in [R_f + R_P, R) proceed in the same way as the first half of full rounds except that all MDS mixing uses the MDS matrix \Mds\Mds and that the last round r=R1r = R - 1 does not add round constants into \state\state.

After performing RR rounds, Poseidon outputs the digest \state[1]\state[1].

\Functionposeidon(\preimage\typecolon\Zp[t1])\Zp\line1\state\typecolon\Zp[t]=\DomainTag\preimage\Padding\line2\state=\state\RCpre\line3\forr[Rf1]\bcolon\line4\state=[\state[i]α\RCr[i]]i[t]×\Mds\line5\state=[\state[i]α\RCRf1[i]]i[t]×\Pre\line6\forr[Rf,Rf+RP)\bcolon\line7\state[0]=\state[0]α\RCr\line8\state=\state×SrRf\line9\forr[Rf+RP,R1)\bcolon\line10\state=[\state[i]α\RCr[i]]i[t]×\Mds\line11\state=[\state[i]α]i[t]×\Mds\line12\return\state[1]\overline{\underline{\Function \textsf{poseidon}(\preimage \typecolon \Zp^{[t - 1]}) \rightarrow \Zp}} \\ \line{1} \state \typecolon \Zp^{[t]} = \DomainTag \parallel \preimage \parallel \Padding \\ \line{2} \state = \state \mathbin{\vec\oplus} \RC'_\text{pre} \\ \line{3} \for r \in [R_f - 1] \bcolon \\ \line{4} \quad \state = [\state[i]^\alpha \oplus \RC'_r[i]]_{i \in [t]} \times \Mds \\ \line{5} \state = [\state[i]^\alpha \oplus \RC'_{R_f - 1}[i]]_{i \in [t]} \times \Pre \\ \line{6} \for r \in [R_f, R_f + R_P) \bcolon \\ \line{7} \quad \state[0] = \state[0]^\alpha \oplus \RC'_r \\ \line{8} \quad \state = \state \times \mathcal{S}_{r - R_f} \\ \line{9} \for r \in [R_f + R_P, R - 1) \bcolon \\ \line{10} \quad \state = [\state[i]^\alpha \oplus \RC'_r[i]]_{i \in [t]} \times \Mds \\ \line{11} \state = [\state[i]^\alpha]_{i \in [t]} \times \Mds \\ \line{12} \return \state[1]

Algorithm Comments: Line 1. Initialize the \state\state. Line 2. Adds the pre- r=0r = 0 round constants. Lines 3-4. Performs all but the last first-half of full rounds r[Rf1]r \in [R_f - 1]. Line 5. Performs the last first-half full round r=Rf1r = R_f - 1. Lines 6-8. Performs the partial rounds r[Rf,Rf+RP)r \in [R_f, R_f + R_P). Mixing in the ithi^{th} partial round, where i=rRfi = r - R_f, is done using the ithi^{th} sparse matrix \SparserRf\Sparse_{r - R_f}. Lines 9-10. Performs all but the last second-half full rounds r[Rf+RP,R1)r \in [R_f + R_P, R - 1). Line 11. Performs the last second-half rull round r=R1r = R - 1.

Optimized Poseidon Algorithm