mirror of
https://github.com/luxfi/corona.git
synced 2026-09-24 00:25:54 +00:00
Forked from github.com/luxfi/corona @ d09b2c2. Pulsar inherits the
2-round signing math byte-equal but adds blockchain-grade key
lifecycle and a SHA3-based domain-separated hash profile for
permissionless validator-set rotation.
Inherits from upstream Corona (byte-equal):
- Sign1/Sign2/Combine 2-round signing math
- Lattice ring R_q = Z_q[X]/(X^256+1), q = 0x1000000004A01
- Module-LWE / Module-SIS hardness
- NTT, Discrete-Gaussian Ziggurat, MAC layer
Replaces from upstream:
- Broken Feldman-style DKG (pseudoinverse-recoverable). Replaced
with: (a) one-time foundation MPC ceremony / trusted-dealer
Bootstrap; (b) Pedersen DKG over R_q (research, dkg2/) hiding
under MLWE on B / binding under MSIS on [A | B].
- Trusted-dealer-per-epoch lifecycle (wrong shape for permissionless
rotation). Replaced with Verifiable Secret Resharing (VSR) every
epoch under the persistent group public key.
Key-era lifecycle (KeyEraID / Generation / RollbackFrom):
- KeyEraID bumps only at Reanchor (rare governance event)
- Generation bumps every Refresh / Reshare under the same GroupKey
- RollbackFrom records prior generation reverted from (0 = forward)
- Activation cert (QUASAR-PULSAR-ACTIVATE-v1) gates new generations
under the unchanged GroupKey
Pulsar-SHA3 hash profile (NIST FIPS 202 / SP 800-185):
- Hc: cSHAKE256("PULSAR-HC-v1", transcript) → challenge
- Hu: cSHAKE256("PULSAR-HU-v1", transcript) → XOF stream
- TranscriptHash: TupleHash256("PULSAR-TRANSCRIPT-v1", parts...)
- PRF: KMAC256(key, msg, "PULSAR-PRF-v1")
- MAC: KMAC256(key, msg, "PULSAR-MAC-v1")
- Pairwise: KMAC256(kex, encode(...), "PULSAR-PAIRWISE-v1")
- Default suite is Pulsar-SHA3; Pulsar-BLAKE3 retained as optional
non-normative fast profile. HashSuiteID is bound into transcripts.
- Implementation at pulsar/hash/ with HashSuite interface.
- All KATs regenerated under SHA3 profile; vectors in
luxcpp/crypto/pulsar/test/kat/.
Nebula root binding (TranscriptInputs + ActivationMessage canonical
bytes, all bound under TupleHash256):
chain_id, network_id, group_id,
key_era_id, old_generation, new_generation,
old_epoch_id, new_epoch_id,
old_set_hash, new_set_hash,
threshold_old, threshold_new,
group_public_key_hash,
nebula_root,
hash_suite_id,
implementation_version,
variant.
Packages:
primitives/ Shamir over R_q, Lagrange, hash helpers
sign/ 2-round sign math (byte-equal upstream)
threshold/ GroupKey, KeyShare, Signer types
reshare/ VSR kernel — Refresh (HJKY97 zero-poly), Reshare
(Desmedt-Jajodia), commit (Pedersen R_q), complaint,
transcript, pairwise KEX, activation cert
hash/ HashSuite interface; PulsarSHA3 (default), PulsarBLAKE3
dkg2/ Pedersen DKG over R_q (research, reference only)
keyera/ Lifecycle wrapper: Bootstrap → Reshare → Reanchor
papers/ LP-073-pulsar paper (sections + bibliography)
cmd/*_oracle/ KAT generators (Go ↔ C++ byte-equal validation)
DESIGN.md is the single source of truth:
- The Pulsar metaphor (rotating beam over persistent body)
- Vocabulary stack per LP-105: Nebula / Photon / Lumen / Beam /
Pulsar / Pulse / Prism / Horizon / Quasar
- Pulsar ≠ Lumen (PQ stream is separate, planned)
- Pulsar / Lens / LSS three-layer architecture
- Bootstrap Dealer vs Signature Coordinator
- No-slashing failure ladder (timeout → retry → rollback → reanchor)
- MVP VSR vs Robust VSR phasing
- Borrowed-brand terms ("X-Wing" used casually) replaced with
"hybrid KEM" / "Hybrid Lumen Handshake"; X-Wing cited only as
the IETF combiner primitive name where exact reference matters.
Tests: full suite green
hash 5+/5+, keyera 5/5, reshare 45+/45+, threshold, sign, dkg,
dkg2, primitives, networking, utils, corona_oracle_v2 KAT —
all pass.
Honest claim: the crypto primitives are not new (HJKY97,
Desmedt-Jajodia97, Wong-Wang-Wing02, Corona, Hermine, Threshold
Raccoon all exist). The novel contribution is the systems
composition: a permissionless-consensus deployment architecture that
turns static two-round PQ threshold signatures into a dynamic,
leaderless, validator-rotation-tolerant finality layer.
Companion docs: papers/lp-073-pulsar/ (academic paper); LP-073 +
LP-103 + LP-105 in github.com/luxfi/lps; threshold/protocols/lss/
lss_pulsar.go (LSS adapter wrapping this kernel).
243 lines
26 KiB
TeX
243 lines
26 KiB
TeX
\section{Proactive Secret Resharing}
|
|
\label{sec:resharing}
|
|
|
|
\subsection{Problem statement}
|
|
|
|
The two-round signing protocol of \S\ref{sec:protocol} is parameterised by a master secret $s \in R_q^{n_{\textrm{vec}}}$, a public matrix $A \in R_q^{m_{\textrm{pk}} \times n_{\textrm{vec}}}$, an error term $e \in R_q^{m_{\textrm{pk}}}$, and the public group key $b = A \cdot s + e$ (rounded into $\tilde b$). At genesis a trusted-dealer Gen distributes Shamir shares of $s$ to the initial validator committee. The secret $s$ never appears explicitly thereafter --- only its shares.
|
|
|
|
When the validator set rotates at the end of an epoch, the natural na\"{i}ve approach is to run trusted-dealer Gen again with the new committee. This re-derives $s$ inside a single party's memory, which is unacceptable for a leaderless public chain: the new dealer learns the master secret, and the public key $b$ changes (so all signatures from prior epochs become unverifiable without auxiliary indexing).
|
|
|
|
\textbf{Goal.} Rotate the share distribution from an old committee $\mathcal{O} = \{1, \dots, n_{\mathrm{old}}\}$ with threshold $t_{\mathrm{old}}$ onto a new committee $\mathcal{N}$ (potentially disjoint, with possibly different $|\mathcal{N}|, t_{\mathrm{new}}$), without (i) reconstructing $s$ in any party's memory, and (ii) changing the public key $\tilde b$. Equivalently: any threshold subset of $\mathcal{N}$ jointly produces signatures that verify under the same $\tilde b$ as before.
|
|
|
|
We adapt the proactive secret-sharing protocol of Herzberg, Jakobsson, Jarecki, Krawczyk, and Yung \cite{hjky97} to the lattice ring $R_q = \mathbb{Z}_q[X]/(X^{256} + 1)$ used by Pulsar.
|
|
|
|
\subsection{Notation}
|
|
|
|
Throughout this section, party identifiers are 1-indexed. Each old party $i \in \mathcal{O}$ holds a Shamir share $s_i \in R_q^{n_{\textrm{vec}}}$ of $s$ in standard (non-NTT, non-Montgomery) form, where $s_i$ is the value of an underlying secret polynomial $P : R_q \to R_q^{n_{\textrm{vec}}}$ at $X = i$:
|
|
\[
|
|
P(X) = s + a_1 X + a_2 X^2 + \cdots + a_{t_{\mathrm{old}} - 1} X^{t_{\mathrm{old}} - 1}, \qquad s_i = P(i),
|
|
\]
|
|
with $a_1, \dots, a_{t_{\mathrm{old}}-1}$ uniform in $R_q$. Here $P$, $a_d$, and $s_i$ are vectors of polynomials in $R_q^{n_{\textrm{vec}}}$; Shamir is applied independently per coordinate of each component poly. The threshold relation is $\sum_{i \in T} \lambda_i^T \cdot s_i = s$ for any $T \subseteq \mathcal{O}$ with $|T| = t_{\mathrm{old}}$, where the Lagrange basis at $X = 0$ is
|
|
\[
|
|
\lambda_i^T = \prod_{j \in T,\, j \neq i} \frac{-x_j}{x_i - x_j} \pmod{q}, \qquad x_i = i.
|
|
\]
|
|
|
|
\subsection{Protocol \textsc{Reshare}}
|
|
|
|
\begin{enumerate}
|
|
\item \textbf{Quorum selection.} The protocol picks a deterministic quorum $\mathcal{Q} \subseteq \mathcal{O}$ with $|\mathcal{Q}| = t_{\mathrm{old}}$. The Pulsar canonical takes the smallest-ID quorum so the protocol is reproducible across implementations (load-bearing for the cross-language KAT in \S\ref{sec:resharing-kat}).
|
|
|
|
\item \textbf{Per-party random polynomial.} Each $i \in \mathcal{Q}$ samples a random degree-$(t_{\mathrm{new}} - 1)$ polynomial $g_i$ in $X$ over $R_q^{n_{\textrm{vec}}}$ with constant term equal to $\lambda_i^{\mathcal{Q}} \cdot s_i$ (the party's own share scaled by its Lagrange coefficient for $\mathcal{Q}$):
|
|
\[
|
|
g_i(X) = \bigl(\lambda_i^{\mathcal{Q}} \cdot s_i\bigr) + r_{i,1} X + r_{i,2} X^2 + \cdots + r_{i, t_{\mathrm{new}} - 1} X^{t_{\mathrm{new}} - 1},
|
|
\]
|
|
where $r_{i,d} \in R_q^{n_{\textrm{vec}}}$ is uniform for $1 \le d < t_{\mathrm{new}}$. Each coefficient is sampled independently per Shamir-coordinate slot.
|
|
|
|
\item \textbf{Share dealing.} For each $j \in \mathcal{N}$, party $i$ computes $g_i(j) \in R_q^{n_{\textrm{vec}}}$ and sends it to party $j$ over an authenticated, encrypted pairwise channel. The transport layer is out of scope for this section --- standard X25519 + ML-KEM hybrid \cite{lp070} suffices.
|
|
|
|
\item \textbf{New share assembly.} Each new party $j \in \mathcal{N}$ collects the $t_{\mathrm{old}}$ values $\{g_i(j) : i \in \mathcal{Q}\}$ and computes its new share:
|
|
\[
|
|
s'_j \;=\; \sum_{i \in \mathcal{Q}} g_i(j) \pmod{q}.
|
|
\]
|
|
\end{enumerate}
|
|
|
|
\begin{remark}[Comparison with the literature.]
|
|
The HJKY97 paper presents two distinct primitives: (a) a same-committee proactive update using a zero-polynomial $z_i(0) = 0$, and (b) a redistribution protocol for changing access structures. The protocol above is a specialization of (b) due to Desmedt and Jajodia \cite{desmedt-jajodia97}; the same-committee version is implemented as a \emph{distinct} routine \textsc{Refresh} (described in \S\ref{sec:resharing-refresh} below) for periodic mobile-adversary defense within a stable validator set.
|
|
\end{remark}
|
|
|
|
\subsection{Protocol \textsc{Refresh} (same-committee)}
|
|
\label{sec:resharing-refresh}
|
|
|
|
\textsc{Refresh} is the kernel for HJKY97's primitive (a). The committee $T = \{\alpha_1, \dots, \alpha_n\}$ and threshold $t$ are held \emph{fixed}. Every party rotates its share to fresh independent randomness while preserving the master secret. This is a proactive-security \emph{hygiene} step within an unchanging validator set --- distinct from \textsc{Reshare} above, which rotates the validator set itself.
|
|
|
|
\begin{enumerate}
|
|
\item Each $i \in T$ samples a random degree-$(t-1)$ polynomial $z_i(X)$ over $R_q^{n_{\textrm{vec}}}$ with $z_i(0) = 0$. Concretely the constant term is forced to zero; the higher-degree coefficients are uniformly random.
|
|
\item Each $i$ privately delivers $z_i(\alpha_j)$ to every $j \in T$ over an authenticated channel.
|
|
\item Each $j$ updates: $s'_j = s_j + \sum_i z_i(\alpha_j) \pmod q$.
|
|
\end{enumerate}
|
|
|
|
\begin{theorem}[\textsc{Refresh} preserves the master secret]
|
|
Define $Z(X) = \sum_i z_i(X)$. $Z$ has degree at most $t-1$ and $Z(0) = 0$. Let $S(X)$ be the original Shamir polynomial of degree $t-1$ with $S(0) = s$ and $S(\alpha_j) = s_j$ for all $j \in T$. The new shares are evaluations of $S'(X) = S(X) + Z(X)$ at the original points; $S'(0) = s$, so the new shares are valid $(t, n)$-Shamir shares of the same master secret $s$. Furthermore the joint distribution of $\{s'_j\}_{j \in T}$ is independent of $\{s_j\}_{j \in T}$ subject to $S'(0) = s$, because $Z$'s degree-$1, \dots, t-1$ coefficients are uniform and independent of $S$.
|
|
\end{theorem}
|
|
|
|
\textsc{Refresh} and \textsc{Reshare} compose: a chain may schedule \textsc{Refresh} on a periodic background timer (e.g.\ every block within a stable epoch) and \textsc{Reshare} only at validator-set rotation boundaries. Both produce activation certificates (\S\ref{sec:resharing-activation}) under the same unchanged group public key.
|
|
|
|
\subsection{Correctness}
|
|
|
|
\begin{theorem}[Public-key invariance]
|
|
\label{thm:resharing-pk-invariant}
|
|
Let $s'_j$ be the share computed by new party $j \in \mathcal{N}$ after \textsc{Reshare}. Then for every threshold subset $T' \subseteq \mathcal{N}$ with $|T'| = t_{\mathrm{new}}$,
|
|
\[
|
|
\sum_{j \in T'} \lambda_j^{T'} \cdot s'_j \;=\; s \pmod{q},
|
|
\]
|
|
where $s$ is the master secret of the input shares. Equivalently, the public key $\tilde b$ derived from $s$ at genesis verifies signatures produced by any $t_{\mathrm{new}}$-subset of $\mathcal{N}$.
|
|
\end{theorem}
|
|
|
|
\begin{proof}
|
|
Define $G(X) = \sum_{i \in \mathcal{Q}} g_i(X)$. By construction $G$ has degree at most $t_{\mathrm{new}} - 1$ and constant term
|
|
\[
|
|
G(0) = \sum_{i \in \mathcal{Q}} g_i(0) = \sum_{i \in \mathcal{Q}} \lambda_i^{\mathcal{Q}} \cdot s_i = s,
|
|
\]
|
|
where the last equality is the Lagrange-interpolation identity for the old quorum. The new shares are $s'_j = G(j)$ for $j \in \mathcal{N}$, so $\{s'_j\}_{j \in \mathcal{N}}$ are valid Shamir shares of $G(0) = s$ with threshold $t_{\mathrm{new}}$. Lagrange interpolation of any $t_{\mathrm{new}}$-subset $T' \subseteq \mathcal{N}$ at $X = 0$ recovers $G(0) = s$. Therefore the underlying master secret is preserved exactly. The genesis-time public key $b = A \cdot s + e$ depends only on $s$ (and the unchanged $A, e$), so $\tilde b = \mathrm{Round}(A \cdot s + e, \xi)$ is identical before and after \textsc{Reshare}.\qed
|
|
\end{proof}
|
|
|
|
\begin{remark}[Degree of $G$]
|
|
$G$ has degree exactly $t_{\mathrm{new}} - 1$ except with negligible probability: the leading-coefficient slot of $G$ is $\sum_{i \in \mathcal{Q}} r_{i, t_{\mathrm{new}}-1}$, a sum of $t_{\mathrm{old}}$ independent uniform $R_q^{n_{\textrm{vec}}}$ samples. The probability that this sum equals zero on every coordinate is at most $q^{-n_{\textrm{vec}} \cdot \varphi} = 2^{-48 \cdot 7 \cdot 256} \approx 2^{-86\,016}$, vanishing.
|
|
\end{remark}
|
|
|
|
\subsection{Security}
|
|
|
|
\subsubsection{Threat model}
|
|
|
|
We adopt the static-corruption-per-epoch adversary of \cite{hjky97}: in each epoch, a probabilistic polynomial-time adversary $\mathcal{A}$ may corrupt up to $t_{\mathrm{old}} - 1$ parties of $\mathcal{O}$ (resp.\ $t_{\mathrm{new}} - 1$ of $\mathcal{N}$) and learn their shares and per-session randomness. Crucially, corruptions in epoch $e$ and epoch $e+1$ are \emph{independent}: a party corrupted across both epochs is counted against the threshold of each, but the adversary obtains no extra advantage from the temporal aggregation.
|
|
|
|
We further assume each honest outgoing party's RNG is unbiased and unobservable. A corrupted party's randomness is under adversarial control; a corrupted party may also drop messages, send malformed messages, or otherwise deviate from the protocol --- but the kernel \textsc{Reshare} as specified above does not detect such deviations. Verifiable Secret Resharing (VSR) is layered on top in production; see \S\ref{sec:resharing-vsr}.
|
|
|
|
\subsubsection{Confidentiality}
|
|
|
|
\begin{theorem}[Static confidentiality]
|
|
\label{thm:resharing-confidentiality}
|
|
Let $\mathcal{B}_{\mathrm{old}} \subseteq \mathcal{O}$ with $|\mathcal{B}_{\mathrm{old}}| < t_{\mathrm{old}}$ and $\mathcal{B}_{\mathrm{new}} \subseteq \mathcal{N}$ with $|\mathcal{B}_{\mathrm{new}}| < t_{\mathrm{new}}$ denote the corruption sets in the old and new epochs. Then the joint view of $\mathcal{A}$ across the resharing,
|
|
\[
|
|
\mathrm{View}_\mathcal{A} = \bigl(\{s_i\}_{i \in \mathcal{B}_{\mathrm{old}}},\, \{s'_j\}_{j \in \mathcal{B}_{\mathrm{new}}},\, \{g_i(j)\}_{i \in \mathcal{Q},\, j \in \mathcal{B}_{\mathrm{new}}}\bigr),
|
|
\]
|
|
is statistically independent of the master secret $s$.
|
|
\end{theorem}
|
|
|
|
\begin{proof}[Sketch]
|
|
The old shares $\{s_i\}_{i \in \mathcal{B}_{\mathrm{old}}}$ alone leak no information about $s$ by the standard $(t_{\mathrm{old}}-1)$-Shamir security, applied per Shamir-coordinate slot \cite{shamir79}. The new shares $\{s'_j\}_{j \in \mathcal{B}_{\mathrm{new}}}$ similarly leak nothing on their own: $|\mathcal{B}_{\mathrm{new}}| < t_{\mathrm{new}}$ so they correspond to $< t_{\mathrm{new}}$ evaluations of $G$. The cross-leakage from the dealing step contributes only $\{g_i(j) : i \in \mathcal{Q},\, j \in \mathcal{B}_{\mathrm{new}}\}$. Each $g_i$ is constructed from $i$'s own share $s_i$ (already known to $\mathcal{A}$ if $i \in \mathcal{B}_{\mathrm{old}}$, otherwise an honest random) plus $t_{\mathrm{new}} - 1$ uniform high-degree coefficients. Conditioned on $s_i$, the distribution of $g_i(j)$ for $|\{j\}| < t_{\mathrm{new}}$ is uniformly random in $R_q^{n_{\textrm{vec}}}$, independent of $s$. Therefore the entire $\mathrm{View}_\mathcal{A}$ admits a simulator that draws the leaked $g_i(j)$ values uniformly without knowing $s$ --- so $\mathcal{A}$'s advantage in distinguishing $s$ from random is information-theoretically zero.\qed
|
|
\end{proof}
|
|
|
|
\begin{remark}[Why we do \emph{not} require $|\mathcal{B}_{\mathrm{old}}| + |\mathcal{B}_{\mathrm{new}}| < t_{\mathrm{old}}$]
|
|
A weaker version of \Cref{thm:resharing-confidentiality} would require $|\mathcal{B}_{\mathrm{old}}| + |\mathcal{B}_{\mathrm{new}}| < t_{\mathrm{old}}$ (corruptions must not aggregate across epochs). The stronger statement above holds because every honest old party's contribution to a leaked $g_i(j)$ is randomized by independent $r_{i,d}$ values, and an honest party's $s_i$ is itself uniformly random (per Shamir security). The randomization in degrees $1, \dots, t_{\mathrm{new}} - 1$ is the crucial ingredient: it means the new-epoch corruption set $\mathcal{B}_{\mathrm{new}}$ is "fresh" with respect to $s$, even if the same physical machines are corrupted across both epochs.
|
|
\end{remark}
|
|
|
|
\subsubsection{Public-key indistinguishability}
|
|
|
|
A separate, weaker security property: an external observer who sees only the public output $\tilde b$ before and after \textsc{Reshare} learns nothing about the rotation. This is trivial: $\tilde b$ is unchanged by \Cref{thm:resharing-pk-invariant}, so there is no observable.
|
|
|
|
\subsection{Verifiable Secret Resharing (VSR)}
|
|
\label{sec:resharing-vsr}
|
|
|
|
The protocols of \S\ref{sec:resharing}--\S\ref{sec:resharing-refresh} are the arithmetic \emph{kernel}: adequate for honest-but-curious parties and sufficient for cryptographic-correctness proofs. Production deployment in a permissionless setting layers the following components on top, all collectively known as Verifiable Secret Resharing (VSR) \cite{wong02}. Each is implemented in a sibling file in the \texttt{pulsar/reshare/} package.
|
|
|
|
\paragraph{Authenticated point-to-point channels} (\texttt{pairwise.go}). Each pair $(i, j)$ runs an authenticated key exchange: X25519 in the classical baseline, X25519 $\|$ ML-KEM-768 hybrid in the post-quantum mode. The wire-identity Ed25519 key signs the X25519 ephemeral, defeating active man-in-the-middle. The transcript hash (below) is bound into the signed ephemeral so a stale exchange cannot be replayed in a different invocation.
|
|
|
|
\paragraph{Domain-separated transcript binding} (\texttt{transcript.go}). Every resharing message and complaint includes the 32-byte BLAKE3 hash of
|
|
\[
|
|
(\mathit{chain\_id},\, \mathit{group\_id},\, \mathit{old\_epoch\_id},\, \mathit{new\_epoch\_id},\, H(\mathcal{O}),\, H(\mathcal{N}),\, t_{\mathrm{old}},\, t_{\mathrm{new}},\, H(\tilde b),\, \mathit{variant})
|
|
\]
|
|
under personalization \texttt{"pulsar.reshare.transcript.v1"}. The \emph{variant} field $\in \{\textsc{refresh}, \textsc{reshare}\}$ separates the two primitives' transcripts so an activation cert produced for one cannot be replayed for the other. The validator-set hashes $H(\mathcal{O}), H(\mathcal{N})$ are computed over sorted public-key byte strings, independent of map iteration order.
|
|
|
|
\paragraph{Polynomial commitments} (\texttt{commit.go}). Each old party $i$ broadcasts the Pedersen-style commitment
|
|
\[
|
|
C_{i,k} \;=\; A_R \cdot \mathrm{NTT}(c_{i,k}) + B_R \cdot \mathrm{NTT}(r_{i,k})
|
|
\]
|
|
for $k = 0, \dots, t-1$, where $A_R, B_R$ are public matrices derived from \texttt{"pulsar.reshare.A.v1"} and \texttt{"pulsar.reshare.B.v1"} via BLAKE3-XOF $\to$ KeyedPRNG $\to$ UniformSampler. (These tags are distinct from the \texttt{dkg2} commit tags, so a DKG commit cannot be replayed as a reshare commit.) Each recipient $j$ verifies
|
|
\[
|
|
A_R \cdot \mathrm{NTT}(\mathit{share}_{i \to j}) + B_R \cdot \mathrm{NTT}(\mathit{blind}_{i \to j}) \;=\; \sum_{k=0}^{t-1} \beta_j^k \cdot C_{i,k}
|
|
\]
|
|
in $R_q^M$ as exact equality. Hiding follows from MLWE on $B_R$; binding (over the small-norm cone) from MSIS on $[A_R \mid B_R]$. The argument is structurally identical to the \texttt{dkg2} commit scheme analyzed in \S\ref{sec:pedersen-dkg}.
|
|
|
|
\paragraph{Complaints and disqualification} (\texttt{complaint.go}). On a failed verification --- \textsc{BadDelivery}, \textsc{Equivocation}, \textsc{Missing}, or \textsc{MalformedCommit} --- the recipient broadcasts an Ed25519-signed \texttt{Complaint} naming the misbehaving sender and the failing slot. Sender $i$ is disqualified iff $\geq t_{\mathrm{old}} - 1$ distinct complainers signed valid complaints against $i$. The threshold $t_{\mathrm{old}} - 1$ exceeds the static-corruption bound $t_{\mathrm{old}} - 1$ by exactly one in the worst-case dishonest coalition, so no honest sender can be disqualified by a maximally adversarial set. After disqualification settles, every honest party computes the same surviving quorum $\mathcal{Q}' = \mathcal{Q} \setminus \mathit{disqualified}$ and the same $\lambda^{\mathcal{Q}'}$ coefficients. If $|\mathcal{Q}'| < t_{\mathrm{old}}$, the round fails and the chain stays at the old epoch.
|
|
|
|
\paragraph{Slashing evidence.} Each disqualifying \texttt{Complaint}, plus the equivocation pair $(C_a, C_b)$ where applicable, is admissible as slashing evidence at the consensus layer. The slasher re-runs \texttt{VerifyShareAgainstCommits} or \texttt{CommitDigest} equality under the recorded transcript hash; successful re-verification triggers the slashing transaction. The on-chain wire format is documented in \texttt{quasar\_integration.go}.
|
|
|
|
\subsection{Activation circuit-breaker}
|
|
\label{sec:resharing-activation}
|
|
|
|
Resharing is meaningless without a hard go/no-go switch at the chain level. If the new committee cannot collectively sign under the \emph{unchanged} group public key, the shares it holds do not actually reconstruct the master secret, and rotating to those shares would brick the chain. The \emph{activation certificate} is the proof that the new committee \emph{can} sign --- and therefore the chain accepts the new epoch.
|
|
|
|
The activation message (canonical bytes signed by the new committee under the unchanged group public key):
|
|
\begin{verbatim}
|
|
"QUASAR-PULSAR-ACTIVATE-v1"
|
|
|| transcript_hash (32 bytes, TranscriptInputs.Hash())
|
|
|| reshare_transcript_hash (32 bytes, exchange digest:
|
|
commits, complaints, disqualifications)
|
|
\end{verbatim}
|
|
The \texttt{transcript\_hash} field is a structural binding to the resharing parameters. The \texttt{reshare\_transcript\_hash} field is the BLAKE3 digest of the actual exchange under personalization \texttt{"pulsar.reshare.exchange-transcript.v1"}: every committed value, every complaint, every disqualification, in canonical order. Two honest validators with the same view of the exchange compute the same 32-byte hash.
|
|
|
|
\paragraph{Acceptance rule (consensus layer).}
|
|
\begin{enumerate}
|
|
\item The chain extracts the new $\textsc{EpochShareState}$ from the resharing batch.
|
|
\item The new committee runs the Sign protocol on the activation message.
|
|
\item The threshold-aggregated signature MUST verify under the unchanged group public key.
|
|
\item If verification succeeds, the chain commits the new $\textsc{EpochShareState}$, forwards activation evidence to the slashing module, and erases the old shares (\S\ref{sec:resharing-erasure}).
|
|
\item If verification fails, the chain stays at the old epoch. The old committee continues signing. A new resharing attempt is scheduled at the next epoch boundary.
|
|
\end{enumerate}
|
|
|
|
\subsection{KeyShare regeneration}
|
|
\label{sec:resharing-keyshare}
|
|
|
|
The Pulsar and Corona signing protocols consume a per-party \texttt{KeyShare} struct that carries more than just the bare Shamir share:
|
|
\[
|
|
\texttt{KeyShare} = (\mathit{Index},\, \mathit{SkShare},\, \mathit{Seeds},\, \mathit{MACKeys},\, \Lambda,\, \mathit{GroupKey}).
|
|
\]
|
|
\textsc{Reshare} and \textsc{Refresh} only update $\mathit{SkShare}$. The remaining four fields must be regenerated for a complete \texttt{KeyShare} that can drive the signing protocol:
|
|
|
|
\begin{itemize}
|
|
\item $\Lambda$ (Lagrange coefficient): deterministic from the new committee's evaluation point set, recomputed via \texttt{primitives.ComputeLagrangeCoefficients} and brought into NTT-Montgomery form.
|
|
|
|
\item $\mathit{Seeds}_{ij}$ (per-pair PRF seed): derived from $\mathit{auth\_kex}_{ij}$ under the BLAKE3 personalization \texttt{"pulsar.reshare.prf-seed.v1"} with explicit binding to $(\mathit{chain\_id}, \mathit{group\_id}, \mathit{new\_epoch\_id}, i, j)$. The pair ordering $(i, j)$ is canonicalized (smaller-id first) so both endpoints derive the same value.
|
|
|
|
\item $\mathit{MACKeys}_{ij}$ (per-pair MAC key): same construction as $\mathit{Seeds}$, but under personalization \texttt{"pulsar.reshare.mac-key.v1"}. The distinct tag enforces $\mathit{Seeds}_{ij} \neq \mathit{MACKeys}_{ij}$ even though both are KDF outputs of the same $\mathit{auth\_kex}_{ij}$.
|
|
|
|
\item $\mathit{GroupKey}$: pointer copy of the persistent $(A, \tilde b)$ --- \emph{unchanged} across resharing.
|
|
\end{itemize}
|
|
|
|
The regeneration is implemented in \texttt{keyshare.go} as \texttt{PartyKey\-Share\-From\-Share}. The integration test \texttt{TestFullReshareThenKeyShareThenSign} drives the full chain Reshare $\to$ \texttt{PartyKey\-Share\-From\-Share} $\to$ Sign and confirms that the resulting signature verifies against the original (unchanged) $\tilde b$.
|
|
|
|
\subsection{Forced erasure}
|
|
\label{sec:resharing-erasure}
|
|
|
|
After the activation cert is accepted, every old share MUST be erased. The package provides \texttt{EraseShare} as a reasonable-effort overwrite of every \texttt{uint64} coefficient in every level of every polynomial in the share. Production deployments SHOULD additionally:
|
|
\begin{enumerate}
|
|
\item Pin the share allocation with \texttt{mlock} (Linux) or equivalent so paged-out swap cannot leak it after the fact.
|
|
\item Call \texttt{EraseShare} inline at the activation moment; do not retain stale references to the old share.
|
|
\item Optionally trigger \texttt{runtime.GC()} to free non-pinned references; pinned memory is unaffected.
|
|
\end{enumerate}
|
|
Failure to erase undermines the proactive-security guarantee: a long-running adversary that compromises a node \emph{after} resharing-activation gains the old share if it remains in memory, and an old-share leak combined with $< t_{\mathrm{new}}$ new shares does \emph{not} reconstruct the master secret --- but it does pin one position of the original Shamir polynomial, eroding the threshold security margin in a way that aggregates over many epochs.
|
|
|
|
\subsection{Quasar consensus integration}
|
|
\label{sec:resharing-quasar}
|
|
|
|
The Quasar epoch manager (\texttt{consensus/protocol/quasar/epoch.go}) currently regenerates a \emph{fresh} group public key at every validator-set rotation by calling \texttt{coronaThreshold.GenerateKeys}. Under \textsc{Reshare}, the group public key is invariant for the entire group lineage; the rotation method is renamed for clarity:
|
|
|
|
\begin{center}
|
|
\begin{tabular}{ll}
|
|
\textbf{Old name} & \textbf{New name} \\ \hline
|
|
\texttt{RotateEpochKeys} & \texttt{ReshareEpoch} \\
|
|
\texttt{EpochKeys} & \texttt{EpochShareState} \\
|
|
\texttt{RotateEpoch(newVals)} calling \texttt{GenerateKeys} & \texttt{ReshareEpoch(newVals)} calling \texttt{Reshare(\ldots)} \\
|
|
GroupKey changes per epoch & GroupKey persistent for group lineage \\
|
|
\end{tabular}
|
|
\end{center}
|
|
|
|
\noindent
|
|
The forward-compat sketch in \texttt{consensus/protocol/quasar/reshare\_epoch.go} declares \texttt{ReshareEpoch} as an alias for \texttt{RotateEpoch} pending the body change, and exposes \texttt{ActivationMessage\-Personalization} plus the canonical transcript-input shape. The full integration checklist (renaming, transport reuse, complaint workflow, slashing evidence ingestion) is documented in \texttt{pulsar/reshare/quasar\_integration.go}.
|
|
|
|
\subsection{KAT cross-implementation contract}
|
|
\label{sec:resharing-kat}
|
|
|
|
The Go canonical at \texttt{pulsar/reshare/reshare.go} is byte-equivalent to the C++ port at \texttt{luxcpp/crypto/pulsar/reshare/} under a deterministic SHA-256 counter-mode PRNG. The KAT oracle at \texttt{pulsar/cmd/reshare\_oracle/main.go} emits 22 entries to \texttt{luxcpp/crypto/pulsar/test/kat/reshare\_kat.json}: 16 \textsc{Reshare} entries and 6 \textsc{Refresh} entries.
|
|
|
|
The 16 \textsc{Reshare} entries cover:
|
|
\begin{itemize}
|
|
\item Threshold tightening: $(2, 3) \to (3, 5)$, $(3, 5) \to (5, 7)$, $(5, 7) \to (7, 11)$
|
|
\item Threshold loosening: $(5, 7) \to (3, 5)$, $(5, 7) \to (2, 3)$
|
|
\item Same-threshold rotation: $(3, 5) \to (3, 5)$ disjoint, $(5, 7) \to (5, 7)$ disjoint
|
|
\item Committee growth: $(2, 3) \to (5, 9)$, $(2, 3) \to (2, 9)$
|
|
\item Partial overlap: $(3, 5) \to (3, 5)$ with shared and fresh IDs
|
|
\item Edge: $n_{\mathrm{new}} = t_{\mathrm{new}} + 1$ (smallest valid new committee)
|
|
\end{itemize}
|
|
|
|
The 6 \textsc{Refresh} entries cover $t \in \{1, 2, 3, 5, 7\}$ across canonical and relabelled committee IDs, including the degenerate $t = 1$ case (where \textsc{Refresh} must be the identity). Each entry pins (i) the planted secret $s$, (ii) the deterministic seed for the old Shamir polynomial coefficients, (iii) the seed for the resharing/refresh PRNG, (iv) the byte-exact old shares, (v) the byte-exact new shares, and (vi) per-share SHA-256 commitments. The C++ test reads the JSON, replays both primitives under the pinned PRNG seeds, and asserts byte-equality on all 22 entries.
|
|
|
|
\subsection{Performance}
|
|
|
|
The dominant cost of \textsc{Reshare} is dealing: each old party in $\mathcal{Q}$ samples $(t_{\mathrm{new}} - 1)$ polynomials in $R_q^{n_{\textrm{vec}}}$ ($\approx t_{\mathrm{new}} \cdot n_{\textrm{vec}} \cdot \varphi$ uniform $\mathbb{F}_q$ samples, totaling $\approx 7 \cdot 256 \cdot t_{\mathrm{new}}$ samples per party for the production parameters $n_{\textrm{vec}} = 7$, $\varphi = 256$) and evaluates the resulting polynomial at $|\mathcal{N}|$ points. Each evaluation is a Horner sweep over $t_{\mathrm{new}}$ coefficients, dominated by $t_{\mathrm{new}} \cdot n_{\textrm{vec}} \cdot \varphi$ modular multiplications.
|
|
|
|
For the canonical Quasar-21 deployment ($t_{\mathrm{new}} = 14$, $|\mathcal{N}| = 21$), each party performs $\approx 14 \cdot 21 \cdot 7 \cdot 256 \approx 5 \cdot 10^5$ modular multiplications. On a single Apple Silicon M2 Ultra core this completes in well under $10$~ms, dominated by the BLAKE3-XOF stream for the random coefficients. The wire cost is $|\mathcal{Q}| \cdot |\mathcal{N}| \cdot n_{\textrm{vec}} \cdot \varphi \cdot \lceil \log_2 q \rceil / 8 \approx 14 \cdot 21 \cdot 7 \cdot 256 \cdot 6 = 3.16$~MiB total across the committee, or $\approx 150$~KiB per ordered pair. The latency is dominated by the slowest pairwise channel, not by computation.
|
|
|
|
In comparison, a fresh trusted-dealer Gen at the same parameters costs the same $\approx 5 \cdot 10^5$ modular multiplications per share plus an additional $A \cdot s$ multiplication ($m_{\textrm{pk}} \cdot n_{\textrm{vec}} \cdot \varphi^2 / 2 \approx 1.8 \cdot 10^6$ multiplications under NTT) and the discrete-Gaussian sampling for $s, e$. The crucial cost difference is not asymptotic but architectural: \textsc{Reshare} preserves the public key, avoids a single dealer holding $s$, and runs entirely on operational infrastructure --- no offline ceremony required.
|