diff --git a/docs/concepts/concepts-index.md b/docs/concepts/concepts-index.md new file mode 100644 index 00000000..b99cc947 --- /dev/null +++ b/docs/concepts/concepts-index.md @@ -0,0 +1,33 @@ +# Documentation Overview + +This set of documents describes the core logic and workflows implemented in the system. + +## Prerequisites + +The reader is expected to have basic familiarity with the following concepts: + +- Cyclic groups and finite fields +- Polynomial arithmetic +- Elliptic curve cryptography (ECC) +- Core cryptographic principles, including: + - Symmetric and asymmetric encryption + - Key exchange mechanisms + +## Threshold Encryption + +- [High Level Client Integration Flow](./threshold-encryption/3-full-client-flow.md) + Explains the client role in the full process. Includes detailes about data structures used, but provides little detail on the underlying processes. Good if you just need to know the basics. + +- [Threshold Encryption: Full Flow](./threshold-encryption/2-te-full-flow.md) + End-to-end description of the API-exposed threshold encryption process involving key generation, encryption, and decryption. Includes all process details of all stages. Does not include details on the underlying mathematics. Good if you need to know how the entire process works, and how threshold encryption is used. + +- [How Threshold Encryption Works](./threshold-encryption/1-threshold-encryption.md) + Introduction to the core principles and structure of the threshold encryption scheme. INcludes details on the underlying mathematics. Good if you need to know how it works under the hood. + +- [Validation Optimizations](./threshold-encryption/4-optimizations.md) + Explains optimizations implemented to speedup validation steps. + +## Distributed Key Generation (DKG) + +- [Distributed Key Generation Protocol](./distributed-key-generation/dkg.md) + Description of the collaborative key generation protocol, ensuring no single participant holds the full private key. Includes mathematical details. Also includes communication steps. Good if you need to know either communication steps or the details. diff --git a/docs/concepts/distributed-key-generation/dkg.md b/docs/concepts/distributed-key-generation/dkg.md new file mode 100644 index 00000000..6687c0f0 --- /dev/null +++ b/docs/concepts/distributed-key-generation/dkg.md @@ -0,0 +1,143 @@ +# Distributed Key Generation (DKG) + +**Distributed Key Generation (DKG)** is a cryptographic protocol that allows a group of `n` participants to collaboratively generate a shared public key and corresponding private key shares, such that **no individual participant ever learns the full private key**. + +DKG is designed for **threshold cryptographic schemes**, where any subset of at least `t` participants (with `t ≤ n`) can jointly perform cryptographic operations, while smaller subsets learn nothing about the secret. + +Unlike schemes that rely on a trusted dealer to distribute key shares, DKG ensures that the generation and distribution of the secret is: + +- **Decentralized** — there is no central authority. +- **Verifiable** — all parties can validate the correctness of the shares. +- **Secure** — adversarial participants cannot compromise the secret unless they reach the threshold. + +This document describes all stages of the DKG protocol, with justifications and background provided throughout. + +To aid comprehension, diagrams are used: + +- Elements **in green** represent **public key-related data**. +- Elements **in orange** represent **private key-related data**. +- **Circles with numbers** depict participating nodes and their indices. + + +--- + +# Step 1: Local Polynomial Generation + +Each participant `i` in {1, ..., n} independently generates a random secret polynomial `f_i(x)` over a finite field `F`, of degree `t - 1`. The diagram below shows an example with 4 nodes, which we will use throughout the rest of this document. + +Diagram +
+
+ +The coefficients of each polynomial are chosen uniformly at random. + +This polynomial serves two purposes: +- It defines the shares that participant `i` will distribute to all other participants. +- It enables the derivation of a **public commitment vector**, which will be used for share verification. + +Besides the computation of the polynomial, each node also computes its public commitment vector `Pub_i` as `f_i(x) * G`, where `G` is the **generator** of the chosen elliptic curve group. For simplicity, we only show this computation for node 0 in the diagram above, but all other nodes also compute it using their own polynomials. + +Although we represent the commitment vector as `f_i(x) * G` in the diagram above for simplicity, it is actually stored as a vector of each coefficient: + +
+Diagram +
+ +Thus, each index `i` of this public commitment vector will be `a_i * G`. + +### Aggregated Global Polynomial + +The overall system secret is defined implicitly by the sum of all individual polynomials: + +Diagram + +This results in a global polynomial `P(x)` of degree at most `t - 1`, which is never explicitly constructed or known in its entirety by any single party. However, each participant will end up holding one evaluation \( P(j) \), corresponding to their private share, and the public commitment to \( P(x) \) will be derived from the commitments to each \( f_i(x) \). + +The `CommonPrivateKey` of the system (the same for entrie system) is set as `P(0)`, i.e, the **constant term of the global polynomial** +The `CommonPublicKey` will be defined as `P(0) * G`. I.e, `CommonPrivateKey * G`. + +Both will only be computed at the final step as we will se later on. + +Diagram +
+
+ + +--- + +# Step 2: Share Distribution and Public Commitment Broadcasting + +After each participant generates its secret polynomial and public commitment vector, the next step is to exchange information with the group. This step ensures that each node contributes to the final shared secret, and allows others to verify the validity of those contributions. + +#### Private share distribution + +Each node `i` evaluates its polynomial `f_i(x)` at every index `j = 1 to n`. This produces a list of `n` scalar values: + +``` +Priv_i = [f_i(1), f_i(2), ..., f_i(n)] +``` + +Then, node `i` sends `f_i(j)` **only** to node `j`. This means each node receives one value from every other node. + +- Node `j` will receive: `f_1(j), f_2(j), ..., f_n(j)` + +This process is illustrated below: + +
+Diagram +
+
+ +#### Public commitment vector distribution + +Each node `i` will also share its **public commitment vector**, computed in **Step 1**. As explained, this public key can be seen either as `f_i(x) * G`, or as a list of **t** items, where each index is a coefficient multiplied by `G`. Both , but represented in different ways. + +It is important to notice that the list representation of the public key is of size **t**, whereas the list of private shares is of size **n**. +The diagram below shows this process: + +
+Diagram +
+
+ +#### Final state + +As an example, at the end of steps 1 and 2, node `3` will end up with the following state: +- A **public commitment vector** `Pub_i` from each other node `i` (green boxes in diagram below) +- An evaluation at point `3` from the polynomial of each node `i` (orange boxes in diagram below) + +
+Diagram +
+ +--- + +# Step 3 - Validation + +Upon having received all public and private key shares from all other nodes, each node `i` performs the validation of each (`Pub_j`, `Priv_j[i]`) pair received from node `j`. + +The validation process is simple. Each node `i`: +1. Having received `f_j(x) * G` from node `j`, compute `f_j(i) * G` by substituting the `x` values in the polynomial by `i`. Notice that we only have acces to the coefficients already multiplied by `G`, not the original coefficients. And due to ECDL problem, we cannot get `a` from `a * G`. This step is represented in the left bottom half of the diagram below, using the green boxes. Let's call the result of this operation `[f_j(i)*G]_pub` since it was computed from `Pub_j`. +2. Having received `f_j(i)` from node `j`, we compute the same end value `f_j(i) * G`. But this time we start from the private share we got from `j`, instead of using the public key. Let's call this result `[f_j(i)*G]_priv`. +3. Compare `[f_j(i)*G]_pub` with `[f_j(i)*G]_priv`. If they differ, validation does not pass. Else, it passes. + +
+Diagram +
+ +--- + +# Step 4 - Computation of all needed keys + +After having validated all data received from all nodes, each node computes the following keys: +- `PrivateKeyShare` - Sum of all `f_j(i)` received, which is the same as `P(3)`, where `P(x)` is the global polynomial (sum of all individual plynomials). Meaning each node will end up with the evaluation of `P(x)` for its node index. This will come handy when we collect several other points from other nodes in order to reconstruct the original polynomial from its points. This key is **unique to this node**. +- `PublicKeyShare` - Computed as `PrivateKeyShare * G`. Will be **unique to this node**. +- `CommonPublicKey` - Computed as the sum of all indices 0 from all commitment vectors - `Pub_i` - received. This results in the same value as `P(0) * G` without having to know `P(0)`. + +Below we show an example of final state for node 3: +
+Diagram +
+
+ +After **Step 4**, the system is ready to start using either BLS signatures, or [Threshold Encryption](../threshold-encryption/1-threshold-encryption.md). \ No newline at end of file diff --git a/docs/concepts/threshold-encryption/1-threshold-encryption.md b/docs/concepts/threshold-encryption/1-threshold-encryption.md new file mode 100644 index 00000000..c0bca047 --- /dev/null +++ b/docs/concepts/threshold-encryption/1-threshold-encryption.md @@ -0,0 +1,407 @@ +# Threshold Encryption + +Threshold encryption (TE) is a form of public key encryption in which the decryption key is distributed across multiple parties. A message can only be decrypted if a threshold number of parties collaborate. This technique ensures both **confidentiality** and **fault tolerance** in cryptographic systems, especially in distributed environments. + +This document starts by explaining the **encryption** phase of the threshold encryption process, focusing on the generation of a `CipheredKey` object from a message, a public key, and a random scalar. + +Then we explain the **decryption** process in order to retrieve the original plaintext. + +Finally we explain how **validation** happens in the different stages of the encryption-decryption process such that any node can validate the correctness of the data it is using for the threshold encryption process. + +> **Note:** This process assumes the [DKG](../distributed-key-generation/dkg.md) procedure was already executed, and each node is already in possession of its `PrivateKeyShare` as well as the global `CommonPublicKey`. + +--- + +# 1. Encryption + +Below is a visual representation of the generic encryption process, which will be used as reference in the `step-by-step breakdown` section: + +Diagram + +
+ +The process takes `3` inputs: +- `m` - Plaintext message. Must be exactly `32` bytes. For our use case, this message will be an `AES256` key, which is exactly `32` bytes. We explain why we use an `AES256` key in [te-full-flow.md](./2-te-full-flow.md). +- `Y = x⋅P` - A public key (G2 point), generated from the respective private key share `x`, where `P` is a known generator. +- `r`: A random scalar (ephemeral secret). + +And it generates a single output - A `CipheredKey` struct that is always `224` bytes long, and includes `V`, `W` and `U` fields. + +--- + +## Step-by-Step Breakdown + +### 1. Compute `V` + +We start by computing: + +``` +V = G(r ⋅ Y) ⊕ m +``` + +- `r ⋅ Y` is a scalar multiplication over the elliptic curve, producing a secret point using the common public key `Y` and the secret scalar `r`. +- `G(⋅)` is a cryptographic hash function that maps the elliptic curve point to a fixed-length byte array. +- `⊕ m` denotes a bitwise XOR between the derived key and the plaintext message `m`. + +The result `V` is 32 bytes and constitutes the encrypted (masked) version of the key. `V` can only be computed or reversed if either `r` or `r * Y` is known. Without knowledge of either of them, no party can recover `m` from `V`. + +The security of this construction relies on two cryptographic hardness assumptions: +1. **One-wayness of hash functions**: Given `A = G(x)`, it is computationally infeasible to recover `x` from `A`. +2. **Elliptic Curve Discrete Logarithm Problem (ECDLP)**: Given a point `A = r ⋅ Y` and the public key `Y`, it is computationally infeasible to determine the scalar `r`. + +These properties ensure that even if an adversary knows `V` and `Y`, they cannot recover the plaintext `m` without the original random scalar `r`. + +### 2. Compute `U` + +The ephemeral public component derived from the random scalar: + +``` +U = r ⋅ P +``` + +The value `U` is essential for enabling decryption: it is used by decrypting nodes to reconstruct the shared secret via pairing-based operations. This mechanism allows the sender to transmit a masked message without revealing `r`, while still enabling the authorized parties to recover it collaboratively. + +### 3. Compute `W` + +A binding value used for integrity verification: + +``` +W = r ⋅ H(U, V) +``` + +Here, `H(U, V)` is a hash-to-curve function that maps the tuple `(U, V)` to a point in the elliptic curve group. The resulting `W` value acts as a cryptographic commitment, binding together the ephemeral component `U` and the encrypted key `V`. + +--- + +## Output: `CipheredKey` + +The output of the encryption process is the `CipheredKey` struct, composed of the following fields: + +| Field | Type | Size (bytes) | +|-------|----------|--------------| +| `V` | Bytes | 32 | +| `W` | G1 point | 64 | +| `U` | G2 point | 128 | + +This tuple forms the encrypted representation of the message `m`. Only a valid quorum of decryptors (holding key shares) will be able to recover the original message. + +--- + +# 2. Decryption + +We've seen earlier that the ciphertext is formed via `G( r * Y) ⊕ m`. This means the only way to recover `m` is to recompute `G( r * Y)`, without ever needing to know `r`. +Recall that : +- The common public key `Y` is the same as `s * P`, where `s` is the common private key, and `P` is the generator. +- The ciphertext comes with `U = r * P` + +This means that: + +``` +r * Y = r * (s * P) = s * (r * P) = s * U +``` + +Since we have access to `U`, we need to somehow compute `s`. This is what the decryption process is about - collecting enough private key shares in order to merge them into `s`. This is all done implicitly, without ever allowing any party to know `s`. That is, the decryption process results in the production of `r * Y = s * U`, without ever: +- Knowing any private key share `s_i` (besides possibly its own) +- Knowing the ephemeral secret `r` +- Knowing the common private key `s` + + +The following diagram (**Diagram 2**) shows the communication process used to decrypt the ciphertext, and is used as reference for the following 2 subsections: + +Diagram + +This section will focus on the **Deciphering** part of the diagram. The ciphering part is already explained in the previous section. + +## 2.1 Computing Decryption Shares + +To compute `s * U`, as explained earlier, we must share **t** encoded private key shares `s_i`, so that `s_i` itself is not exposed. To do this, each node computes a **partial decryption share** (`DecryptShare` in the diagram) from the ciphertext. This share is then broadcast into the network such that each node eventually gets enough shares from all other nodes, so that they can use it to get the original plaintext. +The share computation is shown in detail in the diagram below: + +Diagram + +The process uses 2 inputs: +- `U` field from `Ciphertext` +- `s_i` - Private key share of node `i`. Only known by node `i`, and never exposed. + +Each node computes: +``` +D = s_i ⋅ U +``` + +And broadcasts this value to all other nodes. + +## 2.2 Merging Decryption Shares + +Each node waits to receive a supermajority (t >= 2/3) of `DecryptShares` from other nodes. Once enough shares are received, each node merges them. +This process assumes you are familiar with **Lagrange Interpolation**. If not, we informally explain it below: +- Given a set of **t** points from a polynomial P(x) of degree **t-1**, Lagrange Interpolation allows rebuilding the original polynomial (by computing its coefficients) from the points only. +- Intuitively, compute a polynomial `f_i(x)` that has the same value as `P(i)` (global polynomial) for `f_i(i)`, and has the value `0` for all other points `j`: `f_i(j) = 0, j != i`. Then, we only need to **sum** all these polynomials to get `P(x)`. It is easy to see that only one such polynomial will pass through each point, while all others will be 0. So the final polynomial must pass through all points. + +> Here is a video that explains [Lagrange interpolation](https://www.youtube.com/watch?v=bzp_q7NDdd4) + +The original Lagrange Interpolation equation is: + +
+Diagram +
+ +Where `x_j - x_i` in the denominator stands for the roots, which enforce that the polynomial has value 0 for all other points `j`, and `P(x_i)` is multiplied to enforce `f_i(i) = P(x_i)`. + +This allows constructing the original polynomial. Since we only care about rebuilding the `CommonPrivateKey`, i.e., `s = P(0)`, where `P(x)` is the global polynomial as described in [DKG](../distributed-key-generation/dkg.md), we only need to compute this point specifically. So we set `x` to 0 in the above equation, and get: + +
+Diagram +
+ +Recall that we do not have access to `P(x_i) = s_i` - this is the private key share of each node - and that our goal is to compute `r * Y`. +Since we have `D_i`, we can multiply the entire equation by `U`, and end up with `D_i` without needing to know `s_i`. +- We substituted the product operator by lambda symbol for simplicity +- We substituted `P(x_i)` with `s_i`, as they are the same. + +
+Diagram +
+ +Now we have all the values needed to compute the above equation: +- We have `D_i` +- We have the node indices to compute the Lagrange coefficient + +Finally, notice that `P(0) * U` is the same as `r * Y`: + +
+Diagram +
+ +Allowing us to get the needed value to decrypt the original message. +The whole process is shown in the diagram below: + +
+Diagram +
+
+ +Notice that for Lagrange interpolation, the `x_i` and `x_j` values are the indices of the nodes (`idx[i]`) that provided the `DecryptShare`. After having computed `r * Y` from the decrypt shares (box in the middle), we recover the plaintext: +1. Compute `G( r * Y )`. Since we have `r * Y`, we only need to apply `G()` to it. +2. XOR the previous step's result with the value of field `V` from the ciphertext. Recall that `V = G( r * Y ) ⊕ m`. Thus: + +``` +V ⊕ G(r * Y) = G( r * Y ) ⊕ G( r * Y ) ⊕ m = m +``` +and we get the original plaintext. + +--- + +# 3. Validation + +`libBLS` provides a validation step after each main step in the TE flow. +Below we describe the encryption-time validation in detail. + +## 3.1 Validate Encryption + +**Goal.** Prove that the ciphertext `{U, V, W}` was built consistently with a single randomness `r`, i.e., that the same `r` links `U` and `W`, and that `W` binds to `(U, V)`. + +**Setup.** +- Groups: `G1`, `G2` with a bilinear pairing `e : G1 × G2 → GT`. +- Generators: `P ∈ G2`. +- Types: `U ∈ G2`, `W ∈ G1`, `H(U,V) ∈ G1`. +- Construction: `U = r ⋅ P`, `W = r ⋅ H(U,V)`. + +**Verifier computation.** +1. Recompute the hash-to-curve point: + `H' = H(U, V) ∈ G1`. +2. Check the pairing equation: + +```math +e(W, P) \stackrel{?}{=} e(H', U) +``` + +**Why it works (bilinear proof).** +Assuming honest formation with the *same* scalar `r`: +- Left side: $e(W, P) = e(r ⋅ H, P) = e(H, P)^r$. +- Right side: $e(H', U) = e(H, r ⋅ P) = e(H, P)^r$ (since $H' = H$). + +Thus both sides are equal **iff**: +- `W` was formed as `r ⋅ H(U,V)` for the same `U`, and +- `U` encodes the *same* scalar `r` as used in `W`. + +**What it guarantees.** +- **Consistency of randomness:** `U` and `W` use the same `r`. +- **Binding to ciphertext:** Because `H` hashes `(U,V)`, any change to `U` or `V` alters `H` and breaks the check. +- **Public verifiability:** Anyone can verify without knowing `r` or the plaintext `m`. +- **No leakage:** The check reveals nothing about `r`, `s`, or `m`. + +**Failure modes caught.** +- If an adversary swaps `U` or `V`, the recomputed `H'` changes and the equation fails. +- If `U` and `W` are created with different scalars, bilinearity no longer aligns and the equation fails. +- If `W` is random/forged, it will not satisfy the equality with overwhelming probability. + + + +## 3.2 Validate Each Decryption Share + +**Goal.** + +Given a ciphertext `{U, V, W}` and a purported decryption share $D_i$ from node $i$, verify that $D_i$ was computed correctly from the node’s secret share $s_i$ and that it matches the same encryption randomness $r$ that binds `{U,V,W}`. + + +**Setup.** + +During encryption: +```math +U = rP \in G_2,\qquad +W = r\,H(U,V) \in G_1, +``` +where $H(U,V)\in G_1$ is a hash-to-curve of `(U,V)`. + +For node $i$: +```math +Y_i = s_i P \in G_2,\qquad +D_i = s_i U = s_i (rP) = (r s_i) P \in G_2. +``` + +Let $e: G_1 \times G_2 \to G_T$ be a bilinear pairing. + + +**Validation equations.** + +1) **Ciphertext integrity (same $r$ across `U` and `W`).** + +```math +e(W, P)\;\stackrel{?}{=}\;e(H(U,V), U). +``` + If true, then $W = r H(U,V)$ and $U = rP$ share the same randomness $r$. (Same as in 3.1) + +2) **Per-share correctness (same $s_i$, same $r$).** + +```math +e(W, Y_i)\;\stackrel{?}{=}\;e(H(U,V), D_i). +``` + +**Why it works.** + +For 1), we have already seen why it works from 3.1. + +For 2), for a valid share with $Y_i=s_iP$ and $D_i=s_iU=s_i(rP)$: +```math +e(W,Y_i)=e(rH, s_iP)=e(H,P)^{r s_i},\qquad +e(H,D_i)=e(H, s_i r P)=e(H,P)^{r s_i}, +``` + so the second equality holds **iff** $D_i$ was computed from the *same* $r$ (via $U$) and the *correct* secret share $s_i$ (via $Y_i$). + + + +## 3.3 Validate Combined Decryption Shares + +**Goal.** + +Verify that the decrypted message is mathematically consistent with the ciphertext `{U, V, W}` and the public key $Y$, ensuring that the random scalar $r'$ recovered from the decrypted data corresponds to the same randomness $r$ used during encryption. + + +**Setup.** + +During encryption: + +```math +U = rP, \quad V = G(rY) \oplus m +``` + +where +- $r \in \mathbb{F}_r$ is a random scalar, +- $Y = sP$ is the public key, +- $G: G_2 \rightarrow \{0,1\}^{32}$ is a key-derivation hash, +- $m$ is the AES-256 key (plaintext). + +After decryption, the payload reveals a candidate scalar $r'$, which should equal $r$ if the decryption shares were correctly combined. + +**Validation Equation.** + +Compute: + +```math +Y' = r'Y +``` + +Derive: + +```math +m' = V \oplus G(Y') +``` + +and verify: + +```math +m' = m +``` + +**Why it works.** + +If the combined decryption shares are correct, then $r' = r$. +Hence: + +```math +Y' = r'Y = rY +``` + +and + +```math +G(Y') = G(rY) +``` + +Substituting into the expression for $m'$ gives: + +```math +m' = V \oplus G(rY) = (G(rY) \oplus m) \oplus G(rY) = m +``` + +The equality holds **if and only if** $r'$ matches the original randomness $r$ and the ciphertext `{U, V}` was not tampered with. Any alteration in $r'$, $V$, or $Y$ breaks the equality, making the decryption invalid. + +## 3.4 Validate Deciphered Message + +**Goal.** + +Verify that the AES key recovered from the decrypted payload is consistent with the ciphertext `{U, V, W}` and the public key $Y$, by confirming that the random scalar $r'$ extracted from the plaintext explains $V$. + +**Setup.** + +During encryption: +```math +U = rP,\qquad V = G(rY)\oplus m, +``` + +where $r\in\mathbb{F}_r$ is random, $Y=sP$ is the public key, $G: G_2\to\{0,1\}^{32}$ is a KDF/hash-to-key, and $m$ is the AES-256 key. + +After decryption of the outer AES-GCM payload, we obtain a candidate scalar $r'$ (encoded in the plaintext). The ciphertext component $V$ is available from `{U,V,W}`. + + +**Validation Equation.** + +Compute: +```math +Y' = r'Y,\qquad K' = G(Y'),\qquad m' = V \oplus K'. +``` +Accept iff: +```math +m' = m. +``` + + +**Why it works.** + +If the decrypted payload is correct and the combine of shares was honest, then $r'=r$. Hence: +```math +Y' = r'Y = rY,\qquad K' = G(Y') = G(rY). +``` +Substitute into $m'$: +```math +m' = V \oplus G(rY) = (G(rY)\oplus m)\oplus G(rY) = m. +``` +Therefore the equality holds **iff** $r'$ matches the original randomness and $V$ was not altered. + + +## Next Steps + +Although threshold encryption is powerful, it is slow for large messages. So it is best to use it alongside symmetric cryptography. This allows us to benefit from symmetric cryptography speed as well as threshold cryptography's security. + +This more robust process is explained [here](./2-te-full-flow.md). + diff --git a/docs/concepts/threshold-encryption/2-te-full-flow.md b/docs/concepts/threshold-encryption/2-te-full-flow.md new file mode 100644 index 00000000..7ca95dd1 --- /dev/null +++ b/docs/concepts/threshold-encryption/2-te-full-flow.md @@ -0,0 +1,233 @@ +# Threshold Encryption + +This document explains the complete encryption flow when using threshold encryption (TE). We first explain the base idea using a single public key, and later on we introduce the use of two public keys. The approach combines symmetric encryption for performance with threshold encryption for secure key encapsulation. + +> The support for two keys is necessary for certain scenarios, such as key rotation in a committee of nodes managing the TE public key. When the set of nodes responsible for threshold decryption changes, a new public key must be generated. During the rotation period, it may be required to encrypt data under both the old and new keys to maintain continuity. This approach allows ciphertexts to be decrypted by either key set, ensuring seamless transition and backward compatibility until the rotation is fully completed. + +# 1. Full Encryption Flow (Single Key) + +The process is designed to: + +1. Encrypt the plaintext using a randomly generated AES key (symmetric encryption). +2. Securely encrypt the AES key using threshold encryption with a TE common public key. +3. Construct a ciphertext that contains both the encrypted AES key and the encrypted payload. + +--- + +## Overview of the Process + +Diagram + +
+
+ + +The diagram depicts the internal steps involved in building the ciphertext. The steps occur on the client side and assume the availability of: +- A plaintext message `T` +- A TE common public key `Y₁` (e.g., shared by a server or group of key holders) + +### Inputs + +- `T`: The plaintext message to be encrypted. +- `Y₁`: A public key used for threshold encryption. + +--- + +## Encryption Steps +### 1. Generate Secrets + +As a first step, two cryptographic secrets are generated: + +- A **random AES key `m`**: Used for symmetric encryption of the message. +- A **random number `r`**: Used as part of the threshold encryption input to ensure semantic security. + + + +### 2. Encrypt the Plaintext with AES + +The message `T` is concatenated with the random number `r`, forming `T || r`. This is done so that, after decrypting the original key `m` through Threshold Decryption, and decrypting also `T` using `m`, we can get `r`, and use it to validate the decryption - i.e, **validate the decrypted data against the threshold encrypted key**. + +This combined input is encrypted using AES in GCM mode: + +``` +C = GCM_m(T || r) +``` + + +Where: + +- `m` is the randomly generated AES key +- `GCMₘ` is the AES-GCM encryption function keyed with `m` +- `C` is the resulting ciphertext, which includes authentication data + +This step is performed for efficiency, as symmetric encryption is significantly faster than asymmetric alternatives. + + +### 3. Encrypt the AES Key with Threshold Encryption + +The AES key `m` is encrypted using threshold encryption with public key `Y₁` and random number `r`: + +``` +K_1 = TE(m, r, Y_1) +``` + + +Where: + +- `TE` is the threshold encryption function ([explained here](./1-threshold-encryption.md)) +- `Y₁` is the network's TE common public key +- `K₁` is the encrypted AES key (also referred to as a `CipheredKey`) + +Threshold encryption ensures that decryption will require access to the corresponding secret share(s), depending on the system’s threshold configuration. + + +### 4. Construct the Final Ciphertext + +The final ciphertext consists of: + +- The encrypted AES key `K₁` +- The AES-encrypted message `C` + +This is serialized as: + +``` +Ciphertext = [Keys Size] || K₁ || C +``` + +The diagram reflects this with the final output structure shown on the right-hand side. + +#### Example Field Sizes (for 1 Key) + +| Field | Size (bytes) | +|-------------|------------------| +| Keys Size | 1 | +| K₁ | 224 | +| C | Varies | + +`Keys Size` specifies the number of keys appended. Currently we support only either `1` or `2` keys. The size of `K₁` is always fixed at 224 bytes. The size of `C` depends on the length of the plaintext and the AES-GCM overhead. + + +# 1.2 Full Encryption Flow (Two Keys) + +The flow for 2 keys is mostly the same as when using a single key. The only difference is that we now also threshold encrypt the second key, and append it to the ciphertext. + +Meaning that we reuse the same AESKey `m` as well as the random secret `r` for both Threshold Encryptions, and only change the public common key input to each of the processes. + +This will still result in different `CipheredKey` struct outputs, which will then be concatenated, and be part of the final `Ciphertext`. + +The following diagram shows the entire process using 2 keys, and also depicts the TE process in the bottom half, for ease of reference: + + +Diagram + +
+ +--- + +# 2. Decryption + +Next we explain the decryption process without delving into math details. If you need to know the details [read this](./1-threshold-encryption.md). +In the threshold decryption process, each node uses its private key share to compute a **partial decryption** of the ciphertext and broadcasts this decryption share to the network. Once a supermajority (i.e., at least the threshold number of valid shares) is received, any node can combine these shares to reconstruct the original plaintext, without needing the full private key. + +Since only the `CipheredKey` structure was ciphered using threshold encryption, only this needs to be merged using threshold encryption semantics. +The **result of such merging will not be the original plaintext, but the deciphered `AESKey`** + +The process is shown below: + +Diagram +
+
+ +1. Each node uses its `PrivateKeyShare_i` to partial decipher the `CipheredKey`, resulting in a `DecryptShare_i` that is broadcasted to all other nodes. +2. Each node awaits for **t** decrypt shares, and merges them, resulting in the original `AESKey` used to cipher the `Ciphertext` + +The **merge** process itself is detailed [here](./1-threshold-encryption.md). + +Having seen the threshold encryption-decryption process to get the `AESKey`, we see next how we can use this to get the original plaintext `T` from the `Ciphertext` struct: + + +
+Diagram +
+
+ +1. First we merge the shares against the `CipheredKey` to get the original `AESKey` (shown in bottom half of the diagram) +2. Given the `AESKey`, we run the `GCM` keyed with `m` to decipher the encrypted plaintext - the `C` field from the `Ciphertext` struct. +3. After step 2., we get `T || r`, and we extract `T` from it + +Although the `r` field is not used during decryption, it is essential for validating the result: nodes use it to confirm the correctness of the recovered `AESKey`. Details of this validation process are described in the **validation section**. + +--- + +# 3. Validation + +This section explains the validation checkpoints exposed by libBLS at a high level. Mathematical details are explain [here](./1-threshold-encryption.md) + +## Overview + +Our threshold encryption system implements a validation framework that ensures cryptographic integrity at every stage of the encryption and decryption process. The validation stages are strategically positioned to catch errors and malicious behavior before they can propagate through the system. + +
+Diagram +
+
+ +The validation stages are positioned at critical transition points - usually after a communication round: + +- **After Encryption**: Validates that the received ciphertext was properly formed before starting the decryption process. +- **After Share Generation**: Ensures each decryption share is authentic upon receival, thus, before merging. +- **After Share Merging**: Verifies the merged result before final decryption. +- **After Final Decryption**: Confirms end-to-end integrity of the entire process. + +This creates a defense-in-depth strategy where each validation stage guards against different types of failures or attacks. By validating at these specific points, we prevent corrupted or malicious data from advancing to subsequent stages where it could otherwise have delayed the system into detecting the corrupted data only at the final stage. + +## Validation Stages + +### Stage 1: Ciphertext Validation + +**Purpose**: Ensures that encrypted keys are mathematically well-formed and haven't been tampered with. + +This validation occurs immediately after encryption and before any ciphertext used to compute a `DecryptShare`. It uses elliptic curve pairing operations to verify that the three components of the ciphertext (U, V, W) maintain the correct cryptographic relationship. + +**Why it's critical**: Malformed or tampered ciphertexts could allow attackers to extract information about the underlying plaintext or disrupt the decryption process. By catching these issues early, we prevent malformed ciphertexts to proceed. + +### Stage 2: Decryption Share Validation + +**Purpose**: Verifies that each participant's decryption share is authentic and corresponds to their authorized key fragment. + +This validation happens after each participant computes their decryption share but before shares are combined. It ensures that only authorized parties can contribute valid shares to the decryption process. + +The validation checks that the decryption share was computed correctly using the participant's private key fragment and that it actually corresponds to the specific ciphertext being decrypted. Each share includes cryptographic proof that it was generated by someone possessing the correct key material. + +**Why it's critical**: In threshold cryptography, the security depends on collecting shares only from legitimate participants. Accepting invalid shares could allow attackers to disrupt decryption or potentially compromise the threshold security model. This validation maintains the integrity of the participant authentication system. + +### Stage 3: Combined Decryption Validation + +**Purpose**: Ensures the share combination process worked correctly and produced a valid result. + +This validation occurs after multiple decryption shares have been combined to reconstruct the AES encryption key. It performs two complementary checks: first validating that AES decryption succeeds, then verifying that the decrypted content matches cryptographic expectations. + +**Why it's critical**: Even if individual shares are valid, errors can occur during the combination process. Mathematical errors, software bugs, or subtle tampering could result in an incorrect reconstruction. This validation ensures that the combined result is actually usable and secure. + +### Stage 4: Deciphered Message Validation + +**Purpose**: Provides end-to-end verification that the entire cryptographic process maintained integrity. + +This is the most comprehensive validation, occurring after the complete decryption process. It reconstructs part of the original encryption process using information embedded in the decrypted message and verifies that this reconstruction produces consistent results. + +The validation extracts the random secret `r` that was embedded during the original encryption, uses this secret to recompute how the AES key should have been encrypted, and verifies that this matches the actual encrypted key. This creates a cryptographic "round trip" that can only succeed if every component of the system worked correctly. + +**Why it's critical**: This validation provides the highest level of assurance that the decrypted result is authentic and hasn't been compromised at any stage. This is the final **guarantee that the plaintext you receive is exactly what was originally encrypted**. + +## Security Benefits + +The layered validation approach provides several key security benefits: + +**Early Error Detection**: Problems are caught as soon as they occur, preventing them from cascading through the system and becoming harder to diagnose or causing more severe failures. + +**Attack Surface Reduction**: Each validation stage closes off different attack vectors, making it extremely difficult for malicious actors to compromise the system without detection. + +**System Reliability**: The validations catch not just malicious behavior but also honest errors, hardware failures, and software bugs, making the entire system more robust and reliable. + +**Auditability**: Each validation stage provides a clear checkpoint where system integrity can be verified, making it easier to audit the system's behavior and troubleshoot any issues that arise. + diff --git a/docs/concepts/threshold-encryption/3-full-client-flow.md b/docs/concepts/threshold-encryption/3-full-client-flow.md new file mode 100644 index 00000000..ddf0ee5e --- /dev/null +++ b/docs/concepts/threshold-encryption/3-full-client-flow.md @@ -0,0 +1,161 @@ +# Threshold Encryption – Full Encryption & Decryption Flow + +This document describes the complete flow of the **Threshold Encryption (TE)** process, from encryption to decryption. It also covers scenarios involving **key rotation**, where a ciphertext may be encrypted using two public keys. The purpose is to enable secure handover between two node committees and ensure continuity of decryption capabilities during transitions. + +This document does not cover the details of encryption / decryption. These can be seen in [threshold-encryption.md](./1-threshold-encryption.md) and [te-full-flow.md](./2-te-full-flow.md). + +--- + +The following diagram shows the flow process, as well as some details on the ciphertext structure. The steps are all explained below the diagram. + +Diagram + +
+ +Next we describe each of the steps in order. + +## 1. Encryption (Client Side) + +### 1.1. Retrieve Public Keys + +The client begins by querying the network to obtain the current threshold encryption public keys: + +\( getCommonPubKeys → [Key1, Key2*] \) + +- `Key1`: The current active public key. +- `Key2` (optional): A second public key, used during committee rotation. It represents the new key under transition. + +This dual-key mechanism allows the client to encrypt data such that it is decryptable by either the current or the upcoming committee. This is essential during periods when both committees must temporarily coexist. + +Note that this method is not provided by libBLS. It should be implemented by the application using libBLS. libBLS only deals with the two keys sent to the library. + +### 1.2. Encrypt Data + +From this step on, eveything is implemented by libBLS. + +The client generates: +- A **symmetric key** `m` (AES key). +- A **random scalar** `r` used as input to the threshold encryption scheme. + +Both the above fields are generated once, and shared for the two threshold encryptions as explained in [te-full-flow.md](./2-te-full-flow.md) + +The symmetric key `m` is then encrypted independently with each of the provided public keys: + +```text +U, V, W = EncryptTE(Key1, m, r) +U', V', W' = EncryptTE(Key2, m, r) +``` + + +The full ciphertext is then constructed as: + +``` +CipherText = { + Keys Size: 1 or 2, + K1: { U, V, W }, + K2: { U', V', W'} [optional], + C: AES-Encrypt( m, plaintext || r ) +} +``` + + +The ciphertext `C` is produced by encrypting the plaintext and appending `r`, to enable later validation. + +This full ciphertext is sent to the network, together with any required metadata: + +``` +Client → Network: metadata || CipherText +``` +This metadata is used to decide which key should be used, in case there are 2. If there is only one key, then no metadata is needed. + +--- + +## 2. CipherText Filtering (Network Side) + +Upon receiving the ciphertext, the network must select the appropriate key to use: + +- The metadata is used to determine whether `K1` or `K2` is the active key. +- The **unused key** is **discarded** from the ciphertext to avoid ambiguity. + +Resulting structure: + +``` +CipherText = { + Keys Size: 1, + K_selected: {U, V, W}, + C: AES-encrypted message +} +``` + + +Only one key block (`K1` or `K2`) remains at this stage, depending on which committee is responsible for decrypting the data. + +--- + +## 3. Collect Decryption Shares (Network side) + +At this stage, the decryption is started. Each node first computes one decryption share and shares with all other nodes. +**Diagram 2** below shows a broader view of the encryption-decryption process. + +Diagram + +At this stage, we have already gone through the `Ciphering` part ( **m** being the message, and $Y_1$ being the group's public key) on the left of the diagram, and we are exactly at the middle. + + +### 3.1. Compute Decryption Shares + +Each node will wait to receive the `CipheredKey` as shown in the diagram above, and: +- Extracts the encrypted key fields `(U, V, W)` from the selected key block. +- Builds a value from the `U` field of the key, that will be decrypted, and produces a decryption share, as shown in Diagram 1. +- Uses its private key share to produce a **Decryption Share** from the `U` value of the selected ciphered key: + +``` +Share_i = DecryptShare(U, V, W, sk_i) +``` + + +Each share is broadcasted to all other nodes as shown in Diagram 2. + +## 4. Combine Shares + +Once a threshold number of valid decryption shares is collected: + +``` +Shares = {Share1, Share2, ..., Share_t} +``` + + +They are combined to reconstruct the original symmetric key `m`: + +``` +m = MergeShares(Shares) +``` + + +## 5. Decrypt Ciphertext + +Each node can now decrypt the ciphertext `C` using the reconstructed `m`: + +``` +plaintext || r = AES-Decrypt(m, C) +``` +The output is the original plaintext message, and the appended value `r`. This `r` value can then be used to validate that the recovered plaintext corresponds to the threshold-ciphered key + +--- + + +## Summary + +| Step | Description | +|------|-------------| +| 1 | Client encrypts data using one or two public keys. | +| 2 | Nodes select the correct key and removes the unused one. | +| 3 | Nodes compute and broadcast decryption shares. | +| 4 | Shares are combined to recover the AES key. | +| 5 | AES key is used to decrypt the ciphertext. | +| 6 | `r` is used to validate the recovered plaintext \(T\) corresponds to the ciphered key \(K_n\) | + +This mechanism ensures both **confidentiality** and **robustness**, while supporting **committee rotation** with backward compatibility during key transitions. + + + diff --git a/docs/optimizations.md b/docs/concepts/threshold-encryption/4-optimizations.md similarity index 100% rename from docs/optimizations.md rename to docs/concepts/threshold-encryption/4-optimizations.md diff --git a/docs/diagrams/dkg/dkg-global-poly.svg b/docs/diagrams/dkg/dkg-global-poly.svg new file mode 100644 index 00000000..0e9de5d3 --- /dev/null +++ b/docs/diagrams/dkg/dkg-global-poly.svg @@ -0,0 +1,4 @@ + + + +
=
+
+
...
+
Global polynomial
Common Private Key
Node 1
Node 2
Node n
\ No newline at end of file diff --git a/docs/diagrams/dkg/dkg-step1-pub-commitment-vec.svg b/docs/diagrams/dkg/dkg-step1-pub-commitment-vec.svg new file mode 100644 index 00000000..0e496529 --- /dev/null +++ b/docs/diagrams/dkg/dkg-step1-pub-commitment-vec.svg @@ -0,0 +1,4 @@ + + + +
 
\ No newline at end of file diff --git a/docs/diagrams/dkg/dkg-step1.svg b/docs/diagrams/dkg/dkg-step1.svg new file mode 100644 index 00000000..9270cd99 --- /dev/null +++ b/docs/diagrams/dkg/dkg-step1.svg @@ -0,0 +1,4 @@ + + + +
0
1
2
3
\ No newline at end of file diff --git a/docs/diagrams/dkg/dkg-step2-end.svg b/docs/diagrams/dkg/dkg-step2-end.svg new file mode 100644 index 00000000..203a4e53 --- /dev/null +++ b/docs/diagrams/dkg/dkg-step2-end.svg @@ -0,0 +1,4 @@ + + + +
0
1
2
3
Already generated by this node
This is node index. For node 2, this would be 2
Public shares of all other nodes
Private share from all other nodes to our specific node - 3 in this case
\ No newline at end of file diff --git a/docs/diagrams/dkg/dkg-step2-private.svg b/docs/diagrams/dkg/dkg-step2-private.svg new file mode 100644 index 00000000..14b69d83 --- /dev/null +++ b/docs/diagrams/dkg/dkg-step2-private.svg @@ -0,0 +1,4 @@ + + + +
1
0
3
2
Each node generates a private shares vec of size from its polynomial
n
\ No newline at end of file diff --git a/docs/diagrams/dkg/dkg-step2-public.svg b/docs/diagrams/dkg/dkg-step2-public.svg new file mode 100644 index 00000000..62a844f4 --- /dev/null +++ b/docs/diagrams/dkg/dkg-step2-public.svg @@ -0,0 +1,4 @@ + + + +
1
0
3
2
Each node generates a public shares vec of size from its polynomial
t
=
\ No newline at end of file diff --git a/docs/diagrams/dkg/dkg-step3.svg b/docs/diagrams/dkg/dkg-step3.svg new file mode 100644 index 00000000..8e38c55c --- /dev/null +++ b/docs/diagrams/dkg/dkg-step3.svg @@ -0,0 +1,4 @@ + + + +
0
1
2
3
Verified against
Must be equal
+
(
)
+
(
)
...
(
)
+
\ No newline at end of file diff --git a/docs/diagrams/dkg/dkg-step4-final-keys.svg b/docs/diagrams/dkg/dkg-step4-final-keys.svg new file mode 100644 index 00000000..c2d09d34 --- /dev/null +++ b/docs/diagrams/dkg/dkg-step4-final-keys.svg @@ -0,0 +1,4 @@ + + + +
0
1
2
3
=
+
+
+
=
=
=
+
+
+
\ No newline at end of file diff --git a/docs/diagrams/dkg/dkg-step4.svg b/docs/diagrams/dkg/dkg-step4.svg new file mode 100644 index 00000000..8adf5468 --- /dev/null +++ b/docs/diagrams/dkg/dkg-step4.svg @@ -0,0 +1,4 @@ + + + +
0
1
2
3
=
+
+
+
=
=
=
\ No newline at end of file diff --git a/docs/diagrams/dkg/global-poly.svg b/docs/diagrams/dkg/global-poly.svg new file mode 100644 index 00000000..bcb147b6 --- /dev/null +++ b/docs/diagrams/dkg/global-poly.svg @@ -0,0 +1,4 @@ + + + +

\ No newline at end of file diff --git a/docs/diagrams/threshold-encryption/TE-Merging.svg b/docs/diagrams/threshold-encryption/TE-Merging.svg new file mode 100644 index 00000000..ccc9b256 --- /dev/null +++ b/docs/diagrams/threshold-encryption/TE-Merging.svg @@ -0,0 +1,4 @@ + + + +
Lagrange Coefficients
Compute Lagrange Coefficient :
DecryptionShares
D0
0
D2
2
D5
5
D6
6
D1
1
DecryptionShare point
Signer index
Recover Plaintext
Plaintext m
Inputs
Output
Compute :
Decipher the message :
G(r*Y⊕ V 
CipheredKey
U
V
W
G2 point
Bytes
G1 point
Type
Size (bytes)
128
32
64
Fields
\ No newline at end of file diff --git a/docs/diagrams/threshold-encryption/decrypt-share-computation.svg b/docs/diagrams/threshold-encryption/decrypt-share-computation.svg new file mode 100644 index 00000000..e1cc9bc0 --- /dev/null +++ b/docs/diagrams/threshold-encryption/decrypt-share-computation.svg @@ -0,0 +1,4 @@ + + + +
Ciphertext
U
V
W
G2 point
Bytes
G1 point
Type
Size (bytes)
128
32
64
Fields
PartialDecrypt
Compute
D = si * U 
Private key share si
Inputs
D
Fields
Size (bytes)
Type
64
G1 point
DecryptShare
Output
\ No newline at end of file diff --git a/docs/diagrams/threshold-encryption/full-client-flow.svg b/docs/diagrams/threshold-encryption/full-client-flow.svg new file mode 100644 index 00000000..3d072177 --- /dev/null +++ b/docs/diagrams/threshold-encryption/full-client-flow.svg @@ -0,0 +1,4 @@ + + + +
Client
Network
getCommonPubKeys
Key1
Key2*
1) Encrypt
* Key2 is optional. Network may send only 1 key
metadata || CipherText
2) Select the correct key
K1
*K2
Remove unused key
C
Keys Size
CipherText
1
224
Size (bytes)
224
Varying size
Fields
3) Collect Decryption Shares
C
Keys Size
CipherText
1
Size (bytes)
224
Varying size
Fields
K2
K2
U
V
W
4) Merge 
shares
Share1
Share2
Sharen
Reconstructed m
U
V
W
5) Decrypt
DecryptShare
Share1
C
m
Decrypt
Plaintext
\ No newline at end of file diff --git a/docs/diagrams/threshold-encryption/lagrange-1.svg b/docs/diagrams/threshold-encryption/lagrange-1.svg new file mode 100644 index 00000000..bb88bea6 --- /dev/null +++ b/docs/diagrams/threshold-encryption/lagrange-1.svg @@ -0,0 +1,4 @@ + + + +

 
\ No newline at end of file diff --git a/docs/diagrams/threshold-encryption/lagrange-2.svg b/docs/diagrams/threshold-encryption/lagrange-2.svg new file mode 100644 index 00000000..f498986d --- /dev/null +++ b/docs/diagrams/threshold-encryption/lagrange-2.svg @@ -0,0 +1,4 @@ + + + +
\ No newline at end of file diff --git a/docs/diagrams/threshold-encryption/lagrange-3.svg b/docs/diagrams/threshold-encryption/lagrange-3.svg new file mode 100644 index 00000000..1a62e923 --- /dev/null +++ b/docs/diagrams/threshold-encryption/lagrange-3.svg @@ -0,0 +1,4 @@ + + + +

\ No newline at end of file diff --git a/docs/diagrams/threshold-encryption/lagrange-4.svg b/docs/diagrams/threshold-encryption/lagrange-4.svg new file mode 100644 index 00000000..e9af0293 --- /dev/null +++ b/docs/diagrams/threshold-encryption/lagrange-4.svg @@ -0,0 +1,4 @@ + + + +

\ No newline at end of file diff --git a/docs/diagrams/threshold-encryption/merge-shares.svg b/docs/diagrams/threshold-encryption/merge-shares.svg new file mode 100644 index 00000000..f672241c --- /dev/null +++ b/docs/diagrams/threshold-encryption/merge-shares.svg @@ -0,0 +1,4 @@ + + + +
Lagrange Coefficients
Compute numerator: Multiply all signer indices 
N = ∏ idx[i]
Compute denominator 
for each index i:
D[i] = idx[i] * ∏ (idx[j] - idx[j])
Compute Lagrange Coefficient i:
L[i] = N / D[i]
DecryptionShares
DS1
0
DS2
2
DS5
5
DS6
6
DS1
1
Compute element i
M[i] = L[i] * DS[i]
Sum all
SM[i]
DecryptionShare point
Signer index
Reconstruct original plaintext
m = H(S)
Recover Plaintext
Plaintext m
Input
Output
\ No newline at end of file diff --git a/docs/diagrams/threshold-encryption/simplified-process-overview.svg b/docs/diagrams/threshold-encryption/simplified-process-overview.svg new file mode 100644 index 00000000..d6337084 --- /dev/null +++ b/docs/diagrams/threshold-encryption/simplified-process-overview.svg @@ -0,0 +1,4 @@ + + + +
m
Y1
CipheredKey
+
PrivShare0
PrivShare1
PrivSharen
+
+
+
PartialDecrypt0
PartialDecrypt1
PartialDecryptn
m
Merge
Node i
All nodes
Node i
Ciphering
Deciphering
Broadcast
Broadcast
\ No newline at end of file diff --git a/docs/diagrams/threshold-encryption/te-decrypt-without-validation.svg b/docs/diagrams/threshold-encryption/te-decrypt-without-validation.svg new file mode 100644 index 00000000..2be9dc10 --- /dev/null +++ b/docs/diagrams/threshold-encryption/te-decrypt-without-validation.svg @@ -0,0 +1,4 @@ + + + +
Merge(S, K)
Ciphertext C
CipheredKey K
Decrypt Ciphertext
T || r
GCMm(C)
Inputs
Decrypt
DecryptShares S
AESKey m
plaintext T
K1
C
Keys Size
CipherText
1
224
Size (bytes)
Varying size
Fields
\ No newline at end of file diff --git a/docs/diagrams/threshold-encryption/te-decryption-full.svg b/docs/diagrams/threshold-encryption/te-decryption-full.svg new file mode 100644 index 00000000..4df7b032 --- /dev/null +++ b/docs/diagrams/threshold-encryption/te-decryption-full.svg @@ -0,0 +1,4 @@ + + + +
CipheredKey
PrivKeySharei
+
DecryptSharei
Node i
1) Compute Decryption Share
Broadcast
DecryptShare0
Node i
DecryptShare1
DecryptSharet
Network
Merge
...
AESKey
2) Merge received shares
\ No newline at end of file diff --git a/docs/diagrams/threshold-encryption/te-decryption.svg b/docs/diagrams/threshold-encryption/te-decryption.svg new file mode 100644 index 00000000..4d471a2f --- /dev/null +++ b/docs/diagrams/threshold-encryption/te-decryption.svg @@ -0,0 +1,4 @@ + + + +
Ciphertext
PrivKeySharei
+
DecryptSharei
Node i
1) Compute Decryption Share
Broadcast
DecryptShare0
Node i
DecryptShare1
DecryptSharet
Network
Merge
...
m
2) Merge received shares
m
Y1
+
r
Ciphering
Deciphering
\ No newline at end of file diff --git a/docs/diagrams/threshold-encryption/te-full-flow-full.svg b/docs/diagrams/threshold-encryption/te-full-flow-full.svg new file mode 100644 index 00000000..ba3838e3 --- /dev/null +++ b/docs/diagrams/threshold-encryption/te-full-flow-full.svg @@ -0,0 +1,4 @@ + + + +
K1
*K2
C
Keys Size
CipheredKey
CipherText
Plaintext T
Generate random AESKey m
Encrypt AESKeys
Random number r
Compute 
U = r * P
Compute 
V = G(r * Y) ⊕ m
Compute 
W = * H(U, V)
Public Key
Y = x*P
U
V
W
G2 point
Bytes
G1 point
1
224
Type
Size (bytes)
128
32
64
Size (bytes)
224
Varying size
Fields
Fields
* Key2 is optional, and may be omitted
Input message m
Threshold Encryption (TE)
Optional
Inputs
Output
TE(m, r, Y1)
Generate random number r
CipheredKey K2
Pub Common Key Y1
Pub Common Key Y2
TE(m, r, Y2)
Encrypt Plaintext
T || r
GCMm(T || r)
Ciphertext C
K1 || K2 || C
CipheredKey K1
Encryption with pub key Y2 may be omitted
Inputs
Output
Build Ciphertext
\ No newline at end of file diff --git a/docs/diagrams/threshold-encryption/te-full-flow.svg b/docs/diagrams/threshold-encryption/te-full-flow.svg new file mode 100644 index 00000000..7d89e149 --- /dev/null +++ b/docs/diagrams/threshold-encryption/te-full-flow.svg @@ -0,0 +1,4 @@ + + + +
K1
C
Keys Size
CipherText
Plaintext T
Generate random AESKey m
Encrypt AESKeys
1
224
Size (bytes)
Varying size
Fields
TE(m, r, Y1)
Generate random number r
Pub Common Key Y1
Encrypt Plaintext
T || r
GCMm(T || r)
Ciphertext C
K1 || C
CipheredKey K1
Inputs
Output
Build Ciphertext
\ No newline at end of file diff --git a/docs/diagrams/threshold-encryption/te-validation.svg b/docs/diagrams/threshold-encryption/te-validation.svg new file mode 100644 index 00000000..0c4aebcf --- /dev/null +++ b/docs/diagrams/threshold-encryption/te-validation.svg @@ -0,0 +1,4 @@ + + + +
Encrypt
Merge
Decrypt
Validate Ciphertext
Validate Share
Validate Decryption
Ciphertext
Compute DecryptShare
DecryptShare
Plaintext
Round of communication
Validate Merge
\ No newline at end of file diff --git a/docs/diagrams/threshold-encryption/threshold-encryption.svg b/docs/diagrams/threshold-encryption/threshold-encryption.svg new file mode 100644 index 00000000..551a0091 --- /dev/null +++ b/docs/diagrams/threshold-encryption/threshold-encryption.svg @@ -0,0 +1,4 @@ + + + +
CipheredKey
Random number r
Compute 
U = r * P
Compute 
V = G(r * Y) ⊕ m
Compute 
W = * H(U, V)
Public Key
Y = x*P
U
V
W
G2 point
Bytes
G1 point
Type
Size (bytes)
128
32
64
Fields
Input message m
Threshold Encryption (TE)
Inputs
Output
\ No newline at end of file diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 00000000..95c379e4 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,35 @@ +# Threshold Encryption Documentation + +Welcome to the documentation for libBLS + +This repository is organized into two main sections: + +--- + +## [`./usage`](./usage/usage-index.md) + +This folder contains **practical code examples** demonstrating how to use the library. +It includes: + +- Step-by-step usage patterns +- Encryption and decryption API calls +- Sample input/output + +Refer to this section if you are looking for concrete implementation details. + +--- + +## [`./concepts`](./concepts/concepts-index.md) + +This folder provides both a **theoretical explanation** of how threshold encryption works as well as **system architecture and workflows**. +It covers: + +- Cryptographic foundations +- Protocol design and flow diagrams +- Security and validation mechanisms + +Refer to this section if you want to understand the system at a conceptual or architectural level. + +--- + +For detailed information, explore the folders above. diff --git a/docs/backends.md b/docs/usage/backends.md similarity index 100% rename from docs/backends.md rename to docs/usage/backends.md diff --git a/docs/testing.md b/docs/usage/testing.md similarity index 100% rename from docs/testing.md rename to docs/usage/testing.md diff --git a/docs/README.md b/docs/usage/usage-index.md similarity index 63% rename from docs/README.md rename to docs/usage/usage-index.md index 15562e8b..3b6cba34 100644 --- a/docs/README.md +++ b/docs/usage/usage-index.md @@ -1,6 +1,7 @@ -# libBLS Documentation +# Usage Documentation - [Using Threshold Signatures](using-threshold-signatures.md) - [Using Threshold Encryption](using-threshold-encryption.md) - [Using Distributed Key Generation](using-distributed-key-generation.md) -- [Running Tests](testing.md) +- [Running Tests, Benchmarks & ASan](testing.md) +- [Elliptic Curve Backends](./backends.md) \ No newline at end of file diff --git a/docs/using-distributed-key-generation.md b/docs/usage/using-distributed-key-generation.md similarity index 100% rename from docs/using-distributed-key-generation.md rename to docs/usage/using-distributed-key-generation.md diff --git a/docs/using-threshold-encryption.md b/docs/usage/using-threshold-encryption.md similarity index 100% rename from docs/using-threshold-encryption.md rename to docs/usage/using-threshold-encryption.md diff --git a/docs/using-threshold-signatures.md b/docs/usage/using-threshold-signatures.md similarity index 100% rename from docs/using-threshold-signatures.md rename to docs/usage/using-threshold-signatures.md