1use elliptic_curve::Generate;
25use elliptic_curve::{PrimeField, ops::Invert, point::AffineCoordinates, sec1::ToSec1Point};
26use p256::FieldBytes;
27use p256::{AffinePoint, NonZeroScalar, ProjectivePoint, Scalar};
28use sha2::{Digest, Sha256};
29
30use confium_tc::Result;
31use confium_tc::message::Message;
32use confium_tc::registry::{RoundResult, SessionImpl};
33use confium_tc::session::SessionParams;
34
35use crate::error::{Gg18ErrorCode, scheme_error};
36use crate::lagrange;
37use crate::share::Gg18Share;
38
39pub struct Gg18SignP256;
41
42impl Gg18SignP256 {
43 pub fn build_session(params: &SessionParams) -> Result<Box<dyn SessionImpl>> {
44 let party_id = params.parties.get(params.this_party_idx)?.id.clone();
45 let message = params.message.clone().unwrap_or_default();
46 let share_bytes = params
47 .local_share
48 .as_ref()
49 .map(|s| s.bytes().to_vec())
50 .ok_or_else(|| scheme_error(Gg18ErrorCode::BAD_SHARE))?;
51 let share = Gg18Share::from_bytes(&share_bytes)?;
52
53 let k_i = NonZeroScalar::generate();
54 let r_i_point = (ProjectivePoint::GENERATOR * *k_i).to_affine();
55
56 Ok(Box::new(Gg18SignSession {
57 party_id,
58 message,
59 share,
60 k_i,
61 r_i_point,
62 round1_seen: Vec::new(),
63 round2_seen: Vec::new(),
64 k_inv: None,
65 r_scalar: None,
66 z: None,
67 our_partial: None,
68 round_done: 0,
69 signature: None,
70 }))
71 }
72}
73
74pub struct Gg18SignSession {
75 party_id: String,
76 message: Vec<u8>,
77 share: Gg18Share,
78 k_i: NonZeroScalar,
79 r_i_point: AffinePoint,
80 round1_seen: Vec<(String, u64, AffinePoint)>,
81 round2_seen: Vec<(String, u64, Scalar)>,
82 k_inv: Option<Scalar>,
83 r_scalar: Option<Scalar>,
84 z: Option<Scalar>,
85 our_partial: Option<Scalar>,
86 round_done: u8,
87 signature: Option<Vec<u8>>,
88}
89
90const TAG_NONCE_POINT: u8 = 0xD1;
91const TAG_NONCE_REVEAL: u8 = 0xD2;
92const TAG_PARTIAL: u8 = 0xD3;
93
94impl Gg18SignSession {
95 fn round1_commit(&mut self) -> Result<RoundResult> {
96 let mut payload = Vec::with_capacity(1 + 1 + 33);
97 payload.push(TAG_NONCE_POINT);
98 payload.push(self.share.party_idx as u8);
99 payload.extend_from_slice(self.r_i_point.to_sec1_point(true).as_bytes());
100 Ok(RoundResult::new(
101 vec![Message::broadcast(&self.party_id, 1, payload)],
102 false,
103 ))
104 }
105
106 fn round2_reveal(&mut self, incoming: &[Message]) -> Result<RoundResult> {
107 for msg in incoming {
108 if msg.round != 1 || msg.payload.is_empty() {
109 continue;
110 }
111 if msg.payload[0] != TAG_NONCE_POINT {
112 continue;
113 }
114 if msg.payload.len() != 1 + 1 + 33 {
115 return Err(scheme_error(Gg18ErrorCode::BAD_ROUND_MESSAGE));
116 }
117 let idx = msg.payload[1] as u64;
118 let pt = decode_affine(&msg.payload[2..35])?;
119 if msg.from_party_id == self.party_id {
120 if idx != self.share.party_idx as u64 {
121 return Err(scheme_error(Gg18ErrorCode::BAD_ROUND_MESSAGE));
122 }
123 continue;
124 }
125 self.round1_seen.push((msg.from_party_id.clone(), idx, pt));
126 }
127
128 let mut reveal = Vec::with_capacity(1 + 1 + 32);
129 reveal.push(TAG_NONCE_REVEAL);
130 reveal.push(self.share.party_idx as u8);
131 reveal.extend_from_slice(&self.k_i.to_bytes());
132 Ok(RoundResult::new(
133 vec![Message::broadcast(&self.party_id, 2, reveal)],
134 false,
135 ))
136 }
137
138 fn round3_partial(&mut self, incoming: &[Message]) -> Result<RoundResult> {
139 for msg in incoming {
140 if msg.round != 2 || msg.payload.is_empty() {
141 continue;
142 }
143 if msg.payload[0] != TAG_NONCE_REVEAL {
144 continue;
145 }
146 if msg.payload.len() != 1 + 1 + 32 {
147 return Err(scheme_error(Gg18ErrorCode::BAD_ROUND_MESSAGE));
148 }
149 let idx = msg.payload[1] as u64;
150 let mut kb = [0u8; 32];
151 kb.copy_from_slice(&msg.payload[2..34]);
152 let fb: p256::FieldBytes = kb.into();
153 let k: Scalar = Option::from(Scalar::from_repr(fb))
154 .ok_or_else(|| scheme_error(Gg18ErrorCode::BAD_ROUND_MESSAGE))?;
155 if msg.from_party_id == self.party_id {
156 continue;
157 }
158 self.round2_seen.push((msg.from_party_id.clone(), idx, k));
159 }
160
161 let mut parts: Vec<(u64, String, Scalar, AffinePoint)> = Vec::new();
162 parts.push((
163 self.share.party_idx as u64,
164 self.party_id.clone(),
165 *self.k_i,
166 self.r_i_point,
167 ));
168 for (pid, idx, k) in &self.round2_seen {
169 let r_j = self
170 .round1_seen
171 .iter()
172 .find(|(p, _, _)| p == pid)
173 .map(|(_, _, pt)| *pt)
174 .ok_or_else(|| scheme_error(Gg18ErrorCode::BAD_ROUND_MESSAGE))?;
175 parts.push((*idx, pid.clone(), *k, r_j));
176 }
177 parts.sort_by_key(|(idx, _, _, _)| *idx);
178
179 let k_sum: Scalar = parts
180 .iter()
181 .map(|(_, _, k, _)| *k)
182 .fold(Scalar::ZERO, |a, b| a + b);
183 let k_nz: NonZeroScalar = Option::from(NonZeroScalar::new(k_sum))
184 .ok_or_else(|| scheme_error(Gg18ErrorCode::INTERNAL))?;
185 let k_inv: Scalar = *k_nz.invert();
186
187 let mut r_proj = ProjectivePoint::IDENTITY;
188 for (_, _, _, pt) in &parts {
189 r_proj += ProjectivePoint::from(*pt);
190 }
191 if r_proj == ProjectivePoint::IDENTITY {
192 return Err(scheme_error(Gg18ErrorCode::INTERNAL));
193 }
194 let r_affine = r_proj.to_affine();
195 let r_scalar = reduce_x_mod_n(r_affine);
196
197 let z = hash_to_scalar(&self.message);
198
199 let xs_scalar: Vec<Scalar> = parts
200 .iter()
201 .map(|(idx, _, _, _)| Scalar::from(*idx))
202 .collect();
203 let our_idx_scalar = Scalar::from(self.share.party_idx as u64);
204 let lambda_i = lagrange::lagrange_basis_scalar(our_idx_scalar, &xs_scalar);
205 let our_partial = k_inv * r_scalar * lambda_i * self.share.scalar();
206
207 let mut partial_payload = Vec::with_capacity(1 + 1 + 32);
208 partial_payload.push(TAG_PARTIAL);
209 partial_payload.push(self.share.party_idx as u8);
210 partial_payload.extend_from_slice(&our_partial.to_bytes());
211
212 self.k_inv = Some(k_inv);
213 self.r_scalar = Some(r_scalar);
214 self.z = Some(z);
215 self.our_partial = Some(our_partial);
216
217 Ok(RoundResult::new(
218 vec![Message::broadcast(&self.party_id, 3, partial_payload)],
219 false,
220 ))
221 }
222
223 fn round4_combine(&mut self, incoming: &[Message]) -> Result<RoundResult> {
224 let mut partials: Vec<(u64, Scalar)> = Vec::new();
225 for msg in incoming {
226 if msg.round != 3 || msg.payload.is_empty() {
227 continue;
228 }
229 if msg.payload[0] != TAG_PARTIAL {
230 continue;
231 }
232 if msg.payload.len() != 1 + 1 + 32 {
233 return Err(scheme_error(Gg18ErrorCode::BAD_ROUND_MESSAGE));
234 }
235 let idx = msg.payload[1] as u64;
236 let mut sb = [0u8; 32];
237 sb.copy_from_slice(&msg.payload[2..34]);
238 let fb: p256::FieldBytes = sb.into();
239 let s: Scalar = Option::from(Scalar::from_repr(fb))
240 .ok_or_else(|| scheme_error(Gg18ErrorCode::BAD_ROUND_MESSAGE))?;
241 if msg.from_party_id == self.party_id {
242 continue;
243 }
244 partials.push((idx, s));
245 }
246 let our_partial = self
247 .our_partial
248 .ok_or_else(|| scheme_error(Gg18ErrorCode::INTERNAL))?;
249 partials.push((self.share.party_idx as u64, our_partial));
250
251 let k_inv = self
252 .k_inv
253 .ok_or_else(|| scheme_error(Gg18ErrorCode::INTERNAL))?;
254 let r_scalar = self
255 .r_scalar
256 .ok_or_else(|| scheme_error(Gg18ErrorCode::INTERNAL))?;
257 let z = self
258 .z
259 .ok_or_else(|| scheme_error(Gg18ErrorCode::INTERNAL))?;
260
261 let mut s = k_inv * z;
262 for (_, si) in &partials {
263 s += si;
264 }
265 let s = normalize_s_low(s);
266
267 let r_nz: NonZeroScalar = Option::from(NonZeroScalar::new(r_scalar))
268 .ok_or_else(|| scheme_error(Gg18ErrorCode::BAD_PARTIAL_SIGNATURE))?;
269 let s_nz: NonZeroScalar = Option::from(NonZeroScalar::new(s))
270 .ok_or_else(|| scheme_error(Gg18ErrorCode::BAD_PARTIAL_SIGNATURE))?;
271 let sig = p256::ecdsa::Signature::from_scalars(r_nz, s_nz)
272 .map_err(|_| scheme_error(Gg18ErrorCode::INTERNAL))?;
273 let vk = p256::ecdsa::VerifyingKey::from_affine(self.share.public_key)
274 .map_err(|_| scheme_error(Gg18ErrorCode::INTERNAL))?;
275 use p256::ecdsa::signature::Verifier;
276 vk.verify(&self.message, &sig)
277 .map_err(|_| scheme_error(Gg18ErrorCode::BAD_PARTIAL_SIGNATURE))?;
278
279 let mut out = Vec::with_capacity(64);
280 out.extend_from_slice(&r_scalar.to_bytes());
281 out.extend_from_slice(&s.to_bytes());
282 self.signature = Some(out);
283
284 Ok(RoundResult::done())
285 }
286}
287
288impl SessionImpl for Gg18SignSession {
289 fn round(&mut self, incoming: &[Message]) -> Result<RoundResult> {
290 self.round_done = self.round_done.checked_add(1).ok_or_else(|| {
291 confium_tc::error::RoundOverflowSnafu {
292 round: self.round_done,
293 }
294 .build()
295 })?;
296 match self.round_done {
297 1 => self.round1_commit(),
298 2 => self.round2_reveal(incoming),
299 3 => self.round3_partial(incoming),
300 4 => self.round4_combine(incoming),
301 other => Err(confium_tc::error::RoundOverflowSnafu { round: other }.build()),
302 }
303 }
304
305 fn result(&self) -> Result<Vec<u8>> {
306 if self.round_done < 4 {
307 return Err(confium_tc::error::SessionNotCompleteSnafu {}.build());
308 }
309 self.signature
310 .clone()
311 .ok_or_else(|| scheme_error(Gg18ErrorCode::INTERNAL))
312 }
313
314 fn destroy(&mut self) {
315 self.k_i = Option::from(NonZeroScalar::new(Scalar::ONE))
316 .unwrap_or_else(|| NonZeroScalar::new(Scalar::ONE).unwrap());
317 self.k_inv = None;
318 self.r_scalar = None;
319 self.z = None;
320 self.our_partial = None;
321 self.signature = None;
322 }
323}
324
325fn reduce_x_mod_n(point: AffinePoint) -> Scalar {
326 let x = point.x();
327 let x_bytes: &[u8] = x.as_slice();
328 let mut arr = [0u8; 32];
329 let n = x_bytes.len().min(32);
330 arr[..n].copy_from_slice(&x_bytes[..n]);
331 reduce_to_scalar(arr)
332}
333
334fn reduce_to_scalar(mut bytes: [u8; 32]) -> Scalar {
337 loop {
338 if let Some(s) = Option::<Scalar>::from(Scalar::from_repr(FieldBytes::from(bytes))) {
339 return s;
340 }
341 let mut h = Sha256::new();
342 h.update(b"confium-scalar-reduce-v1");
343 h.update(bytes);
344 bytes = h.finalize().into();
345 }
346}
347
348fn hash_to_scalar(message: &[u8]) -> Scalar {
349 let mut h = Sha256::new();
350 h.update(message);
351 let digest: [u8; 32] = h.finalize().into();
352 reduce_to_scalar(digest)
353}
354
355fn normalize_s_low(s: Scalar) -> Scalar {
356 use crypto_bigint::Limb;
357 use elliptic_curve::Curve;
358 use p256::NistP256;
359 let n_u = <NistP256 as Curve>::ORDER.get();
360 let half = n_u >> 1usize;
361 let s_u = decode_scalar_to_uint(s);
362 if s_u > half {
363 let (s_prime, _) = n_u.borrowing_sub(&s_u, Limb::ZERO);
364 let be = s_prime.to_be_bytes();
365 let src: &[u8] = be.as_slice();
366 let mut bytes = [0u8; 32];
367 let off = src.len().saturating_sub(32);
368 let take = src.len() - off;
369 bytes[..take].copy_from_slice(&src[off..]);
370 let fb: p256::FieldBytes = bytes.into();
371 Option::from(Scalar::from_repr(fb)).unwrap_or(s)
372 } else {
373 s
374 }
375}
376
377fn decode_scalar_to_uint(s: Scalar) -> p256::U256 {
378 use elliptic_curve::Curve;
379 use p256::NistP256;
380 let bytes = s.to_bytes();
381 <NistP256 as Curve>::Uint::from_be_slice(bytes.as_slice())
382}
383
384fn decode_affine(bytes: &[u8]) -> Result<AffinePoint> {
385 use elliptic_curve::sec1::FromSec1Point;
386 if bytes.len() != 33 {
387 return Err(scheme_error(Gg18ErrorCode::BAD_ROUND_MESSAGE));
388 }
389 let enc = elliptic_curve::sec1::Sec1Point::<p256::NistP256>::from_bytes(bytes)
390 .map_err(|_| scheme_error(Gg18ErrorCode::BAD_ROUND_MESSAGE))?;
391 let pt: AffinePoint = Option::from(AffinePoint::from_sec1_point(&enc))
392 .ok_or_else(|| scheme_error(Gg18ErrorCode::BAD_ROUND_MESSAGE))?;
393 let _ = pt.x();
394 Ok(pt)
395}