├── .gitignore
├── CHANGELOG.md
├── Cargo.toml
├── LICENSE
├── README.md
├── benches
└── protocols.rs
├── docs
├── images
│ ├── dark
│ │ └── dependencies.svg
│ └── light
│ │ └── dependencies.svg
├── intro.md
├── key-generation.md
├── orchestration.md
├── proofs.md
├── signing.md
└── triples.md
├── examples
└── network-benches.rs
├── logo.png
└── src
├── compat.rs
├── constants.rs
├── crypto.rs
├── keyshare.rs
├── lib.rs
├── math.rs
├── participants.rs
├── presign.rs
├── proofs
├── dlog.rs
├── dlogeq.rs
└── mod.rs
├── protocol
├── internal.rs
└── mod.rs
├── serde.rs
├── sign.rs
├── test.rs
└── triples
├── batch_random_ot.rs
├── bits.rs
├── correlated_ot_extension.rs
├── generation.rs
├── mod.rs
├── mta.rs
├── multiplication.rs
└── random_ot_extension.rs
/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | /Cargo.lock
3 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # 0.8.0
2 |
3 | - Added an extra requirement to Curve API for constant-time scalar sampling
4 |
5 | # 0.7.0
6 |
7 | - Remove triple setup interface, always doing a fresh setup, for security reasons.
8 | - Fix various security bugs.
9 | - Update dependencies.
10 |
11 | # 0.6.0
12 |
13 | - Modify specification to use a single threshold (turns out the code accidentally enforced this already)
14 | - Modify code to match simplified presigning protocol because of this threshold.
15 | - Modify specification to pre-commit to C polynomial in triple generation.
16 | - Modify code accordingly.
17 |
18 | # 0.5.0
19 |
20 | - Modify specification & implementation to use perfectly hiding commitments.
21 | - Update dependencies to recent Rust-Crypto ECDSA versions.
22 | - Support arbitrary curves and message hashes.
23 | - Add curve description (name) to transcript in keysharing and triple generation.
24 |
25 | # 0.4.0
26 |
27 | - Added key refresh and resharing protocols.
28 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "cait-sith"
3 | description = "Threshold ECDSA via Triples"
4 | repository = "https://github.com/cronokirby/cait-sith"
5 | version = "0.8.0"
6 | edition = "2021"
7 | license = "MIT"
8 |
9 | [dependencies]
10 | auto_ops = "0.3.0"
11 | ck-meow = "0.1.0"
12 | digest = "0.10.7"
13 | ecdsa = { version = "0.16.8", features = ["digest", "hazmat"] }
14 | elliptic-curve = { version = "0.13.5", features = ["serde"] }
15 | event-listener = "2.5.3"
16 | k256 = { version = "0.13.1", features = ["sha256", "ecdsa", "serde"], optional = true }
17 | magikitten = "0.2.0"
18 | rand_core = { version = "0.6.4", features = ["getrandom"] }
19 | rmp-serde = "1.1.2"
20 | serde = { version = "1.0.175", features = ["derive"] }
21 | smol = "1.3.0"
22 | subtle = "2.5.0"
23 |
24 | [dev-dependencies]
25 | criterion = "0.4"
26 | easy-parallel = "3.2.0"
27 | haisou-chan = { git = "https://github.com/cronokirby/haisou-chan", rev = "d28c46e51acfcb818236caae293f6e56dff41ad2" }
28 | structopt = "0.3.26"
29 | k256 = { version = "0.13.0", features = ["sha256", "ecdsa", "serde"], optional = false }
30 |
31 | [[bench]]
32 | name = "protocols"
33 | harness = false
34 | required-features = ["k256"]
35 |
36 | [features]
37 | k256 = ["dep:k256"]
38 |
39 | [[example]]
40 | name = "network-benches"
41 | path = "examples/network-benches.rs"
42 | required-features = ["k256"]
43 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2023 Lúcás C. Meier
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Cait-Sith [](https://crates.io/crates/cait-sith) [](https://docs.rs/cait-sith)
2 |
3 | Cait-Sith is a novel threshold ECDSA protocol (and implementation),
4 | which is both simpler and substantially more performant than
5 | popular alternatives.
6 |
7 | The protocol supports arbitrary numbers of parties and thresholds.
8 |
9 |
13 |
14 | # Warning
15 |
16 | This is experimental cryptographic software, unless you're a cat with
17 | a megaphone on top of a giant Moogle I would exercise caution.
18 |
19 | - The protocol does not have a formal proof of security.
20 | - This library has not undergone any form of audit.
21 |
22 | # Design
23 |
24 | The main design principle of Cait-Sith is offloading as much work
25 | to a key-independent preprocessing phase as possible.
26 | The advantage of this approach is that this preprocessing phase can be conducted
27 | in advance, before a signature is needed, and the results of this phase
28 | can even be peformed before the key that you need to sign with is decided.
29 |
30 | One potential scenario where this is useful is when running a threshold
31 | custody service over many keys, where these preprocessing results
32 | can be performed, and then used on demand regardless of which keys
33 | end up being used more often.
34 |
35 | A detailed specification is available [in this repo](./docs),
36 | but we'll also give a bit of detail here.
37 |
38 | The core of Cait-Sith's design involves a *committed* Beaver triple.
39 | These are of the form:
40 |
41 | $$
42 | ([a], [b], [c]), (A = a \cdot G, B = b \cdot G, C = c \cdot G)
43 | $$
44 |
45 | where $a, b, c$ are scalars such that $a \cdot b = c$, and are
46 | secret shared among several participants, so that no one knows their actual value.
47 | Furthermore, unlike standard Beaver triples, we also have a public commitment
48 | to the these secret values, which helps the online protocol.
49 |
50 | The flow of the protocol is first that the parties need a way to generate triples:
51 |
52 | - A setup protocol is run once, allowing parties to efficiently generate triples.
53 | - The parties can now generate an arbitrary number triples through a distributed protocol.
54 |
55 | Then, the parties need to generate a key pair so that they can sign messages:
56 |
57 | - The parties run a distributed key generation protocol to setup a new key pair,
58 | which can be used for many signatures.
59 |
60 | When the parties want to sign using a given key:
61 |
62 | - Using their shares of a private key, the parties can create a *presignature*,
63 | before knowing the message to sign.
64 | - Once they know this message, they can use the presignature to create a complete signature.
65 |
66 | It's important that presignatures and triples are **never** reused.
67 |
68 | ### Refresh and Resharing
69 |
70 | In addition to key generation, cait-sith also supports key *refresh*,
71 | and key *resharing*.
72 |
73 | Key refresh generates new shares for each party, while keeping the same list
74 | of participants and threshold.
75 |
76 | Key resharing does the same, but also allows changing the threshold,
77 | and the list of participants (as long as enough old participants are present
78 | to meet the old threshold).
79 |
80 | ## API Design
81 |
82 | Internally, the API tries to be as simple as possible abstracting away
83 | as many details as possible into a simple interface.
84 |
85 | This interface just has two methods:
86 | ```rust
87 | pub trait Protocol {
88 | type Output;
89 |
90 | fn poke(&mut self) -> Result, ProtocolError>;
91 | fn message(&mut self, from: Participant, data: MessageData);
92 | }
93 | ```
94 | Given an instance of this trait, which represents a single party
95 | participating in a protocol, you can do two things:
96 | - You can provide a new message received from some other party.
97 | - You can "poke" the protocol to see if it has some kind of action it wants you to perform, or if an error happened.
98 |
99 | This action is either:
100 | - The protocol telling you it has finished, with a return value of type `Output`.
101 | - The protocol asking you to send a message to all other parties.
102 | - The protocol asking you to *privately* send a message to one party.
103 | - The protocol informing you that no more progress can be made until it receives new messages.
104 |
105 | In particular, details about rounds and message serialization are abstracted
106 | away, and all performed internally.
107 | In fact, the protocols aren't designed around "rounds", and can even have parallel
108 | threads of execution internally for some of the more complicated ones.
109 |
110 | # Benchmarks
111 |
112 | Here are some benchmarks, for the `Secp256k1` curve, performed on an Intel Core i5-4690K CPU.
113 |
114 | ```
115 | > cargo bench -F k256
116 |
117 | setup 3
118 | time: [94.689 ms 95.057 ms 95.449 ms]
119 |
120 | triple generation (3, 3)
121 | time: [36.610 ms 36.682 ms 36.757 ms]
122 |
123 | keygen (3,3)
124 | time: [3.0901 ms 3.1095 ms 3.1297 ms]
125 |
126 | presign (3,3)
127 | time: [2.5531 ms 2.5640 ms 2.5761 ms]
128 |
129 | sign (3,3)
130 | time: [446.79 µs 447.89 µs 449.02 µs]
131 | ```
132 |
133 | These were performed with 3 parties running on the same machine,
134 | with no communication cost.
135 |
136 | Note that triple generation needs to be performed *twice* for each signature.
137 | Also, triple generation is relatively bandwidth intensive compared to other
138 | protocols, which isn't reflected in these benchmarks, since network speed
139 | isn't constrained.
140 | Nonetheless, this cost isn't all that important, because it can be performed
141 | in advance, and independent of the key.
142 |
143 | Thus, the cost of presigning + signing should be considered instead.
144 | This cost is low enough to be bottlenecked by network performance, most likely.
145 |
146 | ## Networked Benchmarks
147 |
148 | The library also has an example which runs a benchmark simulating
149 | network latency and bandwidth constraints.
150 | Note that in these examples, multiple threads are used, so better
151 | reflect the fact that computation is parallelized across each node.
152 | However, the CPU I ran these benchmarks on only had 4 cores,
153 | so take the large party benchmarks with a grain of salt.
154 |
155 | Here's an example with 3 parties, with 100ms latency between them,
156 | and a 10 MB/s outgoing link each.
157 |
158 | ```
159 | > cargo run --release -F k256 --example network-benches -- 3 100 10000000
160 |
161 | Triple Setup 3 [100 ms, 10000000 B/S]
162 | time: 304.884093ms
163 | up: 10322 B
164 | down: 10322 B
165 |
166 | Triple Gen 3 [100 ms, 10000000 B/S]
167 | time: 740.041888ms
168 | up: 106202 B
169 | down: 106202 B
170 |
171 | Keygen (3, 3) [100 ms, 10000000 B/S]
172 | time: 207.137969ms
173 | up: 1068 B
174 | down: 1068 B
175 |
176 | Presign (3, 3) [100 ms, 10000000 B/S]
177 | time: 104.090877ms
178 | up: 961 B
179 | down: 961 B
180 |
181 | Sign (3, 3) [100 ms, 10000000 B/S]
182 | time: 100.606562ms
183 | up: 151 B
184 | down: 151 B
185 | ```
186 |
187 | Here's an extreme case, with 100 parties, 300ms of latency between them,
188 | and an outgoing 1 MB/s link each:
189 |
190 | ```
191 | > cargo run --release --example network-benches -- 100 300 1000000
192 |
193 | Triple Setup 100 [300 ms, 1000000 B/S]
194 | time: 51.269278194s
195 | up: 510843 B
196 | down: 510843 B
197 |
198 | Triple Gen 100 [300 ms, 1000000 B/S]
199 | time: 32.959644915s
200 | up: 6765025 B
201 | down: 6765025 B
202 |
203 | Keygen (100, 100) [300 ms, 1000000 B/S]
204 | time: 5.871460998s
205 | up: 551527 B
206 | down: 551527 B
207 |
208 | Presign (100, 100) [300 ms, 1000000 B/S]
209 | time: 2.891458487s
210 | up: 546835 B
211 | down: 546835 B
212 |
213 | Sign (100, 100) [300 ms, 1000000 B/S]
214 | time: 359.795393ms
215 | up: 7859 B
216 | down: 7859 B
217 | ```
218 |
219 | # Generic Curves
220 |
221 | The library has support for generic curves and hashes.
222 |
223 | The support for generic curves is done through a custom `CSCurve` trait,
224 | which can be easily implemented for any curve from the
225 | RustCrypto [elliptic-curves](https://github.com/RustCrypto/elliptic-curves)
226 | suite of libraries.
227 |
228 | This crate also provides implementations of some existing curves behind features,
229 | as per the following table:
230 |
231 | | Curve | Feature |
232 | |-------|---------|
233 | |Secp256k1|`k256`|
234 |
235 | For supporting any message hash, the API requires the user to supply
236 | the hash of a message when signing as a scalar directly.
237 |
238 | # Shortcomings
239 |
240 | The protocol and its implementation do have a few known disadvantages at the moment:
241 |
242 | - The protocol does require generating triples in advance, but these can be generated without knowledge of the private key.
243 | - The protocol does not attempt to provide identifiable aborts.
244 |
245 | We also don't really intend to add identifiable aborts to Cait-Sith itself.
246 | While these can be desirable in certain situations, we aren't satisfied
247 | with the way the property of identifiable aborts is modeled currently,
248 | and are working on improvements to this model.
249 |
--------------------------------------------------------------------------------
/benches/protocols.rs:
--------------------------------------------------------------------------------
1 | use std::vec::Vec;
2 |
3 | use cait_sith::{
4 | keygen, presign,
5 | protocol::{run_protocol, Participant, Protocol},
6 | sign,
7 | triples::{
8 | self, generate_triple, setup, Setup, TripleGenerationOutput, TriplePub, TripleShare,
9 | },
10 | FullSignature, KeygenOutput, PresignArguments, PresignOutput,
11 | };
12 | use criterion::{black_box, criterion_group, criterion_main, Criterion};
13 | use k256::{AffinePoint, Secp256k1, Scalar};
14 | use rand_core::OsRng;
15 |
16 | fn run_setup(participants: Vec) -> Vec<(Participant, Setup)> {
17 | let mut protocols: Vec<(Participant, Box>)> =
18 | Vec::with_capacity(participants.len());
19 |
20 | for p in participants.iter() {
21 | let protocol = setup::(&participants, *p);
22 | assert!(protocol.is_ok());
23 | let protocol = protocol.unwrap();
24 | protocols.push((*p, Box::new(protocol)));
25 | }
26 |
27 | run_protocol(protocols).unwrap()
28 | }
29 |
30 | fn run_triple_generation(
31 | participants: Vec<(Participant, Setup)>,
32 | threshold: usize,
33 | ) -> Vec<(Participant, TripleGenerationOutput)> {
34 | let mut protocols: Vec<(
35 | Participant,
36 | Box>>,
37 | )> = Vec::with_capacity(participants.len());
38 |
39 | let just_participants: Vec<_> = participants.iter().map(|(p, _)| *p).collect();
40 |
41 | for (p, setup) in participants.into_iter() {
42 | let protocol = generate_triple(&just_participants, p, setup, threshold);
43 | assert!(protocol.is_ok());
44 | let protocol = protocol.unwrap();
45 | protocols.push((p, Box::new(protocol)));
46 | }
47 |
48 | run_protocol(protocols).unwrap()
49 | }
50 |
51 | fn run_keygen(
52 | participants: Vec,
53 | threshold: usize,
54 | ) -> Vec<(Participant, KeygenOutput)> {
55 | let mut protocols: Vec<(Participant, Box>>)> =
56 | Vec::with_capacity(participants.len());
57 |
58 | for p in participants.iter() {
59 | let protocol = keygen(&participants, *p, threshold);
60 | assert!(protocol.is_ok());
61 | let protocol = protocol.unwrap();
62 | protocols.push((*p, Box::new(protocol)));
63 | }
64 |
65 | run_protocol(protocols).unwrap()
66 | }
67 |
68 | fn run_presign(
69 | participants: Vec<(Participant, KeygenOutput)>,
70 | shares0: Vec>,
71 | shares1: Vec>,
72 | pub0: &TriplePub,
73 | pub1: &TriplePub,
74 | threshold: usize,
75 | ) -> Vec<(Participant, PresignOutput)> {
76 | assert!(participants.len() == shares0.len());
77 | assert!(participants.len() == shares1.len());
78 |
79 | let mut protocols: Vec<(Participant, Box>>)> =
80 | Vec::with_capacity(participants.len());
81 |
82 | let participant_list: Vec = participants.iter().map(|(p, _)| *p).collect();
83 |
84 | for (((p, keygen_out), share0), share1) in participants
85 | .into_iter()
86 | .zip(shares0.into_iter())
87 | .zip(shares1.into_iter())
88 | {
89 | let protocol = presign(
90 | &participant_list,
91 | p,
92 | PresignArguments {
93 | original_threshold: threshold,
94 | triple0: (share0, pub0.clone()),
95 | triple1: (share1, pub1.clone()),
96 | keygen_out,
97 | threshold,
98 | },
99 | );
100 | assert!(protocol.is_ok());
101 | let protocol = protocol.unwrap();
102 | protocols.push((p, Box::new(protocol)));
103 | }
104 |
105 | run_protocol(protocols).unwrap()
106 | }
107 |
108 | fn run_sign(
109 | participants: Vec<(Participant, PresignOutput)>,
110 | public_key: AffinePoint,
111 | msg: Scalar,
112 | ) -> Vec<(Participant, FullSignature)> {
113 | let mut protocols: Vec<(Participant, Box>>)> =
114 | Vec::with_capacity(participants.len());
115 |
116 | let participant_list: Vec = participants.iter().map(|(p, _)| *p).collect();
117 |
118 | for (p, presign_out) in participants.into_iter() {
119 | let protocol = sign(&participant_list, p, public_key, presign_out, msg);
120 | assert!(protocol.is_ok());
121 | let protocol = protocol.unwrap();
122 | protocols.push((p, Box::new(protocol)));
123 | }
124 |
125 | run_protocol(protocols).unwrap()
126 | }
127 |
128 | pub fn criterion_benchmark(c: &mut Criterion) {
129 | let participants = vec![
130 | Participant::from(0u32),
131 | Participant::from(1u32),
132 | Participant::from(2u32),
133 | ];
134 | let t = 3;
135 |
136 | c.bench_function("setup 3", |b| b.iter(|| run_setup(participants.clone())));
137 |
138 | let mut setup_result = run_setup(participants.clone());
139 | setup_result.sort_by_key(|(p, _)| *p);
140 |
141 | c.bench_function("triple generation (3, 3)", |b| {
142 | b.iter(|| run_triple_generation(black_box(setup_result.clone()), t))
143 | });
144 |
145 | c.bench_function("keygen (3,3)", |b| {
146 | b.iter(|| run_keygen(black_box(participants.clone()), black_box(t)))
147 | });
148 |
149 | let mut keygen_result = run_keygen(participants.clone(), t);
150 | keygen_result.sort_by_key(|(p, _)| *p);
151 |
152 | let public_key = keygen_result[0].1.public_key;
153 |
154 | let (pub0, shares0) = triples::deal(&mut OsRng, &participants, t);
155 | let (pub1, shares1) = triples::deal(&mut OsRng, &participants, t);
156 |
157 | c.bench_function("presign (3,3)", |b| {
158 | b.iter(|| {
159 | run_presign(
160 | black_box(keygen_result.clone()),
161 | black_box(shares0.clone()),
162 | black_box(shares1.clone()),
163 | black_box(&pub0),
164 | black_box(&pub1),
165 | black_box(t),
166 | )
167 | })
168 | });
169 |
170 | let mut presign_result = run_presign(keygen_result, shares0, shares1, &pub0, &pub1, t);
171 | presign_result.sort_by_key(|(p, _)| *p);
172 |
173 | // DO NOT COPY THIS CODE FOR ACTUAL SIGNING
174 | let msg = Scalar::ONE;
175 |
176 | c.bench_function("sign (3,3)", |b| {
177 | b.iter(|| {
178 | run_sign(
179 | black_box(presign_result.clone()),
180 | black_box(public_key),
181 | black_box(msg),
182 | )
183 | })
184 | });
185 | }
186 |
187 | criterion_group!(benches, criterion_benchmark);
188 | criterion_main!(benches);
189 |
--------------------------------------------------------------------------------
/docs/images/dark/dependencies.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
258 |
--------------------------------------------------------------------------------
/docs/images/light/dependencies.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
258 |
--------------------------------------------------------------------------------
/docs/intro.md:
--------------------------------------------------------------------------------
1 | These docs provide a lower level description of the protocols used in Cait-Sith.
2 |
3 | # Overview
4 |
5 | ## [Orchestration](./orchestration.md)
6 |
7 | Describes how the different protocols fit together.
8 |
9 | ## [Proofs](./proofs.md)
10 |
11 | Describes the conventions around transcripts and ZK Proofs.
12 |
13 | ## [Key Generation](./key-generation.md)
14 |
15 | This describes the distributed key generation protocol.
16 |
17 | ## [Triples](./triples.md)
18 |
19 | This describes the triple generation protocol: a pre-processing phase
20 | used to speed up signing.
21 |
22 | ## [Signing](./signing.md)
23 |
24 | This describes the signing protocol, which consists of a presignature phase
25 | and a final signature phase.
26 |
27 | # Security Analysis
28 |
29 | A security analysis of the protocol is available [here](https://cronokirby.com/notes/2023/04/cait-sith-security/).
30 |
31 | # Some Notation conventions
32 |
33 | Vectors / Matrices are denoted $x_i$ or $A_{ij}$, using indices. Operators behave pointwise. For example, $x_i \cdot y_i$ creates a new vector by multiplying the entries of $x$ and $y$ pointwise. $\langle A_{ij}, \ldots \rangle$ denotes summation, over the shared indices. For example $\langle A_{ij}, x_j \rangle_i$ would be $A x$ in more conventional matrix vector multiplication notation.
34 |
35 | $\lambda(\mathcal{P})_i$ denotes the Lagrange coefficient for participant $i$
36 | in a group of participants $\mathcal{P}$, used for interpolating threshold
37 | shared values into linear shared values.
38 |
--------------------------------------------------------------------------------
/docs/key-generation.md:
--------------------------------------------------------------------------------
1 | In this document, we describe a generalized version of key generation,
2 | allowing for key refresh and resharing, and then apply that to
3 | create two specific protocols:
4 | 1. A protocol for generating a fresh key.
5 | 2. A protocol for changing the threshold and set of participants but with the same key.
6 |
7 |
8 | Given a set of players $\mathcal{P} = \{P_1, \ldots, P_N\}$,
9 | and a desired threshold $t$, define
10 | the following protocol:
11 |
12 | ## KeyShare
13 |
14 | We assume that each participant in $\mathcal{P} := \\\{P_1, \ldots, P_n\\\}$
15 | has a secret share $s_i$ (which can possibly be $0$), which sum to $s := \sum_i s_i$ and that they share a public value $S$,
16 | which is either $\bot$ (meaning no value),
17 | or should equal $s \cdot G$.
18 |
19 | The goal of this protocol is for each particapant to obtain a fresh
20 | threshold $t$ sharing of $s$, such that any $t$ participants
21 | can reconstruct the value, along with the value $s \cdot G$,
22 | if $S = \bot$.
23 |
24 | **Round 1:**
25 |
26 | 1. $\blacktriangle$ Each $P_i$ *asserts* that $|\mathcal{P}| \geq t$.
27 | 2. $T.\text{Add}(\mathbb{G}, \mathcal{P}, t)$
28 | 3. Each $P_i$ samples $f \xleftarrow{\\\$} \mathbb{F}_ q[X]_ {\leq t - 1}$,
29 | subject to the constraint that $f(0) = s_i$.
30 | 4. Each $P_i$ sets $F_ i \gets f \cdot G$.
31 | 5. Each $P_i$ sets $(\text{Com}_i, r_i) \gets \text{Commit}(F_i)$.
32 | 6. $\star$ Each $P_i$ sends $\text{Com}_i$ to every other party.
33 |
34 | **Round 2:**
35 |
36 | 1. $\bullet$ Each $P_i$ waits to receive $\text{Com}_j$ from each other $P_j$.
37 | 2. Each $P_i$ sets $\text{Confirm}_i \gets H(\text{Com}_1, \ldots, \text{Com}_N)$.
38 | 3. $T.\text{Add}(\text{Confirm}_i)$
39 | 4. $\star$ Each $P_i$ sends $\text{Confirm}_i$ to every other party.
40 | 5. Each $P_i$ generates the proof $\pi_i \gets \text{Prove}(T.\text{Cloned}(\texttt{dlog0}, i), \text{Mau}(- \cdot G, F_{i}(0); f(0)))$.
41 | 6. $\star$ Each $P_i$ sends $(F_i, r_i, \pi_i)$ to every other party.
42 | 7. $\textcolor{red}{\star}$ Each $P_i$ *privately* sends $x_i^j := f(j)$ to each other party $P_j$, and saves $x_i^i$ for itself.
43 |
44 | **Round 3:**
45 |
46 | 1. $\bullet$ Each $P_i$ waits to receive $\text{Confirm}_j$ from each other $P_j$.
47 | 2. $\blacktriangle$ Each $P_i$ *asserts* that $\forall j \in [N].\ \text{Confirm}_j = \text{Confirm}_i$, aborting otherwise.
48 | 3. $\bullet$ Each $P_i$ waits to receive $(F_j, r_j, \pi_j)$ from each other $P_j$.
49 | 4. $\blacktriangle$ Each $P_i$ *asserts* that $\forall j \in [N].\ \text{deg}(F_ j) = t -1 \land \text{CheckCommit}(\text{Com}_j, F_j, r_j) \land \text{Verify}(T.\text{Cloned}(\texttt{dlog0}, j), \pi_j, \text{Mau}({- \cdot G}, F_j(0)))$.
50 | 5. $\bullet$ Each $P_i$ waits to receive $x_j^i$ from each other $P_j$.
51 | 6. Each $P_i$ sets $x_i \gets \sum_j x^i_j$ and $X \gets \sum_j F_j(0)$.
52 | 7. $\blacktriangle$ Each $P_i$ asserts that $x_i \cdot G = (\sum_j F_j)(i)$.
53 | 8. (If $S \neq \bot$) $\blacktriangle$ Each $P_i$ asserts that $X = S$.
54 | 9. Each $P_i$ outputs $x_i$ and $X$.
55 |
56 | **Output**
57 |
58 | The value $x_i$ is $P_i$'s private share of the secret key $s$.
59 |
60 | $X$ is the public key shared by the group, which should be equal
61 | to the previous value $S$, if it was provided.
62 |
63 | ## Key Generation
64 |
65 | The key sharing protocol can be used for a standard key generation
66 | protocol, by having each party sample $s_i$ randomly,
67 | and setting $S = \bot$ (no expected public key).
68 |
69 | ## Key Refresh
70 |
71 | A key refresh protocol can be performed by first linearizing the
72 | shares $x_1, \ldots, x_n$,
73 | setting $s_i \gets \lambda(\mathcal{P})_i \cdot x_i$,
74 | and then using $S = X$, to check that the public key doesn't
75 | change.
76 |
77 | ## Key Resharing
78 |
79 | A key resharing protocol can be performed as well.
80 | This involves transitioning from $(\mathcal{P}, t)$
81 | to $(\mathcal{P}', t')$, and can be performed as long
82 | as $|\mathcal{P} \cap \mathcal{P}'| \geq t$,
83 | i.e. there are enough old parties with a share.
84 | The end result is that the new set of parties
85 | hold threshold $t'$ shares of the same private key.
86 |
87 | This works by having each party in $\mathcal{P} \cap \mathcal{P}'$ linearize their share,
88 | setting $s_i \gets \lambda(\mathcal{P})_i \cdot x_i$.
89 | Each party in $\mathcal{P}' / \mathcal{P}$ (the new members),
90 | simply set $s_i \gets 0$.
91 | We also set $S = X$, to check that the same public key
92 | is generated.
93 |
94 | Key refresh can be seen as a natural case of
95 | key resharing, with $\mathcal{P} = \mathcal{P}'$,
96 | and $t = t'$.
97 |
--------------------------------------------------------------------------------
/docs/orchestration.md:
--------------------------------------------------------------------------------
1 | Cait-Sith is composed of multiple protocols.
2 | For example, key generation is a separate protocol from signing.
3 | Some protocols are broken into sub-protocols.
4 | For example, signing is broken into three phases, the first can be
5 | done without any secret information, the second with just the shares of the
6 | secret key, and the final one after learning the message to sign.
7 |
8 | This document describes the high-level orchestration of the protocols.
9 |
10 | # Key Generation
11 |
12 | During key generation, a set of parties $\mathcal{P}$ of size $N$ come together
13 | to produce shares of a secret key.
14 | These shares are such that any set $\mathcal{S} \subseteq \mathcal{P}$
15 | of size $\geq t$ can reconstruct the secret.
16 | We call $t$ our threshold.
17 |
18 | # Signing
19 |
20 | Signing is split into three phases:
21 |
22 | 1. Triple generation.
23 | 2. Presigning.
24 | 3. Signing.
25 |
26 | Each of these phases can potentially use a different set of parties,
27 | and these sets can have different sizes.
28 | Furthermore, each phase can have a different threshold describing
29 | how large the party set for the next phase needs to be.
30 |
31 | Concretely, we have the following situation:
32 |
33 | $$
34 | \begin{matrix}
35 | &\scriptsize{\text{Key-Gen}}
36 | &&\scriptsize{\text{Triples}}
37 | &&\scriptsize{\text{Presigning}}
38 | &&\scriptsize{\text{Signing}}
39 | \cr
40 | &\mathcal{P} &\supseteq &\mathcal{P}_0 &\supseteq &\mathcal{P}_1 &\supseteq &\mathcal{P}_2\cr
41 | &N &\geq &N_0 &\geq &N_1 & \geq &N_2\cr
42 | &t &&t_0 &&t_1 &&t_2\cr
43 | \end{matrix}
44 | $$
45 |
46 | Each phase has a different set of parties, with each subsequent phase
47 | having a subset of the parties present in the previous one.
48 | The size of each party set, $N_i$, can also vary.
49 | The thresholds can also change, subject to the following conditions:
50 |
51 | $$
52 | \begin{aligned}
53 | &N_0 \geq t\cr
54 | &N_1 \geq t_0 \geq t\cr
55 | &N_2 \geq t_1 \geq t
56 | \end{aligned}
57 | $$
58 |
59 | In other words, the first threshold $t$ is at most $N$, and every
60 | other threshold must be at least that large.
61 | Otherwise, the only constraint is that each subsequent party set must
62 | be at least as large as the previous threshold.
63 |
64 | ## Discarding information
65 |
66 | Each phase can be run many times in advance, recording the information
67 | public information produced, as well as the list of parties which produced it.
68 | Then, this output is consumed by having a set of parties use it
69 | for a subsequent phase.
70 | It's **critical** that the output is then destroyed, so that no other
71 | group of parties attempts to re-use that output for another phase.
72 | In particular, the parties need some way of agreeing on which
73 | outputs have been created and used.
74 | If the threshold $t_i$ is such that $N_{i} \leq 2t - 1$, then it's impossible
75 | to have two non-overlapping quorums, so if each party locally registers the
76 | fact that an output has been used, then agreement can be had not to
77 | use a certain output.
78 | Otherwise, you might have two independent groups of parties trying
79 | to use the same output, which is bad.
80 |
81 | # Graph
82 |
83 | Here's a figure describing the dependencies between the different phases:
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 | The red boxes mean that the output of that phase can only be used once.
92 |
--------------------------------------------------------------------------------
/docs/proofs.md:
--------------------------------------------------------------------------------
1 | Various protocols use ZK proofs using the Fiat-Shamir
2 | These proofs need to incorporate as much contextual information as necessary to avoid potential replay attacks.
3 | To accomplish this, we use a "transcript" abstraction, which allows absorbing information before then using it in a proof.
4 |
5 | # Transcript API
6 |
7 | - $T.\text{Add}(x_1, x_2, \ldots)$ absorbs new data $x_1, x_2, \ldots$, handling separation and padding.
8 | - $T.\text{Cloned}(\text{tag}, x_1, \ldots)$ produces a forked version of the transcript, by using a given tag, and additional data. This transcript will not modify the original transcript $T$, but will contain the information absorbed in it.
9 |
10 | You can also think of a transcript as essentially containing a complete
11 | list of all the operations performed on it.
12 | So adding $x$ and then $y$ is equivalent to having a transcript consisting
13 | of $[x, y]$.
14 | All this information will then be used when the transcript is passed
15 | to create or verify a proof.
16 |
17 | This transcript API is closely related to the implementation
18 | used in this library: [Magikitten](https://github.com/cronokirby/magikitten).
19 | Looking at the API of that library will likely make this
20 | API more understandable.
21 |
22 | # ZK Proofs
23 |
24 | The proofs we use in this library are all Maurer proofs.
25 | These are proofs of the form:
26 | "I know a secret $x$ such that $\varphi(x) = X$, with $X$ a public value",
27 | and $\varphi$ being a homomorphism, i.e. $\varphi(a + b) = \varphi(a) + \varphi(b)$.
28 |
29 | A common case of this is the Schnorr discrete logarithm proof,
30 | with $\varphi(x) = x \cdot G$.
31 | We would write this as $- \cdot G$ in the notation of our specifications.
32 |
33 | In general, we write $\text{Mau}(\varphi, X; x)$
34 | to denote the relation "I know a private $x$ such that $\varphi(x) = X$.
35 | We also write $\text{Mau}(\varphi, X)$ to denote the verifier's
36 | view of this relation, where $x$ is not known.
37 |
38 | Using this notation, we write:
39 | - $$
40 | \text{Prove}(T, \text{Mau}(\varphi, X; x))
41 | $$
42 | - $$
43 | \text{Verify}(T, \pi, \text{Mau}(\varphi, X))
44 | $$
45 | for creating and verifying a proof, using a transcript for binding
46 | proofs to a given context.
47 |
48 | See [this blog post](https://cronokirby.com/posts/2022/08/the-paper-that-keeps-showing-up/) for more context on Maurer proofs.
49 |
--------------------------------------------------------------------------------
/docs/signing.md:
--------------------------------------------------------------------------------
1 | This document specifies the signing protocol.
2 | The protocol is split into two main phases, a pre-signing phase
3 |
4 | # 1 Preliminaries
5 |
6 | Let $\mathbb{G}$ be a cryptographic group, with generator $G$, of prime order $q$.
7 |
8 | Let $\text{Hash} : \\{0, 1\\}^* \to \mathbb{F}_q$ denote a hash function used for hashing messages
9 | for signatures.
10 | Let $h : \mathbb{G} \to \mathbb{F}_q$ denote a different "hash function" used for converting points to scalars.
11 | Commonly, this is done by "simply" taking the x coordinate of the affine
12 | representation of a point.
13 | Let $H : \\{0, 1\\}^* \to \\{0, 1\\}^{2\lambda}$ be a generic hash function.
14 |
15 | # 2 ECDSA Recap
16 |
17 | ECDSA is defined by algorithms for key generation, signing, and verification:
18 |
19 | First, key generation:
20 |
21 | $$
22 | \begin{aligned}
23 | &\underline{\texttt{Gen}():}\cr
24 | &\ x \xleftarrow{\$} \mathbb{F}_q\cr
25 | &\ X \gets x \cdot G\cr
26 | &\ \texttt{return } (x, X)\cr
27 | \end{aligned}
28 | $$
29 |
30 | Next, signing:
31 |
32 | $$
33 | \begin{aligned}
34 | &\underline{\texttt{Sign}(x : \mathbb{F}_q, m : \{0, 1\}^*):}\cr
35 | &\ k \xleftarrow{\$} \mathbb{F}_q\cr
36 | &\ R \gets \frac{1}{k} \cdot G\cr
37 | &\ r \gets h(R)\cr
38 | &\ \texttt{retry if } r = 0\cr
39 | &\ s \gets k (\texttt{Hash}(m) + rx)\cr
40 | &\ \texttt{return } (R, s)
41 | \end{aligned}
42 | $$
43 |
44 | Note that we deviate slightly from ECDSA specifications by returning
45 | the entire point $R$ instead of just $r$.
46 | This makes it easier for downstream implementations to massage
47 | the result signature into whatever format they need for compatability.
48 |
49 | Finally, verification:
50 |
51 | $$
52 | \begin{aligned}
53 | &\underline{\texttt{Verify}(X : \mathbb{G}, m : \{0, 1\}^*, (R, s) : \mathbb{G} \times \mathbb{F}_q):}\cr
54 | &\ r \gets h(R)\cr
55 | &\ \texttt{assert } r \neq 0, s \neq 0\cr
56 | &\ \hat{R} \gets \frac{\texttt{Hash}(m)}{s} \cdot G + \frac{r}{s} \cdot X\cr
57 | &\ \texttt{asssert } \hat{R} = R\cr
58 | \end{aligned}
59 | $$
60 |
61 | # 3 Presigning
62 |
63 | In the setup phase, the parties generated a $t$ threshold sharing
64 | of the private key $x$, with the share of $\mathcal{P}_i$ being $x_i$.
65 | The parties also hold the public key $X = x \cdot G$.
66 |
67 | In two prior phases $\sigma \in \\{0, 1\\}$, a set of parties $\mathcal{P}_0^\sigma$ of size $N_0^\sigma$
68 | came together to generate a $t$ threshold sharing of triples $a^\sigma$, $b^\sigma$, $c^\sigma = a^\sigma b^\sigma$
69 | along with values $A^\sigma = a^\sigma \cdot G$, $B^\sigma = b^\sigma \cdot G$ and $C^\sigma = c^\sigma \cdot G$.
70 |
71 | In the current phase, a set of parties $\mathcal{P}_ 1 \subseteq \mathcal{P}_ 0^0 \cap \mathcal{P}^1_ 0$
72 | of size $N_1 \geq t$ wish to generate a threshold $t' = t$ sharing
73 | of a pre-signature.
74 |
75 | **Round 1:**
76 |
77 | 1. Each $P_i$ checks that $\mathcal{P}_1 \subseteq \mathcal{P}_0^0 \cap \mathcal{P}_0^1$, and that $t' = t$.
78 | 2. Each $P_i$ renames:
79 |
80 | $$
81 | \begin{aligned}
82 | &k_i \gets a^0_i, &d_i \gets b^0_i,\quad &\text{kd}_i \gets c^0_i\cr
83 | &K \gets A^0, &D \gets B^0,\quad &\text{KD} \gets C^0\cr
84 | &a \gets a^1_i, &b \gets b^1_i,\quad &c \gets c^1_i\cr
85 | &A \gets A^1, &B \gets B^1,\quad &C \gets C^1\cr
86 | \end{aligned}
87 | $$
88 |
89 | 3. Then, each $P_i$ linearizes their shares, setting:
90 |
91 | $$
92 | \begin{aligned}
93 | (k'_i, d_i, \text{kd}_i) &\gets \lambda(\mathcal{P}_1)_i \cdot (k_i, d_i, \text{kd}_i)\cr
94 | (a'_i, b'_i, c'_i) &\gets \lambda(\mathcal{P}_1)_i \cdot (a_i, b_i, c_i)\cr
95 | x'_i &\gets \lambda(\mathcal{P}_1)_i \cdot x_i\cr
96 | \end{aligned}
97 | $$
98 |
99 | 4. $\star$ Each $P_i$ sends $\text{kd}_i$ to every other party.
100 | 5. Each $P_i$ sets:
101 |
102 | $$
103 | \begin{aligned}
104 | &\text{ka}_i \gets k'_i + a'_i\cr
105 | &\text{xb}_i \gets x'_i + b'_i\cr
106 | \end{aligned}
107 | $$
108 |
109 | 6. $\star$ Each $P_i$ sends $\text{ka}_i$ and $\text{xb}_i$ to every other party.
110 |
111 | **Round 2:**
112 |
113 | 1. $\bullet$ Each $P_i$ waits to receive $\text{kd}_j$ from each other $P_j$.
114 | 2. Each $P_i$ sets $\text{kd} \gets \sum_j \text{kd}_j$.
115 | 3. $\blacktriangle$ Each $P_i$ *asserts* that $\text{kd} \cdot G = \text{KD}$.
116 | 4. $\bullet$ Each $P_i$ waits to receive $\text{ka}_j$ and $\text{xb}_j$ from from every other party $P_j$.
117 | 5. Each $P_i$ sets $\text{ka} \gets \sum_j \text{ka}_j$ and $\text{xb} \gets \sum_j \text{xb}_j$.
118 | 6. $\blacktriangle$ Each $P_i$ asserts that:
119 |
120 | $$
121 | \begin{aligned}
122 | \text{ka} \cdot G &= K + A\cr
123 | \text{xb} \cdot G &= X + B
124 | \end{aligned}
125 | $$
126 |
127 | 7. Each $P_i$ sets: $R \gets \frac{1}{\text{kd}} \cdot D$.
128 | 8. Each $P_i$ sets $\sigma_i \gets \text{ka} \cdot x_i - \text{xb} \cdot a_i + c_i$, which is already threshold shared.
129 |
130 | **Output:**
131 | The output is the presignature $(R, k, \sigma)$, with $k$ and $\sigma$
132 | threshold shared as $k_1, \ldots$ and $\sigma_1, \ldots$.
133 |
134 | # 4 Signing
135 |
136 | In the previous phase, a group of parties $\mathcal{P}_1$
137 | generate a presignature $(R, k, \sigma)$, with the values
138 | $k$, $\sigma$ being shared with a threshold of $t$.
139 |
140 | In the signing phase, a group of parties $\mathcal{P}_2 \subseteq \mathcal{P}_1$ of size $\geq t$ consumes this presignature
141 | to sign a message $m$.
142 |
143 | **Round 1:**
144 |
145 | 1. Each $P_i$ linearizes their share of $k$, setting $k_i \gets \lambda(\mathcal{P}_2)_i \cdot k_i$.
146 | 2. Each $P_i$ linearizes their share of $\sigma$, setting $\sigma_i \gets \lambda(\mathcal{P}_2)_i \cdot \sigma_i$.
147 | 3. Each $P_i$ sets $s_i \gets \text{Hash}(M) \cdot k_i + h(R) \sigma_i$.
148 | 4. $\star$ Each $P_i$ sends $s_i$ to every other party.
149 |
150 | **Round 2:**
151 |
152 | 1. $\bullet$ Each $P_i$ waits to receive $s_j$ from every other party.
153 | 2. Each $P_i$ sets $s \gets \sum_{j \in [N]} s_j$.
154 | 3. $\blacktriangle$ Each $P_i$ *asserts* that $(R, s)$ is a valid ECDSA signature for $m$.
155 | 4. Each $P_i$ outputs $(R, s)$.
156 |
157 | **Output**
158 |
159 | The pair $(R, s)$ is the signature.
160 |
161 |
--------------------------------------------------------------------------------
/docs/triples.md:
--------------------------------------------------------------------------------
1 | This document specifies the protocol for triple generation.
2 | Note that while the other specifications and parts of the protocol are intended
3 | to be strictly followed, this specification is less opinionated about generating
4 | triples.
5 |
6 | As long as valid triples are generated according to proces, they
7 | can be used in the subsequent presigning phase.
8 | The presigning phase is agnostic as to how these triples have been generated.
9 |
10 | This document only gives a very concrete suggestion as to how this might be implemented,
11 | and describes how this crate implements triple generation without a trusted
12 | dealer.
13 |
14 | Compared to the other parts of the protocol, triple generation is more complex,
15 | in that it involves the composition of several layers of protocols.
16 | We describe each of these from the bottom up.
17 |
18 | # Random Oblivious Transfer
19 |
20 | The first protocol we make use of is *random oblivious transfer*.
21 | This is a two-party protocol, involving a sender $\mathcal{S}$, and a receiver $\mathcal{R}$.
22 | The receiver has as input a bit $b$.
23 |
24 | The output of the protocol if a trusted dealer existed would be:
25 |
26 | $$
27 | \begin{aligned}
28 | &k_ 0, k_ 1 \xleftarrow{\$} \\\{0, 1\\\}^\lambda\cr
29 | &\mathcal{R} \texttt{ gets } k_ b\cr
30 | &\mathcal{S} \texttt{ gets } k_ 0, k_ 1\cr
31 | \end{aligned}
32 | $$
33 |
34 | The seed length $\lambda$ is the ambient security parameter.
35 | (e.g. 128 bits, in the usual case)
36 |
37 | In particular, we consider a *batched* version of this functionality,
38 | in which the receiver has $l$ bits $b_ 1, \ldots, b_ l$, and both parties.
39 | Receive the output $l$ times.
40 |
41 | We can also consider the result as being two matrices $K_{ij}^0$ and $K_{ij}^1$,
42 | with $i \in [l]$, $j \in [\lambda]$,
43 | with the receiver getting the matrix $K_ {ij}^{b_ i}$.
44 |
45 | ## "Simplest" OT Protocol
46 |
47 | For batch random OT, we use the so-called "simplest" OT Protocol
48 | [[CO15]](https://eprint.iacr.org/2015/267).
49 |
50 | Procotol `Batch-Random-OT`:
51 |
52 | 1. $\mathcal{S}$ samples $y \xleftarrow{R} \mathbb{F}_q$, and sets $Y \gets y \cdot G$, and $Z \gets y \cdot Y$.
53 | 2. $\star$ $\mathcal{S}$ sends $Y$ to $\mathcal{R}$.
54 | 3. $\bullet$ $\mathcal{R}$ waits to receive $Y$.
55 |
56 | In parallel, for $i \in [l]$:
57 |
58 | 4. $\mathcal{R}$ samples $x_i \xleftarrow{R} \mathbb{F}_q$, and computes $X_i \gets b_i \cdot Y + x_i \cdot G$.
59 | 5. $\mathcal{R}$ computes $K_{ij}^{b_ i} \gets H_{(i, Y, X_ i)}(x_ i \cdot Y)$
60 | 6. $\star$ $\mathcal{R}$ sends $X_i$ to $\mathcal{S}$.
61 | 7. $\bullet$ $\mathcal{S}$ waits to receive $X_i$.
62 | 8. $\mathcal{S}$ computes $K_{ij}^b \gets H_{(i, Y, X_ i)}(y \cdot X_ i - b \cdot Z)_j$.
63 |
64 | Here $H$ is a hash function, parameterized by an integer $i$, as well
65 | as two points, providing a key derivation function $\mathbb{G} \to \mathbb{F}_2^{\lambda}$.
66 |
67 | # Setup Phase
68 |
69 | The goal of the setup phase is for each unordered pair of parties $P_a$
70 | and $P_b$ to run a $\lambda$ batched random OT.
71 | Each pair will be run only once, so we need to agree on a canonical
72 | way to determine which of two parties $P_a$ and $P_b$
73 | will act as the sender.
74 | We do this by imposing a total order $<$ on the parties, and $P_a$
75 | is the sender in the $(P_a, P_b)$ pair, if $P_a < P_b$.
76 |
77 | The end result is that $P_a$ will learn $K_{ij}^0$ and $K_{ij}^1$,
78 | and $P_b$ will learn $K_{ij}^{\Delta_ i}$, for a randomly chosen $\Delta \in \mathbb{F}_2^{\lambda}$.
79 |
80 | In more detail:
81 |
82 | Protocol `Triples-Setup`
83 |
84 | In parallel, for each unordered pair of parties $P_a$ and $P_b$
85 | $P_b$ samples $\Delta \xleftarrow{R} \mathbb{F}_2^\lambda$,
86 | and then $P_a$ and $P_b$ run `Batch-Random-OT` with a batch size
87 | of $\lambda$, and save the result.
88 | Note that communication in this subprotocol should be *private*.
89 |
90 | # Extended Oblivious Transfer
91 |
92 | The goal of the extended oblivious transfer protocol is for two parties
93 | to extend their joint setup, and use that setup to generate $\kappa$ oblivious
94 | transfers, using fast symmetric key primitives.
95 |
96 | We use the [KOS15](https://eprint.iacr.org/2015/546),
97 | specifically, the amended version using SoftspokenOT.
98 |
99 | ## Correlated OT Extension
100 |
101 | We start with the *correlated* extension protocol.
102 |
103 | The correlation comes from the $\Delta$ value used in the setup, controlled
104 | by the sender $\mathcal{S}$.
105 | Note that the sender was the receiver in the original setup.
106 | In this protocol $\mathcal{R}$ uses an input matrix $X_{ij}$, and learns a random boolean matrix
107 | $T_{ij} \in \mathbb{F}_ 2$, $i \in [\kappa], j \in [\lambda]$,
108 | and $\mathcal{S}$ learns $Q_{ij} = T_{ij} + X_{ij} \cdot \Delta_ j$
109 |
110 | Protocol `Correlated-OT-Extension`:
111 |
112 | $\mathcal{R}$ has $K_{ij}^b$ from a prior setup phase, and $\mathcal{S}$
113 | has $\Delta_i$ and $K_{ij}^{\Delta_i}$ from that setup phase.
114 |
115 | $\mathcal{R}$ has an input matrix $X_{ij}$, with $i \in [\kappa]$, and $j \in [\lambda]$.
116 |
117 | We also require a pseudo-random generator $\text{PRG} : \mathbb{F}_2^{\lambda} \to \mathbb{F}_2^{\kappa}$.
118 | This generator is parameterized by a session id $\text{sid}$, allowing the same
119 | setup to be used for multiple extensions, so long as $\text{sid}$ is **unique**
120 | for each execution.
121 |
122 | 1. $\mathcal{R}$ computes: $T_ {ij}^b \gets \text{PRG}_ {\text{sid}}(K^b_ {j \bullet})_ i$.
123 | 2. $\mathcal{S}$ computes: $T_ {ij}^{\Delta_ j} \gets \text{PRG}_ {\text{sid}}(K^{\Delta_ j}_ {j \bullet})_ i$.
124 | 3. $\mathcal{R}$ computes $U_{ij} = T_{ij}^0 + T_{ij}^1 + X_{ij}$.
125 | 4. $\star$ $\mathcal{R}$ sends $U_{ij}$ to $\mathcal{S}$
126 | 5. $\bullet$ $\mathcal{S}$ waits to receive $U_{ij}$.
127 | 6. $\mathcal{S}$ computes $Q_{ij} = \Delta_j \cdot U_{ij} + T_{ij}^{\Delta_j}$.
128 | 7. $\square$ $\mathcal{R}$ returns $T_{ij}^0$, and $\mathcal{S}$ returns $Q_{ij}$.
129 |
130 | Note that since we're working in $\mathbb{F}_ 2$, we have $Q_ {ij} = T_ {ij}^0 + X_ {ij} \cdot \Delta_ j$.
131 |
132 | ## Random OT Extension
133 |
134 | Random OT extension also uses $K^b_{ij}$, and $\Delta_i$ from the setup phase.
135 | The output of this phase are $\kappa$ pairs of random field elements
136 | $v_1^b, \ldots, v_\kappa^b$ in $\mathbb{F}_q$ for the sender, and $v_i^{b_i}$ for the receiver,
137 | where $b_i$ is random bit for the $i$-th element.
138 |
139 | For the sake of this protocol, we can identifier vectors in $\mathbb{F}_ 2^\lambda$
140 | with field elements in $\mathbb{F}_ {2^\lambda}$, and we write $\text{mul}$ for
141 | explicit multiplication in this field.
142 |
143 | Protocol `Random-OT-Extension`:
144 |
145 | $\mathcal{R}$ has $K^b_{ij}$ from a prior setup phase, and $\mathcal{S}$ has
146 | $\Delta_i$ and $K_{ij}^{\Delta_i}$ from that same setup.
147 |
148 | This protocol is also parameterized by a unique session id $\text{sid}$
149 |
150 | 1. $\mathcal{R}$ generates a random vector $b_ i \in \mathbb{F}_ 2$, with $i \in [\kappa']$, and sets $X_ {ij} \gets b_ i 1_ j$. Where $1_ j$ is a vector filled with $\lambda$ ones.
151 | 2. $\mathcal{R}$ and $\mathcal{S}$ run `Correlated-OT-Extension`, with batch size $\kappa'$, and session id $\text{sid}$ with $\mathcal{R}$
152 | using $X_{ij}$ as its input.
153 | The parties receive $T_ {ij}$ and $Q_ {ij}$ respectively.
154 | 3. $\mathcal{S}$ samples $s \xleftarrow{R} \mathbb{F}_2^\lambda$.
155 | 4. $\star$ $\mathcal{S}$ sends $s$ to $\mathcal{R}$.
156 | 5. $\bullet$ $\mathcal{R}$ waits to receive $s$.
157 | 6. Let $\mu \gets \lceil \kappa' / \lambda \rceil$,
158 | then, the parties set $\hat{T}_ {ij}$, $\hat{b}_ i$, $\hat{Q}_ {ij}$,
159 | with $i \in [\mu]$, $j \in [\lambda]$ by grouping adjacent bits
160 | into elements of the field $\mathbb{F}_ {2^\lambda}$.
161 |
162 | 7. The parties use a
163 | PRG to set $\chi_1, \ldots, \chi_\mu \gets \text{PRG}(s)$,
164 | where $\chi_i \in \mathbb{F}_{2^\lambda}$.
165 | 8. $\mathcal{R}$ computes $x \gets \langle \hat{b}_ i, \chi_ i \rangle$,
166 | and $t_ j \gets \langle \text{mul}(\hat{T}_ {i j}, \chi_i), 1_ i\rangle$.
167 | 9. $\star$ $\mathcal{R}$ sends $x$ and $t_ 1, \ldots, t_ {\lambda}$ to $\mathcal{S}$.
168 | 10. $\mathcal{S}$ calculates $q_j \gets \langle \text{mul}(\hat{Q}_{ij}, \chi_i), 1_i \rangle$.
169 | 11. $\bullet$ $\mathcal{S}$ waits to receive $x$ and $t_j$, and checks that
170 | $q_j = t_j + \Delta_j \cdot x$.
171 | 12. $\mathcal{S}$ sets $v^0_i \gets H_i(Q_{i\bullet})$ and $v^1_i \gets H_i(Q_{i \bullet} + \Delta_\bullet)$, for $i \in [\kappa]$
172 | 13. $\mathcal{R}$ sets $v^{b_ i}_ i \gets H_ i(T_ {i\bullet})$, for $i \in [\kappa]$
173 |
174 | # Multiplicative to Additive Conversion
175 |
176 | We follow [HMRT21](https://eprint.iacr.org/2021/1373).
177 |
178 | In this protocol, two parties $\mathcal{S}$ and $\mathcal{R}$ have values
179 | $a, b \in \mathbb{F}_q$ respectively.
180 | The output of this protocol has each party receiver $\alpha, \beta \in \mathbb{F}_q$
181 | respectively, such that $\alpha + \beta = a \cdot b$.
182 |
183 | This protocol requires the parties to have a triple setup.
184 | Additionally, rather than describing the protocol as making a call to a random
185 | OT extension internally, we instead say that the participants must have done
186 | this prior to the protocol.
187 | This makes our description of using a single OT extension for multiple instances
188 | of MTA easier.
189 |
190 | Protocol `MTA`:
191 |
192 | Let $\kappa = \lceil \lg q \rceil + \lambda$.
193 |
194 | The parties have, in a previous phase, have generated correlated randomness
195 | of the following form:
196 |
197 | $$
198 | \begin{aligned}
199 | &v_i^0, v_i^1 \xleftarrow{R} \mathbb{F}_q\ (i \in [\kappa])\cr
200 | &t_i \xleftarrow{R} \mathbb{F}_2\cr
201 | &\mathcal{S} \texttt{ receives } (v_i^0, v_i^1)\cr
202 | &\mathcal{R} \texttt{ receives } (t_i, v_i^{t_i})\cr
203 | \end{aligned}
204 | $$
205 |
206 | 1. $\mathcal{S}$ samples random $\delta_1, \ldots, \delta_\kappa \xleftarrow{R} \mathbb{F}_q$.
207 | 2. $\star$ $\mathcal{S}$ sends $(-a + \delta_i + v_i^0, a + \delta_i + v_i^1)$ to $\mathcal{R}$.
208 | 3. $\bullet$ $\mathcal{R}$ waits to receive $(c^0_i, c^1_i)$ from $\mathcal{S}$, and
209 | sets $m_i \gets c^{t_i}_i - v_i^{t_i}$
210 | 4. $\mathcal{R}$ samples $s \xleftarrow{R} \mathbb{F}_2^\lambda$, and
211 | extends this into $\chi_2, \ldots, \chi_\kappa \gets \text{PRG}(s)$.
212 | $\mathcal{S}$ then sets $\chi_ 1 \gets (-1)^{t_ 1}(b - \sum_{i \in [2\ldots \kappa]} \chi_ i \cdot (-1)^{t_ i})$.
213 | (This makes it so that $b = \langle \chi_ i, (-1)^{t_ i} \rangle$)
214 | 5. $\mathcal{R}$ saves $\beta = \langle \chi_ i, m_ i \rangle$.
215 | 6. $\star$ $\mathcal{R}$ sends $s$ and $\chi_ 1$ to $\mathcal{S}$.
216 | 7. $\bullet$ $\mathcal{S}$ waits to receive $s$ and $\chi_ 1$, and uses $s$
217 | to expand $\chi_ 2, \ldots, \chi_\kappa \gets \text{PRG(s)}$.
218 | 8. $\square$ $\mathcal{S}$ outputs $\alpha \gets - \langle \chi_ i, \delta_ i \rangle$
219 |
220 | In the presence of malicious parties, this protocol may return a result
221 | such that $\alpha + \beta$ is *not* $ab$, however, malicious parties
222 | cannot learn information about the other party's result, except with
223 | negligible probability.
224 |
225 | Our triple generation protocol will take care of multiplication potentially
226 | being wrong.
227 |
228 | # Multiplication
229 |
230 | This protocol involves $n$ parties $P_1, \ldots, P_n$.
231 | Each of them has a share $a_i$ and $b_i$, of global values $a$ and $b$ in $\mathbb{F}_q$.
232 |
233 | The goal is for each party to obtain a share $c_i$ of $c = ab$.
234 |
235 | The idea behind the protocol is to use the decomposition:
236 |
237 | $$
238 | c = ab = (\sum_i a_i)(\sum_j b_j) = \sum_{ij} a_i b_j
239 | $$
240 |
241 | We run the `MTA` protocol for each unordered pair of parties, giving each party
242 | two shares $\gamma^0_i$, and $\gamma^1_i$, which they then add to $a_{i} b_i$ to
243 | get their share $c_i$.
244 |
245 | The protocol is also parameterized by a unique session id $\text{sid}$,
246 | and requires the triple setup phase to have been performed.
247 |
248 | Protocol `Multiplication`:
249 |
250 | In parallel, for each order pair of parties $P_i < P_j$,
251 | with $\mathcal{S} = P_i$, being the sender, and $\mathcal{R} = P_j$
252 | being the receiver.
253 |
254 | Let $\kappa = \lceil q \rceil + \lambda$.
255 |
256 | 1. $\mathcal{S}$ and $\mathcal{R}$ run `Random-OT-Extension` with $\text{sid}$ and a batch size of $2 \kappa$.
257 | $\mathcal{S}$ receives $v_i^0, v_i^1$, and $\mathcal{R}$ receives $t_i$ and $v_i^{t_i}$.
258 |
259 | In parallel, for $(a,b) = (a_i, b_j)$ and $(a, b) = (b_i, a_j)$:
260 |
261 | 2. $\mathcal{S}$ and $\mathcal{R}$ run `MTA` using the first (or last, in the second
262 | instance) $\kappa$ elements of the previous step, as well as their respective inputs
263 | $a$ and $b$.
264 |
265 | 3. $\mathcal{S}$ receives $\gamma_j^0, \gamma_j^1$, and $\mathcal{R}$ receives
266 | $\gamma_i^0, \gamma_i^1$. (Writing it this way means that each party has
267 | one instance of $\gamma$ for every other party they're interacting with).
268 |
269 | After all the p2p interactions are done:
270 |
271 | 4. Every party $P_i$ sets $c_i = a_i b_i + \sum_j (\gamma_j^0 + \gamma_j^1)$.
272 |
273 | # Triple Generation
274 |
275 | The goal of triple generation is to generate *threshold* shares
276 | of values $a, b, c$ such that $ab = c$.
277 | Additionally, the parties should also learn $A = a \cdot G$, $B = b \cdot G$,
278 | and $C = c \cdot G$.
279 |
280 | More concretely, we have a set of parties $\mathcal{P}$ of size $N$,
281 | which want to generate a triple with threshold $t$.
282 |
283 | **Round 1:**
284 |
285 | 1. $T.\text{Add}(\mathbb{G}, \mathcal{P}, t)$
286 | 2. Each $P_ i$ samples $e, f, l \xleftarrow{R} \mathbb{F}_ q[X]_ {\leq (t - 1)}$.
287 | 3. Each $P_ i$ sets $l(0) = 0$.
288 | 3. Each $P_i$ sets $E_i \gets e \cdot G, F_i \gets f \cdot G, L_i \gets l \cdot G$.
289 | 4. Each $P_i$ sets $(\text{Com}_i, r_i) \gets \text{Commit}((E_i, F_i, L_i))$.
290 | 5. $\star$ Each $P_i$ sends $\text{Com}_i$ to all other parties.
291 |
292 | **Round 2:**
293 |
294 | 1. $\bullet$ Each $P_i$ waits to receive $\text{Com}_j$ from each other $P_j$.
295 | 2. Each $P_i$ sets $\text{Confirm}_i \gets H(\text{Com}_1, \ldots, \text{Com}_N)$.
296 | 3. $T.\text{Add}(\text{Confirm}_i)$
297 | 4. In *parallel* to the following steps, the parties run `Multiplication` using
298 | $\text{Confirm}_i$ as the session id, and using $e(0)$ and $f(0)$ as their personal shares.
299 | 5. $\star$ Each $P_i$ sends $\text{Confirm}_i$ to every other party.
300 | 6. Each $P_i$ generates the proofs:
301 |
302 | $$
303 | \begin{aligned}
304 | \pi^0_i &\gets \text{Prove}(T.\text{Cloned}(\texttt{dlog0}, i), \text{Mau}(- \cdot G, E_i(0); e(0)))\cr
305 | \pi^1_i &\gets \text{Prove}(T.\text{Cloned}(\texttt{dlog1}, i), \text{Mau}(- \cdot G, F_i(0); f(0)))\cr
306 | \end{aligned}
307 | $$
308 |
309 | 7. $\star$ Each $P_i$ sends $(E_i, F_i, L_i, r_i, \pi^0_i, \pi^1_i)$ to every other party.
310 | 7. $\textcolor{red}{\star}$ Each $P_i$ *privately* sends $a_i^j = e(j)$ and $b_i^j$ = $f(j)$ to every other party $P_j$.
311 |
312 | **Round 3:**
313 |
314 | 1. $\bullet$ Each $P_i$ waits to receive $\text{Confirm}_j$ from each other $P_j$.
315 | 2. $\blacktriangle$ Each $P_i$ *asserts* that $\forall P_j.\ \text{Confirm}_j = \text{Confirm}_i$.
316 | 3. $\bullet$ Each $P_i$ waits to receive $(E_j, F_j, L_j, r_i, \pi^0_j, \pi^1_j)$ from each other $P_j$.
317 | 4. $\blacktriangle$ Each $P_i$ asserts that $\forall P_j$:
318 |
319 | $$
320 | \begin{aligned}
321 | &\text{deg}(E_j) = \text{deg}(F_j) = \text{deg}(L_j) = t - 1\cr
322 | &\forall j. L_j(0) = 0\cr
323 | &\text{CheckCommit}(\text{Com}_j, (E_j, F_j, L_j), r_j)\cr
324 | &\text{Verify}(T.\text{Cloned}(\texttt{dlog0}, j), \pi^0_j, \text{Mau}(- \cdot G, E_j(0)))\cr
325 | &\text{Verify}(T.\text{Cloned}(\texttt{dlog1}, j), \pi^1_j, \text{Mau}(- \cdot G, F_j(0)))\cr
326 | \end{aligned}
327 | $$
328 |
329 | 5. $\bullet$ Each $P_i$ waits to receive $a^i_j$ and $b^i_j$ from every other $P_j$.
330 | 6. Each $P_i$ sets $a_i \gets \sum_j a^i_j$, $b_i \gets \sum_j b^i_j$, $E \gets \sum_j E_j$, and $F \gets \sum_j F_j$.
331 | 7. $\blacktriangle$ Each $P_i$ *asserts* that $E(i) = a_i \cdot G$ and $F(i) = b_i \cdot G$.
332 | 8. Each $P_i$ sets $C_i \gets e(0) \cdot F(0)$.
333 | 9. Each $P_I$ generates the proof:
334 |
335 | $$
336 | \pi_i \gets \text{Prove}(T.\text{Cloned}(\texttt{dlogeq0}, i), \text{Mau}((- \cdot G, - \cdot F(0)), (E_i(0), C_i); e(0))
337 | $$
338 |
339 | 10. $\star$ Each $P_i$ sends $(C_i, \pi_i)$ to every other party.
340 |
341 | **Round 4:**
342 |
343 | 1. $\bullet$ Each $P_i$ waits to receive $(C_j, \pi_j)$ from each other $P_j$.
344 | 2. $\blacktriangle$ Each $P_i$ *asserts* that $\forall P_j$:
345 |
346 | $$
347 | \text{Verify}(T.\text{Cloned}(\texttt{dlogeq0}, j), \pi_j, \text{Mau}((- \cdot G, - \cdot F(0)), (E_j(0), C_j)))
348 | $$
349 |
350 | 3. Each $P_i$ sets $C \gets \sum_i C_i$.
351 | 4. $\bullet$ Each $P_i$ waits to receive $l_0$ from the `Multiplication` protocol.
352 | 5. Each $P_i$ sets $\hat{C}_i = l_0 \cdot G$.
353 | 6. Each $P_i$ generates the proof:
354 |
355 | $$
356 | \begin{aligned}
357 | \pi_i &\gets \text{Prove}(T.\text{Cloned}(\texttt{dlog2}, i), \text{Mau}(- \cdot G, \hat{C}_i; l_0)))\cr
358 | \end{aligned}
359 | $$
360 |
361 | 7. $\star$ Each $P_i$ sends $(\hat{C}_i, \pi_i)$ to every other party.
362 | 8. $\textcolor{red}{\star}$ Each $P_i$ *privately* sends $c_i^j \gets l_0 + l_i(j)$ to every other $P_j$.
363 |
364 | **Round 5:**
365 |
366 | 1. $\bullet$ Each $P_i$ waits to receive $(\hat{C}_j, \pi_j)$ from every other party.
367 | 2. $\blacktriangle$ Each $P_i$ *asserts* that (for all $j$):
368 |
369 | $$
370 | \begin{aligned}
371 | &\text{Verify}(T.\text{Cloned}(\texttt{dlog2}, j), \pi_j, \text{Mau}(- \cdot G, \hat{C}_j)\cr
372 | \end{aligned}
373 | $$
374 |
375 | 3. Each $P_i$ sets $L \gets \sum_i \hat{C}_i + L_i$.
376 | 4. $\blacktriangle$ Each $P_i$ *asserts* that $C = L(0)$.
377 | 5. $\bullet$ Each $P_i$ waits to receive $c_j^i$ from every other $P_j$.
378 | 6. Each $P_i$ sets $c_i \gets \sum_j c_j^i$.
379 | 7. $\blacktriangle$ Each $P_i$ *asserts* that $L(i) = c_i \cdot G$.
380 | 8. Each $P_i$ sets $A \gets E(0)$, $B \gets F(0)$.
381 | 9. $\square$ Each $P_i$ returns $((a_i, b_i, c_i), (A, B, C))$.
382 |
383 |
--------------------------------------------------------------------------------
/examples/network-benches.rs:
--------------------------------------------------------------------------------
1 | use std::{
2 | collections::HashMap,
3 | time::{Duration, Instant},
4 | };
5 |
6 | use cait_sith::{
7 | keygen, presign,
8 | protocol::{Action, MessageData, Participant, Protocol},
9 | sign, triples, PresignArguments,
10 | };
11 | use digest::{Digest, FixedOutput};
12 | use easy_parallel::Parallel;
13 | use ecdsa::hazmat::DigestPrimitive;
14 | use elliptic_curve::{ops::Reduce, Curve};
15 | use haisou_chan::{channel, Bandwidth};
16 |
17 | use k256::{FieldBytes, Scalar, Secp256k1};
18 | use rand_core::OsRng;
19 | use structopt::StructOpt;
20 |
21 | fn scalar_hash(msg: &[u8]) -> Scalar {
22 | let digest = ::Digest::new_with_prefix(msg);
23 | let m_bytes: FieldBytes = digest.finalize_fixed();
24 | ::Uint>>::reduce_bytes(&m_bytes)
25 | }
26 |
27 | #[derive(Debug, StructOpt)]
28 | struct Args {
29 | /// The number of parties to run the benchmarks with.
30 | parties: u32,
31 | /// The latency, in milliseconds.
32 | latency_ms: u32,
33 | /// The bandwidth, in bytes per second.
34 | bandwidth: u32,
35 | }
36 |
37 | #[derive(Debug, Clone, Copy)]
38 | struct Stats {
39 | sent: usize,
40 | received: usize,
41 | }
42 |
43 | fn run_protocol(
44 | latency: Duration,
45 | bandwidth: Bandwidth,
46 | participants: &[Participant],
47 | f: F,
48 | ) -> Vec<(Participant, Stats, T)>
49 | where
50 | F: Fn(Participant) -> P + Send + Sync,
51 | P: Protocol