multi_party_schnorr/common/
utils.rs1use crypto_box::{
5 aead::{Aead, AeadCore},
6 PublicKey, SalsaBox, SecretKey,
7};
8
9use crypto_bigint::{
10 generic_array::{typenum::Unsigned, GenericArray},
11 rand_core::CryptoRngCore,
12};
13
14use sha2::{Digest, Sha256};
15
16pub const SCALAR_CIPHERTEXT_SIZE: usize = 64 + <SalsaBox as AeadCore>::TagSize::USIZE;
18
19#[cfg(feature = "serde")]
21pub mod serde_point {
22 use std::marker::PhantomData;
23
24 use elliptic_curve::group::GroupEncoding;
25 use serde::de::Visitor;
26
27 pub fn serialize<S, G: GroupEncoding>(point: &G, serializer: S) -> Result<S::Ok, S::Error>
28 where
29 S: serde::Serializer,
30 {
31 use serde::ser::SerializeTuple;
32 let mut tup = serializer.serialize_tuple(G::Repr::default().as_ref().len())?;
33 for byte in point.to_bytes().as_ref().iter() {
34 tup.serialize_element(byte)?;
35 }
36 tup.end()
37 }
38
39 pub fn deserialize<'de, D, G: GroupEncoding>(deserializer: D) -> Result<G, D::Error>
40 where
41 D: serde::Deserializer<'de>,
42 {
43 struct PointVisitor<G: GroupEncoding>(PhantomData<G>);
44
45 impl<'de, G: GroupEncoding> Visitor<'de> for PointVisitor<G> {
46 type Value = G;
47
48 fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
49 formatter.write_str("a valid point in Edwards y + sign format")
50 }
51
52 fn visit_seq<A>(self, mut seq: A) -> Result<G, A::Error>
53 where
54 A: serde::de::SeqAccess<'de>,
55 {
56 let mut encoding = G::Repr::default();
57 for (idx, byte) in encoding.as_mut().iter_mut().enumerate() {
58 *byte = seq.next_element()?.ok_or_else(|| {
59 serde::de::Error::invalid_length(idx, &"wrong length of point")
60 })?;
61 }
62
63 Option::from(G::from_bytes(&encoding))
64 .ok_or(serde::de::Error::custom("point decompression failed"))
65 }
66 }
67
68 deserializer.deserialize_tuple(G::Repr::default().as_ref().len(), PointVisitor(PhantomData))
69 }
70}
71
72#[cfg(feature = "serde")]
73pub mod serde_vec_point {
74 use elliptic_curve::group::GroupEncoding;
75
76 pub fn serialize<S, G: GroupEncoding>(points: &[G], serializer: S) -> Result<S::Ok, S::Error>
77 where
78 S: serde::Serializer,
79 {
80 let mut bytes = Vec::with_capacity(points.len() * G::Repr::default().as_ref().len());
81 points.iter().for_each(|point| {
82 bytes.extend_from_slice(point.to_bytes().as_ref());
83 });
84 serializer.serialize_bytes(&bytes)
85 }
86
87 pub fn deserialize<'de, D, G: GroupEncoding>(deserializer: D) -> Result<Vec<G>, D::Error>
88 where
89 D: serde::Deserializer<'de>,
90 {
91 let bytes: Vec<u8> = serde::Deserialize::deserialize(deserializer)?;
92 let point_size = G::Repr::default().as_ref().len();
93 if bytes.len() % point_size != 0 {
94 return Err(serde::de::Error::custom("Invalid number of bytes"));
95 }
96 let mut points = Vec::with_capacity(bytes.len() / point_size);
97 for i in 0..bytes.len() / point_size {
98 let mut encoding = G::Repr::default();
99 encoding
100 .as_mut()
101 .copy_from_slice(&bytes[i * point_size..(i + 1) * point_size]);
102 points.push(
103 Option::from(G::from_bytes(&encoding))
104 .ok_or_else(|| serde::de::Error::custom("Invalid point"))?,
105 );
106 }
107 Ok(points)
108 }
109}
110
111#[cfg(feature = "serde")]
112pub mod serde_arc {
113 use std::sync::Arc;
114
115 use serde::{Deserialize, Serialize};
116
117 pub fn serialize<S, T: Serialize>(value: &Arc<T>, serializer: S) -> Result<S::Ok, S::Error>
118 where
119 S: serde::Serializer,
120 {
121 value.serialize(serializer)
122 }
123
124 pub fn deserialize<'de, D, T: Deserialize<'de>>(deserializer: D) -> Result<Arc<T>, D::Error>
125 where
126 D: serde::Deserializer<'de>,
127 {
128 Ok(Arc::new(T::deserialize(deserializer)?))
129 }
130}
131
132#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
133#[derive(Clone, Copy, Debug, bytemuck::AnyBitPattern, bytemuck::NoUninit)]
134#[repr(C)]
135pub struct EncryptedData {
140 pub sender_pid: u8,
141 pub receiver_pid: u8,
142 #[cfg_attr(feature = "serde", serde(with = "serde_bytes"))]
143 pub ciphertext: [u8; SCALAR_CIPHERTEXT_SIZE],
144 pub nonce: [u8; <SalsaBox as AeadCore>::NonceSize::USIZE],
146}
147
148pub type SessionId = [u8; 32];
150pub type HashBytes = [u8; 32];
151
152impl EncryptedData {
153 pub fn new(
154 ciphertext: [u8; SCALAR_CIPHERTEXT_SIZE],
155 nonce: [u8; 24],
156 sender_pid: u8,
157 receiver_pid: u8,
158 ) -> Self {
159 Self {
160 ciphertext,
161 nonce,
162 sender_pid,
163 receiver_pid,
164 }
165 }
166}
167
168pub(crate) trait BaseMessage {
170 fn party_id(&self) -> u8;
172}
173
174pub fn calculate_final_session_id(
176 party_ids: impl Iterator<Item = u8>,
177 sid_i_list: &[SessionId],
178 extra: &[&[u8]],
179) -> SessionId {
180 let mut hasher = Sha256::new();
181
182 for e in extra {
183 hasher.update(e);
184 }
185
186 party_ids.for_each(|pid| hasher.update((pid as u32).to_be_bytes()));
187 sid_i_list.iter().for_each(|sid| hasher.update(sid));
188
189 hasher.finalize().into()
190}
191
192pub fn encrypt_message<R: CryptoRngCore>(
193 sender_secret_info: (&SecretKey, u8),
194 receiver_public_info: (&PublicKey, u8),
195 message: &[u8; 64],
196 rng: &mut R,
197) -> Option<EncryptedData> {
198 let sender_box = SalsaBox::new(receiver_public_info.0, sender_secret_info.0);
199 let nonce = SalsaBox::generate_nonce(rng);
200 sender_box
201 .encrypt(&nonce, message.as_slice())
202 .ok()
203 .and_then(|data| {
204 Some(EncryptedData::new(
205 data.try_into().ok()?,
206 nonce.into(),
207 sender_secret_info.1,
208 receiver_public_info.1,
209 ))
210 })
211}
212
213pub fn decrypt_message(
214 receiver_private_key: &SecretKey,
215 sender_public_key: &PublicKey,
216 enc_data: &EncryptedData,
217) -> Option<[u8; 64]> {
218 let receiver_box = SalsaBox::new(sender_public_key, receiver_private_key);
219 receiver_box
220 .decrypt(
221 &GenericArray::from(enc_data.nonce),
222 enc_data.ciphertext.as_slice(),
223 )
224 .ok()
225 .and_then(|data| data.try_into().ok())
226}
227
228#[cfg(any(test, feature = "test-support"))]
229pub mod support {
230
231 use crate::keygen::{utils::setup_keygen, Keyshare};
232
233 use crate::common::{
234 ser::Serializable,
235 traits::{GroupElem, Round, ScalarReduce},
236 };
237
238 #[allow(clippy::map_identity)]
241 pub fn run_round<I, R, O, E>(actors: impl IntoIterator<Item = R>, msgs: I) -> Vec<O>
242 where
243 R: Round<Input = I, Output = O, Error = E> + Serializable + Send,
244 I: Clone + Sync,
245 E: std::fmt::Debug,
246 O: Send,
247 {
248 actors
249 .into_iter()
250 .map(|actor| {
251 #[cfg(all(feature = "serde", test))]
252 let actor: R = {
253 let mut v = vec![];
254 ciborium::into_writer(&actor, &mut v).unwrap();
255 ciborium::from_reader(v.as_ref() as &[u8]).unwrap()
256 };
257
258 actor
259 })
260 .map(|actor| actor.process(msgs.clone()).unwrap())
261 .collect()
262 }
263
264 pub fn run_keygen<const T: usize, const N: usize, G: GroupElem>() -> [Keyshare<G>; N]
266 where
267 G::Scalar: ScalarReduce<[u8; 32]>,
268 G::Scalar: Serializable,
269 {
270 let actors = setup_keygen(T as u8, N as u8);
271
272 let (actors, msgs): (Vec<_>, Vec<_>) = run_round(actors, ()).into_iter().unzip();
273 let (actors, msgs): (Vec<_>, Vec<_>) = run_round(actors, msgs).into_iter().unzip();
274
275 run_round(actors, msgs)
276 .try_into()
277 .map_err(|_| panic!("Failed to convert keyshares"))
278 .unwrap()
279 }
280}