confium_tc_gg18/
inprocess.rs1use elliptic_curve::sec1::ToSec1Point;
27use p256::AffinePoint;
28
29use confium_tc::Result;
30use confium_tc::inprocess as driver;
31
32use crate::share::Gg18Share;
33
34#[derive(Debug, Clone)]
37pub struct KeygenOutput {
38 pub shares: Vec<Vec<u8>>,
41 pub public_key: Vec<u8>,
43}
44
45pub fn keygen(threshold: u32, party_count: usize) -> Result<KeygenOutput> {
51 let shares = driver::run_dkg(crate::DKG_SCHEME_NAME, threshold, party_count)?;
52 let first = Gg18Share::from_bytes(&shares[0])?;
53 let public_key: Vec<u8> = first.public_key.to_sec1_point(true).as_bytes().to_vec();
54 Ok(KeygenOutput { shares, public_key })
55}
56
57pub fn sign(share_blobs: &[Vec<u8>], threshold: u32, message: &[u8]) -> Result<Vec<u8>> {
65 driver::run_sign(crate::SIGN_SCHEME_NAME, share_blobs, threshold, message)
66}
67
68pub fn sign_batch(
71 share_blobs: &[Vec<u8>],
72 threshold: u32,
73 messages: &[&[u8]],
74) -> Result<Vec<Vec<u8>>> {
75 let mut out = Vec::with_capacity(messages.len());
76 for msg in messages {
77 out.push(sign(share_blobs, threshold, msg)?);
78 }
79 Ok(out)
80}
81
82pub fn decode_public_key(bytes: &[u8]) -> Result<AffinePoint> {
85 use elliptic_curve::sec1::FromSec1Point;
86 if bytes.len() != 33 {
87 return Err(crate::error::scheme_error(
88 crate::error::Gg18ErrorCode::BAD_SHARE,
89 ));
90 }
91 let enc = elliptic_curve::sec1::Sec1Point::<p256::NistP256>::from_bytes(bytes)
92 .map_err(|_| crate::error::scheme_error(crate::error::Gg18ErrorCode::BAD_SHARE))?;
93 Option::<AffinePoint>::from(AffinePoint::from_sec1_point(&enc))
94 .ok_or_else(|| crate::error::scheme_error(crate::error::Gg18ErrorCode::BAD_SHARE))
95}
96
97#[cfg(test)]
98mod tests {
99 use super::*;
100 use p256::ecdsa::{Signature, VerifyingKey, signature::Verifier};
101
102 #[test]
103 fn keygen_and_sign_round_trip() {
104 let kg = keygen(2, 3).expect("dkg");
105 assert_eq!(kg.shares.len(), 3);
106 assert_eq!(kg.public_key.len(), 33);
107
108 let sig = sign(&kg.shares[..2], 2, b"hello gg18").expect("sign");
109 assert_eq!(sig.len(), 64);
110
111 let pk = decode_public_key(&kg.public_key).expect("pk decode");
112 let vk = VerifyingKey::from_affine(pk).expect("vk");
113 let s = Signature::from_slice(&sig).expect("parse sig");
114 vk.verify(b"hello gg18", &s).expect("verify ok");
115 }
116
117 #[test]
118 fn keygen_below_threshold_sign_errors() {
119 let kg = keygen(3, 5).expect("dkg");
120 let err = sign(&kg.shares[..2], 3, b"msg");
121 assert!(err.is_err());
122 }
123
124 #[test]
125 fn keygen_full_committee_signs_and_verifies() {
126 let kg = keygen(3, 3).expect("dkg");
127 let sig = sign(&kg.shares, 3, b"all-three").expect("sign");
128 let pk = decode_public_key(&kg.public_key).expect("pk decode");
129 let vk = VerifyingKey::from_affine(pk).expect("vk");
130 let s = Signature::from_slice(&sig).expect("parse sig");
131 vk.verify(b"all-three", &s).expect("verify ok");
132 }
133
134 #[test]
135 fn keygen_rejects_corrupt_share_magic() {
136 let kg = keygen(2, 3).expect("dkg");
137 let mut corrupt = kg.shares[0].clone();
138 corrupt[0] = b'X';
139 let err = sign(&[corrupt], 1, b"msg");
140 assert!(err.is_err());
141 }
142
143 #[test]
144 fn sign_batch_produces_one_sig_per_message() {
145 let kg = keygen(2, 3).expect("dkg");
146 let messages: Vec<&[u8]> = vec![b"msg-a", b"msg-b", b"msg-c", b"msg-d"];
147 let sigs = sign_batch(&kg.shares[..2], 2, &messages).expect("batch sign");
148 assert_eq!(sigs.len(), 4);
149 for s in &sigs {
150 assert_eq!(s.len(), 64);
151 }
152 }
153
154 #[test]
155 fn sign_batch_all_verify_under_joint_public_key() {
156 let kg = keygen(2, 3).expect("dkg");
157 let messages: Vec<&[u8]> = vec![b"a", b"b", b"c"];
158 let sigs = sign_batch(&kg.shares[..2], 2, &messages).expect("batch sign");
159 let pk = decode_public_key(&kg.public_key).expect("pk");
160 let vk = VerifyingKey::from_affine(pk).expect("vk");
161 for (msg, sig) in messages.iter().zip(sigs.iter()) {
162 let s = Signature::from_slice(sig).expect("parse sig");
163 vk.verify(msg, &s).expect("verify");
164 }
165 }
166
167 #[test]
168 fn sign_batch_empty_messages_returns_empty() {
169 let kg = keygen(2, 3).expect("dkg");
170 let sigs = sign_batch(&kg.shares[..2], 2, &[]).expect("empty batch");
171 assert!(sigs.is_empty());
172 }
173
174 #[test]
175 fn sign_batch_propagates_signing_error() {
176 let kg = keygen(3, 5).expect("dkg");
177 let messages: Vec<&[u8]> = vec![b"a", b"b"];
179 let err = sign_batch(&kg.shares[..2], 3, &messages);
180 assert!(err.is_err());
181 }
182}