Skip to content

Commit 0390761

Browse files
authored
Merge pull request #9 from BlockstreamResearch/spec
Modified functions and address chapter, adding paragraph about precom…
2 parents a5bea50 + 604364b commit 0390761

2 files changed

Lines changed: 15 additions & 11 deletions

File tree

docs/shrincs_spec/content/5-functions-and-addresses.tex

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,30 +7,34 @@ \subsection{Hash Function}
77
\texttt{H(m) = SHA-256(m)[0:n]}
88
\end{center}
99

10-
\subsection{Tweakable Hash Function}
11-
To prevent multi-target attacks and ensure domain separation between the various components, we employ the tweakable hash function construction defined in SPHINCS+ \cite{sphincsplus_paper2019}:
10+
\paragraph{SHA-256 Block Precomputation}
11+
For SHA-256, the compression function \texttt{C(H,M)} operates on 64-byte blocks. Since \texttt{PK.seed} is 16 bytes, we pad it to 64 bytes to enable precomputation of the first block:
1212

1313
\begin{center}
14-
\texttt{Tw(PK.seed, ADRS, m) = H(PK.seed || ADRS || m)}
14+
\texttt{P = C(IV, PK.seed || 0\string^48)}
1515
\end{center}
1616

17-
This usage of tweaked hashing is critical. It ensures that a hash computed at one position in the Merkle tree cannot be substituted for a hash at another position, effectively mitigating generic tree-grafting attacks.
17+
Then we use the following precomputation \texttt{P} in the next compression function iteration.
18+
Every function that uses SHA-256 with it's argument involving PK.seed will use this precomputation value. This optimization allows to reduce the number of hashing operation, which leads to faster computation, especially in the grinding parts of the SHRINCS.
1819

19-
\paragraph{Note:} For SHA-256, the compression function operates on 64-byte blocks. Since \texttt{PK.seed} is 16 bytes, we pad it to 64 bytes to enable precomputation:
20+
Additionally, we will denote the concatenation \texttt{PK.seed || 0\string^48} as \texttt{PK.seed*}.
21+
22+
\subsection{Tweakable Hash Function}
23+
To prevent multi-target attacks and ensure domain separation between the various components, we employ the tweakable hash function construction defined in SPHINCS+ \cite{sphincsplus_paper2019}:
2024

2125
\begin{center}
22-
\texttt{Tw(PK.seed, ADRS, m) = H(PK.seed || 0\string^48|| ADRS || m)}
26+
\texttt{Tw(PK.seed, ADRS, m) = H(PK.seed* || ADRS || m)}
2327
\end{center}
2428

25-
Having the first 64 bytes constant (\texttt{PK.seed || 0\string^48}), we can precompute the compression function state once per key pair. Each tweak call then only needs to process the variable part (\texttt{ADRS || m}), reducing per-call overhead.
29+
This usage of tweaked hashing is critical. It ensures that a hash computed at one position in the Merkle tree cannot be substituted for a hash at another position, effectively mitigating generic tree-grafting attacks.
2630

2731
\subsection{Pseudorandom Functions}
2832
SHRINCS uses two pseudorandom functions for different purposes: key derivation and getting the randomizer for the signature.
2933

3034
For deterministic derivation of secret key material (WOTS+C chain starting values, FORS secret keys):
3135

3236
\begin{center}
33-
\texttt{PRF(PK.seed, ADRS, SK.seed) = Tw(PK.seed, ADRS, SK.seed)}
37+
\texttt{PRF(PK.seed, ADRS, SK.seed) = Tw(PK.seed*, ADRS, SK.seed)}
3438
\end{center}
3539

3640
The address structure \texttt{ADRS} ensures that each derived value is unique and bound to its position in the scheme.
@@ -61,19 +65,19 @@ \subsubsection{WOTS+C: Two-Phase Hashing}
6165

6266
\paragraph{Phase 1. Message Digest.} Computes a randomized hash of the user message:
6367
\begin{center}
64-
\texttt{H\_msg(ADRS, R, PK.seed, PK.root, m) = SHA-256(ADRS || R || PK.seed || PK.root || m)[0:n]}
68+
\texttt{H\_msg(ADRS, R, PK.seed, PK.root, m) = SHA-256(PK.seed* || ADRS || R || PK.root || m)[0:n]}
6569
\end{center}
6670
This is computed once per signature, producing an n-byte digest.
6771

6872
\paragraph{Phase 2. Grinding Hash.} Takes the digest and a counter:
6973
\begin{center}
70-
\texttt{H\_grind(ADRS, PK.seed, digest, ctr) = SHA-256(ADRS || PK.seed || digest || ctr)[0:n]}
74+
\texttt{H\_grind(ADRS, PK.seed, digest, ctr) = SHA-256(PK.seed* || ADRS || digest || ctr)[0:n]}
7175
\end{center}
7276

7377
\subsubsection{FORS+C: Single-Phase Hashing}
7478
FORS+C always signs user messages (never tree roots), so it uses a simpler single-phase approach. The grinding counter is not included in the message hash. Instead, it is iterated inside \texttt{PRF\_msg\_ctr} (Section~5.3) to produce a fresh \texttt{R} on each grinding attempt. The verifier only sees the final \texttt{R}:
7579
\begin{center}
76-
\texttt{H\_msg\_fors(ADRS, R, PK.seed, PK.root, m) = SHA-256(ADRS || R || PK.seed || PK.root || m)[0:roundup((k*a + hsl) / 8)]}
80+
\texttt{H\_msg\_fors(ADRS, R, PK.seed, PK.root, m) = SHA-256(PK.seed* || ADRS || R || PK.root || m)[0:roundup((k*a + hsl) / 8)]}
7781
\end{center}
7882

7983
The output length is roundup((k*a + hsl) / 8) = 20 bytes for both parameter sets, encoding both the \texttt{k} FORS leaf indices and the \texttt{hsl} hypertree path indices in a unified digest (see Section~10.5).

docs/shrincs_spec/main.pdf

144 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)