This page describes the cryptographic scheme implemented in fedimint-tbs:
an ad-hoc threshold blind signature built from
BLS signatures over the pairing-friendly curve BLS12-381. It is the
primitive behind Fedimint's Chaumian e-cash: the federation blindly
signs a note so that no guardian learns which note it signed, yet any later holder can
verify the note against a single aggregate public key.
Sections 1–3 present the scheme with a single signer; Section 5 then replaces the signer by a $t$-of-$n$ federation of guardians — a standard transformation that exploits only the linearity of signing in the secret key.
Throughout, $K := \mathbb{Z}/p\mathbb{Z}$ denotes the field of integers modulo the prime $p$, and $K^n$ the $K$-vector space of (column) vectors of length $n$.
Let $\mathbb{G}_1, \mathbb{G}_2, \mathbb{G}_T$ be the BLS12-381 pairing groups, all of order $p$ and written additively, with fixed generators $g_1 \in \mathbb{G}_1$, $g_2 \in \mathbb{G}_2$, and let $e : \mathbb{G}_1 \times \mathbb{G}_2 \to \mathbb{G}_T$ be the pairing, a nondegenerate $K$-bilinear map. Messages and signatures live in $\mathbb{G}_1$; keys live in $\mathbb{G}_2$.
A note is identified by a message point obtained by hashing arbitrary bytes into the curve, $\mathsf{H}_{\mathbb{G}_1} : \{0,1\}^* \to \mathbb{G}_1$, modeled as a random oracle:
$$m \;=\; \mathsf{H}_{\mathbb{G}_1}(\mathit{data}) \;\in\; \mathbb{G}_1.$$
Because $\mathsf{H}_{\mathbb{G}_1}$ is a random oracle, no party knows the discrete logarithm of $m$ with respect to $g_1$, so a signature on $m$ cannot be produced by the holder alone.
The signer's secret key is a single scalar $x \in K$, and the public key publishes it in $\mathbb{G}_2$:
$$\mathsf{sk} = x \in K, \qquad \mathsf{pk} = x\, g_2 \in \mathbb{G}_2.$$
To have a note signed without revealing it, the user samples a blinding key $r \stackrel{\$}{\leftarrow} K \setminus \{0\}$ and sends the blinded message
$$\bar{m} \;=\; r\, m \;\in\; \mathbb{G}_1.$$
The signer applies its key and returns the blinded signature
$$\bar{\sigma} \;=\; x\, \bar{m} \;=\; r\,(x\,m) \;\in\; \mathbb{G}_1.$$
The user verifies the response against the public key, $e(\bar{m}, \mathsf{pk}) = e(\bar{\sigma}, g_2)$, and removes the blinding by dividing out $r$ in the exponent:
$$\sigma \;=\; r^{-1}\, \bar{\sigma} \;=\; x\, m,$$
a plain BLS signature on $m$ that verifies under $\mathsf{pk}$ via the equation of §2.
Nothing above requires a single signer. Because signing $$\bar{\sigma} \;=\; x\, \bar{m}$$ is $K$-linear in the secret key $x$, the signer can be replaced by a $t$-of-$n$ federation via Shamir secret sharing plus Lagrange interpolation in the exponent — the textbook threshold-BLS construction.
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_2 \in \mathbb{G}_2.$$ The aggregate public key is unchanged: $\mathsf{pk} = f(0)\, g_2 = x\, g_2$. (Evaluation points are the indices $i = \texttt{peer} + 1$, since the point $0$ is reserved for the secret itself.) In a real federation this sharing is set up without a trusted dealer by a distributed key generation.
Each guardian signs the blinded message with its own share, $$\bar{\sigma}_i \;=\; f(i)\, \bar{m},$$ and the user verifies each share independently against $\mathsf{pk}_i$ by the pairing equation $e(\bar{m}, \mathsf{pk}_i) = e(\bar{\sigma}_i, g_2)$ — so a misbehaving guardian is identified immediately and its share discarded. Any set $S$ of $t$ valid shares then interpolates to the federation signature. Write $\lambda_i^S$ for the Lagrange coefficients 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\text{so that}\qquad \sum_{i \in S} \lambda_i^S\, f(i) \;=\; f(0) \;=\; x \quad\text{for every } f \text{ with } \deg f \le t-1.$$
The aggregation is this same combination carried out in the exponent. It lands on the single-signer signature because scalar multiplication $K \times \mathbb{G}_1 \to \mathbb{G}_1$, $(\lambda, v) \mapsto \lambda\,v$, is bilinear — in particular linear in its scalar argument — so it commutes with the Lagrange sum, letting the interpolation run on the group element $\bar{m}$ rather than on the field values $f(i)$: $$\begin{aligned} \bar{\sigma} &\;=\; \sum_{i \in S} \lambda_i^S\, \bar{\sigma}_i && \text{(combine the shares)} \\[2pt] &\;=\; \sum_{i \in S} \lambda_i^S\, \bigl(f(i)\, \bar{m}\bigr) && \text{(each share is } \bar{\sigma}_i = f(i)\,\bar{m}) \\[2pt] &\;=\; \Bigl(\sum_{i \in S} \lambda_i^S\, f(i)\Bigr) \bar{m} && \text{(linearity in the scalar argument)} \\[2pt] &\;=\; f(0)\, \bar{m} && \text{(Lagrange interpolation at } 0) \\[2pt] &\;=\; x\, \bar{m}. && (f(0) = x) \end{aligned}$$ The middle equality is the crux: pulling the coefficients $\lambda_i^S$ out through the scalar multiplication is exactly the bilinear map commuting with the interpolation, and it turns $t$ independent group elements back into the evaluation of the joint key polynomial at the secret point $0$. (For a $1$-of-$1$ federation the single share is already the aggregate and no interpolation is needed.)
The user then unblinds as in §3, $\sigma = r^{-1}\bar{\sigma} = x\,m$, and the result verifies against the aggregate public key $\mathsf{pk}$ — identical to the single-signer scheme. Fewer than $t$ guardians learn nothing about $x$ and cannot assemble a valid combination.
aggregate_public_key_shares, though the aggregate is more
directly obtained by evaluating the DKG polynomial at $0$ during setup.
hash_to_curve ciphersuite — fine as a random oracle into the group,
but non-interoperable with other BLS implementations.