This page describes the cryptographic scheme implemented in fedimint-tpe:
an encryption of a 32-byte secret to a keyholder, built from a pairing-based
committed Diffie–Hellman construction over BLS12-381. It is used by the
Lightning v2 module to encrypt a payment preimage to the
federation, bound to a contract commitment, so that the secret can be recovered once the
contract's conditions are met.
Sections 1–5 present the complete scheme with a single keyholder; Section 6 then replaces the keyholder by a $t$-of-$n$ federation of guardians — a standard transformation that exploits only the linearity of the decryption operation in the secret key, and is the same threshold backbone as the blind signature scheme. Throughout, the encryptor and the keyholder play the two sides of a Diffie–Hellman exchange: the same shared secret is computed by the encryptor from its ephemeral key and the keyholder's public key, and by the keyholder from its secret key and the ephemeral public key.
$K := \mathbb{Z}/p\mathbb{Z}$ is the field of integers modulo the prime $p$. Let $\mathbb{G}_1, \mathbb{G}_2, \mathbb{G}_T$ be the BLS12-381 pairing groups of order $p$, written additively, with generators $g_1 \in \mathbb{G}_1$, $g_2 \in \mathbb{G}_2$ and nondegenerate $K$-bilinear pairing $e : \mathbb{G}_1 \times \mathbb{G}_2 \to \mathbb{G}_T$. Keys and the ephemeral public key live in $\mathbb{G}_1$; hashed messages and signatures live in $\mathbb{G}_2$.
The plaintext and the binding value are:
| Value | Meaning |
|---|---|
| $\mathit{preimage} \in \{0,1\}^{256}$ | the 32-byte secret to be encrypted (a Lightning preimage) |
| $\mathit{cm}$ | a SHA-256 commitment the ciphertext is bound to (the contract / payment hash) |
A hash $\mathsf{H}_{\mathbb{G}_2} : \{0,1\}^* \to \mathbb{G}_2$ maps bytes into the second group, modeled as a random oracle.
The keyholder's secret key is a scalar $x \in K$ with public key in $\mathbb{G}_1$: $$\mathsf{sk} = x \in K, \qquad \mathsf{pk} = x\, g_1 \in \mathbb{G}_1.$$
To encrypt under $\mathsf{pk}$ and bind to commitment $\mathit{cm}$, the encryptor derives an ephemeral scalar $\eta \in K$ from a 32-byte encryption seed and forms:
$$\begin{aligned} E &= \eta\, g_1 \in \mathbb{G}_1 && \text{(ephemeral public key)} \\[2pt] D &= \eta\, \mathsf{pk} = \eta x\, g_1 \in \mathbb{G}_1 && \text{(Diffie–Hellman shared secret)} \\[2pt] \mathit{ct} &= \mathit{preimage} \,\oplus\, \mathsf{SHA256}(D) && \text{(one-time-pad encryption)} \\[2pt] M &= \mathsf{H}_{\mathbb{G}_2}(\mathit{ct} \,\|\, E \,\|\, \mathit{cm}) \in \mathbb{G}_2 && \text{(message point binding } \mathit{ct}, E, \mathit{cm}) \\[2pt] S &= \eta\, M \in \mathbb{G}_2 && \text{(ephemeral BLS signature)} \end{aligned}$$
The ciphertext is the triple $(\mathit{ct}, E, S)$. The shared secret $D$ is the ECDH point of the ephemeral key $\eta$ and the keyholder's key $x$; its hash is used as a one-time pad over the preimage.
Anyone can check that a ciphertext is well formed, against its commitment, with a single pairing equation: $$e(g_1,\; S) \;\stackrel{?}{=}\; e(E,\; M), \qquad M = \mathsf{H}_{\mathbb{G}_2}(\mathit{ct} \,\|\, E \,\|\, \mathit{cm}).$$ This holds for an honest ciphertext because, by bilinearity, $$e(g_1,\; \eta M) = e(g_1, M)^{\eta} = e(\eta g_1,\; M) = e(E,\; M).$$ A passing check certifies that $S$ is the ephemeral key's signature on the exact bytes of $(\mathit{ct}, E, \mathit{cm})$ — i.e. that $S = \eta M$ for the same $\eta$ as $E = \eta g_1$.
The keyholder recovers the shared secret from the other side of the Diffie–Hellman exchange. Where the encryptor formed $D = \eta\,\mathsf{pk}$ from the ephemeral scalar and the public key, the keyholder applies its secret key to the ephemeral public key: $$D \;=\; x\, E \;=\; x\,\eta\, g_1 \;=\; \eta\,(x\, g_1) \;=\; \eta\,\mathsf{pk},$$ the very same group element. The preimage is then unmasked with the one-time pad: $$\mathit{preimage} \;=\; \mathit{ct} \,\oplus\, \mathsf{SHA256}(D).$$
Nothing above requires a single keyholder. Because decryption $$D \;=\; x\, E$$ is $K$-linear in the secret key $x$, the keyholder can be replaced by a $t$-of-$n$ federation via Shamir secret sharing plus Lagrange interpolation in the exponent — the same construction that thresholdizes BLS signing. Encryption (§2) and the validity check (§3) are unchanged; only the recovery of $D$ is distributed.
The key $x$ is Shamir-shared: pick a random polynomial $f \in K[X]$ of degree at most $t-1$ with $f(0) = x$, and give guardian $i \in \{1, \dots, n\}$ the key share and public key share $$\mathsf{sk}_i = f(i) \in K, \qquad \mathsf{pk}_i = f(i)\, g_1 \in \mathbb{G}_1.$$ The aggregate public key is unchanged: $\mathsf{pk} = f(0)\, g_1 = x\, g_1$. Evaluation points are the indices $i = \texttt{peer} + 1$, since the point $0$ is reserved for the secret. In a real federation this sharing is set up without a trusted dealer by a distributed key generation.
Each guardian applies its share to the ephemeral public key, producing a decryption-key share $$D_i \;=\; f(i)\, E \;\in\; \mathbb{G}_1,$$ which the requester verifies independently (on a ciphertext already known to be valid, §3) by the pairing equation $$e(D_i,\; M) \;\stackrel{?}{=}\; e(\mathsf{pk}_i,\; S),$$ both sides being $e(g_1, M)^{f(i)\,\eta}$: the left is $e(f(i)\eta g_1, M)$ and the right is $e(f(i) g_1,\, \eta M)$. So a misbehaving guardian is identified immediately and its share discarded.
Any set $S$ of $t$ valid shares then interpolates to the shared secret. With the Lagrange coefficients $\lambda_i^S$ that reconstruct a degree-$(t-1)$ polynomial at the point $0$, $$\lambda_i^S \;=\; \prod_{\substack{j \in S \\ j \neq i}} \frac{j}{\,j - i\,}, \qquad \sum_{i \in S} \lambda_i^S\, f(i) \;=\; f(0) \;=\; x,$$ the recombination is a Lagrange combination in the exponent. As with signing, it lands on the single-keyholder secret because scalar multiplication $K \times \mathbb{G}_1 \to \mathbb{G}_1$ is bilinear, hence linear in its scalar argument, so it commutes with the Lagrange sum: $$\begin{aligned} D &\;=\; \sum_{i \in S} \lambda_i^S\, D_i && \text{(combine the shares)} \\[2pt] &\;=\; \sum_{i \in S} \lambda_i^S\, \bigl(f(i)\, E\bigr) && \text{(each share is } D_i = f(i)\,E) \\[2pt] &\;=\; \Bigl(\sum_{i \in S} \lambda_i^S\, f(i)\Bigr) E && \text{(linearity in the scalar argument)} \\[2pt] &\;=\; f(0)\, E && \text{(Lagrange interpolation at } 0) \\[2pt] &\;=\; x\, E \;=\; \eta\,\mathsf{pk}, && (f(0) = x) \end{aligned}$$ which is exactly the shared secret of §4, so the preimage is unmasked as before, $\mathit{preimage} = \mathit{ct} \oplus \mathsf{SHA256}(D)$. Fewer than $t$ guardians learn nothing about $x$ and cannot assemble $D$.
hash_to_curve ciphersuite — fine as a random oracle into the group, but
non-interoperable. Note also that the share- and aggregate-verification routines
assert ciphertext validity internally and will panic on a malformed ciphertext, so
callers must run the ciphertext check of §3 first.