confium_privacy/
adaptor_sig.rs1use getrandom::SysRng;
9use p256::ecdsa::{Signature, SigningKey, VerifyingKey};
10use p256::elliptic_curve::rand_core::UnwrapErr;
11use p256::elliptic_curve::sec1::ToSec1Point;
12use p256::elliptic_curve::{Field, PrimeField};
13use p256::{AffinePoint, FieldBytes, ProjectivePoint, Scalar};
14use sha2::{Digest, Sha256};
15
16#[derive(Debug, Clone)]
18pub struct AdaptorPreSig {
19 pub r_prime: AffinePoint,
21 pub s_prime: Scalar,
23}
24
25#[derive(Debug, Clone)]
27pub struct WitnessStatement {
28 pub y_point: AffinePoint,
29}
30
31pub fn create_witness() -> (Scalar, WitnessStatement) {
33 let y = Scalar::random(&mut UnwrapErr(SysRng));
34 let y_point = (ProjectivePoint::GENERATOR * y).to_affine();
35 (y, WitnessStatement { y_point })
36}
37
38pub fn pre_sign(
41 signing_key: &SigningKey,
42 message: &[u8],
43 witness: &WitnessStatement,
44) -> Result<(AdaptorPreSig, Scalar), String> {
45 let k = Scalar::random(&mut UnwrapErr(SysRng));
46 let k_point = (ProjectivePoint::GENERATOR * k).to_affine();
47
48 let r_prime =
50 (ProjectivePoint::from(k_point) + ProjectivePoint::from(witness.y_point)).to_affine();
51
52 let r = x_coord(&r_prime);
54 if r == Scalar::ZERO {
55 return Err("r is zero".into());
56 }
57
58 let e = hash_msg(message);
60 let mut sk_bytes = [0u8; 32];
61 sk_bytes.copy_from_slice(&signing_key.to_bytes());
62 let x = reduce_to_scalar(sk_bytes);
63 let k_inv = invert(&k);
64 let s_prime = k_inv * (e + r * x);
65
66 if s_prime == Scalar::ZERO {
67 return Err("s' is zero".into());
68 }
69
70 Ok((AdaptorPreSig { r_prime, s_prime }, k))
71}
72
73pub fn complete(pre_sig: &AdaptorPreSig, y: &Scalar) -> Result<Signature, String> {
76 let r = x_coord(&pre_sig.r_prime);
78 if r == Scalar::ZERO {
79 return Err("r is zero".into());
80 }
81
82 let y_inv = invert(y);
95 let s = pre_sig.s_prime * y_inv;
96
97 let r_bytes: [u8; 32] = r.to_repr().into();
98 let s_bytes: [u8; 32] = s.to_repr().into();
99 Signature::from_scalars(r_bytes, s_bytes).map_err(|e| format!("{e}"))
100}
101
102pub fn extract_witness(pre_sig: &AdaptorPreSig, full_sig: &Signature) -> Option<Scalar> {
105 let (_, s_full) = full_sig.split_scalars();
106 let s_full_scalar: Scalar = *s_full;
107
108 let s_inv = invert(&s_full_scalar);
110 let y = pre_sig.s_prime * s_inv;
111 if y == Scalar::ZERO { None } else { Some(y) }
112}
113
114pub fn verify_pre_sig(
116 vk: &VerifyingKey,
117 message: &[u8],
118 pre_sig: &AdaptorPreSig,
119 witness: &WitnessStatement,
120) -> bool {
121 let r = x_coord(&pre_sig.r_prime);
122 if r == Scalar::ZERO {
123 return false;
124 }
125 let e = hash_msg(message);
126 let s_inv = invert(&pre_sig.s_prime);
127
128 let pk = ProjectivePoint::from(*vk.as_affine());
133 let u1 = ProjectivePoint::GENERATOR * (e * s_inv);
134 let u2 = pk * (r * s_inv);
135 let expected_k_g = u1 + u2;
136 let k_g = expected_k_g.to_affine();
137
138 let expected_r_prime =
140 (ProjectivePoint::from(k_g) + ProjectivePoint::from(witness.y_point)).to_affine();
141 expected_r_prime == pre_sig.r_prime
142}
143
144fn reduce_to_scalar(mut bytes: [u8; 32]) -> Scalar {
148 loop {
149 if let Some(s) = Option::<Scalar>::from(Scalar::from_repr(FieldBytes::from(bytes))) {
150 return s;
151 }
152 let mut h = Sha256::new();
153 h.update(b"confium-scalar-reduce-v1");
154 h.update(bytes);
155 bytes = h.finalize().into();
156 }
157}
158
159fn x_coord(point: &AffinePoint) -> Scalar {
160 let encoded = point.to_sec1_point(false);
161 if let Some(x_bytes) = encoded.x() {
162 let mut arr = [0u8; 32];
163 arr.copy_from_slice(x_bytes);
164 reduce_to_scalar(arr)
165 } else {
166 Scalar::ZERO
167 }
168}
169
170fn hash_msg(msg: &[u8]) -> Scalar {
171 let mut h = Sha256::new();
172 h.update(msg);
173 let bytes: [u8; 32] = h.finalize().into();
174 reduce_to_scalar(bytes)
175}
176
177fn invert(s: &Scalar) -> Scalar {
178 let ct = s.invert();
181 Option::<Scalar>::from(ct).unwrap_or(Scalar::ZERO)
182}
183
184#[cfg(test)]
185mod tests {
186 use super::*;
187 use p256::elliptic_curve::Generate;
188
189 #[test]
190 fn pre_sign_produces_valid_pre_sig() {
191 let signing = SigningKey::generate();
192 let vk = signing.verifying_key();
193 let (y, witness) = create_witness();
194 let (pre_sig, _k) = pre_sign(&signing, b"message", &witness).unwrap();
195 assert!(verify_pre_sig(vk, b"message", &pre_sig, &witness));
196 let _ = y;
197 }
198
199 #[test]
200 fn complete_produces_signature() {
201 let signing = SigningKey::generate();
202 let (y, witness) = create_witness();
203 let (pre_sig, _k) = pre_sign(&signing, b"payment", &witness).unwrap();
204 let full_sig = complete(&pre_sig, &y).unwrap();
205 let extracted = extract_witness(&pre_sig, &full_sig);
207 assert!(extracted.is_some());
208 }
209
210 #[test]
211 fn different_messages_different_pre_sigs() {
212 let signing = SigningKey::generate();
213 let (_, w1) = create_witness();
214 let (_, w2) = create_witness();
215 let (ps1, _) = pre_sign(&signing, b"msg1", &w1).unwrap();
216 let (ps2, _) = pre_sign(&signing, b"msg2", &w2).unwrap();
217 assert_ne!(ps1.r_prime, ps2.r_prime);
218 }
219
220 #[test]
221 fn wrong_witness_rejected() {
222 let signing = SigningKey::generate();
223 let vk = signing.verifying_key();
224 let (_, w1) = create_witness();
225 let (_, w2) = create_witness();
226 let (pre_sig, _) = pre_sign(&signing, b"msg", &w1).unwrap();
227 assert!(!verify_pre_sig(vk, b"msg", &pre_sig, &w2));
228 }
229
230 #[test]
231 fn witness_extraction() {
232 let signing = SigningKey::generate();
233 let (y, witness) = create_witness();
234 let (pre_sig, _) = pre_sign(&signing, b"msg", &witness).unwrap();
235 let full_sig = complete(&pre_sig, &y).unwrap();
236 let extracted = extract_witness(&pre_sig, &full_sig);
237 assert!(extracted.is_some());
238 }
239
240 #[test]
241 fn create_witness_deterministic_point() {
242 let (y, witness) = create_witness();
243 let expected = (ProjectivePoint::GENERATOR * y).to_affine();
244 assert_eq!(witness.y_point, expected);
245 }
246}