1use hmac::{Hmac, KeyInit, Mac};
19use p256::elliptic_curve::PrimeField;
20use p256::{FieldBytes, Scalar};
21use sha2::Sha256;
22
23type HmacSha256 = Hmac<Sha256>;
24
25pub fn derive_nonce_share(
33 secret_share: &Scalar,
34 message_hash: &[u8; 32],
35 party_idx: u32,
36) -> Scalar {
37 let mut mac =
38 HmacSha256::new_from_slice(&secret_share.to_repr()).expect("HMAC accepts any key length");
39 mac.update(message_hash);
40 mac.update(&party_idx.to_be_bytes());
41 let result = mac.finalize().into_bytes();
42
43 let mut nonce_bytes = [0u8; 32];
44 nonce_bytes.copy_from_slice(&result);
45
46 reduce_mod_order(&nonce_bytes)
47}
48
49pub fn sum_nonce_shares(shares: &[Scalar]) -> Scalar {
57 shares.iter().fold(Scalar::ZERO, |acc, s| acc + s)
58}
59
60pub fn derive_full_nonce(
63 secret_shares: &[Scalar],
64 message_hash: &[u8; 32],
65 party_indices: &[u32],
66) -> Scalar {
67 assert_eq!(secret_shares.len(), party_indices.len());
68 let shares: Vec<Scalar> = secret_shares
69 .iter()
70 .zip(party_indices.iter())
71 .map(|(s, &idx)| derive_nonce_share(s, message_hash, idx))
72 .collect();
73 sum_nonce_shares(&shares)
74}
75
76fn reduce_mod_order(bytes: &[u8; 32]) -> Scalar {
77 let fb = FieldBytes::from(*bytes);
78 let ct = Scalar::from_repr(fb);
79 Option::<Scalar>::from(ct).unwrap_or_else(|| {
80 let mut adjusted = *bytes;
84 adjusted[0] = adjusted[0].wrapping_sub(1);
85 let fb2 = FieldBytes::from(adjusted);
86 let ct2 = Scalar::from_repr(fb2);
87 Option::<Scalar>::from(ct2).unwrap_or(Scalar::ONE)
88 })
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94 use p256::elliptic_curve::Field;
95 use p256::elliptic_curve::rand_core::UnwrapErr;
96
97 fn random_scalar() -> Scalar {
98 Scalar::random(&mut UnwrapErr(getrandom::SysRng))
99 }
100
101 #[test]
102 fn derivation_is_deterministic() {
103 let secret = random_scalar();
104 let msg = [0x42u8; 32];
105 let n1 = derive_nonce_share(&secret, &msg, 1);
106 let n2 = derive_nonce_share(&secret, &msg, 1);
107 assert_eq!(n1, n2);
108 }
109
110 #[test]
111 fn different_party_indices_differ() {
112 let secret = random_scalar();
113 let msg = [0x42u8; 32];
114 let n1 = derive_nonce_share(&secret, &msg, 1);
115 let n2 = derive_nonce_share(&secret, &msg, 2);
116 assert_ne!(n1, n2);
117 }
118
119 #[test]
120 fn different_messages_differ() {
121 let secret = random_scalar();
122 let m1 = [0x01u8; 32];
123 let m2 = [0x02u8; 32];
124 let n1 = derive_nonce_share(&secret, &m1, 1);
125 let n2 = derive_nonce_share(&secret, &m2, 1);
126 assert_ne!(n1, n2);
127 }
128
129 #[test]
130 fn different_secrets_differ() {
131 let s1 = random_scalar();
132 let s2 = random_scalar();
133 let msg = [0x42u8; 32];
134 let n1 = derive_nonce_share(&s1, &msg, 1);
135 let n2 = derive_nonce_share(&s2, &msg, 1);
136 assert_ne!(n1, n2);
137 }
138
139 #[test]
140 fn nonce_share_is_nonzero() {
141 let secret = random_scalar();
142 let msg = [0x42u8; 32];
143 let nonce = derive_nonce_share(&secret, &msg, 1);
144 assert_ne!(nonce, Scalar::ZERO);
145 }
146
147 #[test]
148 fn sum_nonce_shares_works() {
149 let s1 = random_scalar();
150 let s2 = random_scalar();
151 let expected = s1 + s2;
152 assert_eq!(sum_nonce_shares(&[s1, s2]), expected);
153 }
154
155 #[test]
156 fn sum_empty_is_zero() {
157 assert_eq!(sum_nonce_shares(&[]), Scalar::ZERO);
158 }
159
160 #[test]
161 fn full_nonce_is_sum_of_shares() {
162 let secrets = vec![random_scalar(), random_scalar(), random_scalar()];
163 let indices = vec![1u32, 2, 3];
164 let msg = [0xAAu8; 32];
165
166 let shares: Vec<Scalar> = secrets
167 .iter()
168 .zip(indices.iter())
169 .map(|(s, &i)| derive_nonce_share(s, &msg, i))
170 .collect();
171 let expected = sum_nonce_shares(&shares);
172 let actual = derive_full_nonce(&secrets, &msg, &indices);
173 assert_eq!(actual, expected);
174 }
175
176 #[test]
177 fn nonce_is_valid_scalar() {
178 let secret = random_scalar();
179 let msg = [0xFFu8; 32];
180 let nonce = derive_nonce_share(&secret, &msg, 1);
181 let _bytes = nonce.to_repr();
183 }
184
185 #[test]
186 fn reduce_mod_order_handles_large_input() {
187 let max_bytes = [0xFFu8; 32];
188 let result = reduce_mod_order(&max_bytes);
189 assert_ne!(result, Scalar::ZERO);
190 }
191}